8153035: GenModuleInfoSource strips away the API comments
Reviewed-by: chegar
This commit is contained in:
parent
5b93b89a0a
commit
0a40727bca
@ -25,25 +25,27 @@
|
||||
package build.tools.module;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A build tool to extend the module-info.java in the source tree
|
||||
* for platform-specific exports, uses, and provides and write
|
||||
* to the specified output file.
|
||||
* A build tool to extend the module-info.java in the source tree for
|
||||
* platform-specific exports, uses, and provides and write to the specified
|
||||
* output file. Injecting platform-specific requires is not supported.
|
||||
*
|
||||
* GenModulesList build tool currently generates the modules.list from
|
||||
* the module-info.java from the source tree that will be used for
|
||||
* the make target and dependences.
|
||||
*
|
||||
* The build currently invokes gensrc-$MODULE.gmk after modules.list
|
||||
* is generated. Hence, platform-specific requires is not supported.
|
||||
* The extra exports, uses, provides can be specified in module-info.java.extra
|
||||
* files and GenModuleInfoSource will be invoked for each module that has
|
||||
* module-info.java.extra in the source directory.
|
||||
*/
|
||||
public class GenModuleInfoSource {
|
||||
private final static String USAGE =
|
||||
@ -57,17 +59,32 @@ public class GenModuleInfoSource {
|
||||
public static void main(String... args) throws Exception {
|
||||
Path outfile = null;
|
||||
Path moduleInfoJava = null;
|
||||
Map<String, Set<String>> options = new HashMap<>();
|
||||
GenModuleInfoSource genModuleInfo = new GenModuleInfoSource();
|
||||
|
||||
// validate input arguments
|
||||
for (int i = 0; i < args.length; i++){
|
||||
String option = args[i];
|
||||
if (option.startsWith("-")) {
|
||||
String arg = args[++i];
|
||||
if (option.equals("-exports") ||
|
||||
option.equals("-uses") ||
|
||||
option.equals("-provides")) {
|
||||
options.computeIfAbsent(option, _k -> new HashSet<>()).add(arg);
|
||||
if (option.equals("-exports")) {
|
||||
int index = arg.indexOf('/');
|
||||
if (index > 0) {
|
||||
String pn = arg.substring(0, index);
|
||||
String mn = arg.substring(index + 1, arg.length());
|
||||
genModuleInfo.exportTo(pn, mn);
|
||||
} else {
|
||||
genModuleInfo.export(arg);
|
||||
}
|
||||
} else if (option.equals("-uses")) {
|
||||
genModuleInfo.use(arg);
|
||||
} else if (option.equals("-provides")) {
|
||||
int index = arg.indexOf('/');
|
||||
if (index <= 0) {
|
||||
throw new IllegalArgumentException("invalid -provide argument: " + arg);
|
||||
}
|
||||
String service = arg.substring(0, index);
|
||||
String impl = arg.substring(index + 1, arg.length());
|
||||
genModuleInfo.provide(service, impl);
|
||||
} else if (option.equals("-o")) {
|
||||
outfile = Paths.get(arg);
|
||||
} else {
|
||||
@ -87,48 +104,145 @@ public class GenModuleInfoSource {
|
||||
System.err.println(USAGE);
|
||||
System.exit(-1);
|
||||
}
|
||||
// read module-info.java
|
||||
Module.Builder builder = ModuleInfoReader.builder(moduleInfoJava);
|
||||
augment(builder, options);
|
||||
|
||||
// generate new module-info.java
|
||||
Module module = builder.build();
|
||||
genModuleInfo.generate(moduleInfoJava, outfile);
|
||||
}
|
||||
|
||||
private final Set<String> exports = new HashSet<>();
|
||||
private final Map<String, Set<String>> exportsTo = new HashMap<>();
|
||||
private final Set<String> uses = new HashSet<>();
|
||||
private final Map<String, Set<String>> provides = new HashMap<>();
|
||||
GenModuleInfoSource() {
|
||||
}
|
||||
|
||||
private void export(String p) {
|
||||
Objects.requireNonNull(p);
|
||||
if (exports.contains(p) || exportsTo.containsKey(p)) {
|
||||
throw new RuntimeException("duplicated exports: " + p);
|
||||
}
|
||||
exports.add(p);
|
||||
}
|
||||
private void exportTo(String p, String mn) {
|
||||
Objects.requireNonNull(p);
|
||||
Objects.requireNonNull(mn);
|
||||
if (exports.contains(p)) {
|
||||
throw new RuntimeException("unqualified exports already exists: " + p);
|
||||
}
|
||||
exportsTo.computeIfAbsent(p, _k -> new HashSet<>()).add(mn);
|
||||
}
|
||||
|
||||
private void use(String service) {
|
||||
uses.add(service);
|
||||
}
|
||||
|
||||
private void provide(String s, String impl) {
|
||||
provides.computeIfAbsent(s, _k -> new HashSet<>()).add(impl);
|
||||
}
|
||||
|
||||
private void generate(Path sourcefile, Path outfile) throws IOException {
|
||||
Path parent = outfile.getParent();
|
||||
if (parent != null)
|
||||
Files.createDirectories(parent);
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(outfile)) {
|
||||
writer.write(module.toString());
|
||||
}
|
||||
}
|
||||
List<String> lines = Files.readAllLines(sourcefile);
|
||||
try (BufferedWriter bw = Files.newBufferedWriter(outfile);
|
||||
PrintWriter writer = new PrintWriter(bw)) {
|
||||
int lineNumber = 0;
|
||||
for (String l : lines) {
|
||||
lineNumber++;
|
||||
String[] s = l.trim().split("\\s+");
|
||||
String keyword = s[0].trim();
|
||||
int nextIndex = keyword.length();
|
||||
String exp = null;
|
||||
int n = l.length();
|
||||
switch (keyword) {
|
||||
case "exports":
|
||||
boolean inExportsTo = false;
|
||||
// assume package name immediately after exports
|
||||
exp = s[1].trim();
|
||||
if (s.length >= 3) {
|
||||
nextIndex = l.indexOf(exp, nextIndex) + exp.length();
|
||||
if (s[2].trim().equals("to")) {
|
||||
inExportsTo = true;
|
||||
n = l.indexOf("to", nextIndex) + "to".length();
|
||||
} else {
|
||||
throw new RuntimeException(sourcefile + ", line " +
|
||||
lineNumber + ", is malformed: " + s[2]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void augment(Module.Builder builder, Map<String, Set<String>> options) {
|
||||
for (String opt : options.keySet()) {
|
||||
if (opt.equals("-exports")) {
|
||||
for (String arg : options.get(opt)) {
|
||||
int index = arg.indexOf('/');
|
||||
if (index > 0) {
|
||||
String pn = arg.substring(0, index);
|
||||
String mn = arg.substring(index + 1, arg.length());
|
||||
builder.exportTo(pn, mn);
|
||||
} else {
|
||||
builder.export(arg);
|
||||
}
|
||||
}
|
||||
} else if (opt.equals("-uses")) {
|
||||
options.get(opt).stream()
|
||||
.forEach(builder::use);
|
||||
} else if (opt.equals("-provides")) {
|
||||
for (String arg : options.get(opt)) {
|
||||
int index = arg.indexOf('/');
|
||||
if (index <= 0) {
|
||||
throw new IllegalArgumentException("invalid -provide argument: " + arg);
|
||||
}
|
||||
String service = arg.substring(0, index);
|
||||
String impl = arg.substring(index + 1, arg.length());
|
||||
builder.provide(service, impl);
|
||||
// inject the extra targets after "to"
|
||||
if (inExportsTo) {
|
||||
writer.println(injectExportTargets(exp, l, n));
|
||||
} else {
|
||||
writer.println(l);
|
||||
}
|
||||
break;
|
||||
case "to":
|
||||
if (exp == null) {
|
||||
throw new RuntimeException(sourcefile + ", line " +
|
||||
lineNumber + ", is malformed");
|
||||
}
|
||||
n = l.indexOf("to", nextIndex) + "to".length();
|
||||
writer.println(injectExportTargets(exp, l, n));
|
||||
break;
|
||||
case "}":
|
||||
doAugments(writer);
|
||||
// fall through
|
||||
default:
|
||||
writer.println(l);
|
||||
// reset exports
|
||||
exp = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String injectExportTargets(String pn, String exp, int pos) {
|
||||
Set<String> targets = exportsTo.remove(pn);
|
||||
if (targets != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// inject the extra targets after the given pos
|
||||
sb.append(exp.substring(0, pos))
|
||||
.append("\n\t")
|
||||
.append(targets.stream()
|
||||
.collect(Collectors.joining(",", "", ",")))
|
||||
.append(" /* injected */");
|
||||
if (pos < exp.length()) {
|
||||
// print the remaining statement followed "to"
|
||||
sb.append("\n\t")
|
||||
.append(exp.substring(pos+1, exp.length()));
|
||||
}
|
||||
return sb.toString();
|
||||
} else {
|
||||
return exp;
|
||||
}
|
||||
}
|
||||
|
||||
private void doAugments(PrintWriter writer) {
|
||||
if ((exports.size() + exportsTo.size() + uses.size() + provides.size()) == 0)
|
||||
return;
|
||||
|
||||
writer.println(" // augmented from module-info.java.extra");
|
||||
exports.stream()
|
||||
.sorted()
|
||||
.forEach(e -> writer.format(" exports %s;%n", e));
|
||||
// remaining injected qualified exports
|
||||
exportsTo.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.map(e -> String.format(" exports %s to%n%s;", e.getKey(),
|
||||
e.getValue().stream().sorted()
|
||||
.map(mn -> String.format(" %s", mn))
|
||||
.collect(Collectors.joining(",\n"))))
|
||||
.forEach(writer::println);
|
||||
uses.stream().sorted()
|
||||
.forEach(s -> writer.format(" uses %s;%n", s));
|
||||
provides.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.flatMap(e -> e.getValue().stream().sorted()
|
||||
.map(impl -> String.format(" provides %s with %s;",
|
||||
e.getKey(), impl)))
|
||||
.forEach(writer::println);
|
||||
}
|
||||
}
|
||||
|
@ -1,280 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package build.tools.module;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Module {
|
||||
public static class Dependence implements Comparable<Dependence> {
|
||||
final String name;
|
||||
final boolean reexport;
|
||||
Dependence(String name) {
|
||||
this(name, false);
|
||||
}
|
||||
Dependence(String name, boolean reexport) {
|
||||
this.name = name;
|
||||
this.reexport = reexport;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean reexport(){
|
||||
return reexport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
hash = 11 * hash + Objects.hashCode(this.name);
|
||||
hash = 11 * hash + (this.reexport ? 1 : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
Dependence d = (Dependence)o;
|
||||
return this.name.equals(d.name) && this.reexport == d.reexport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Dependence o) {
|
||||
int rc = this.name.compareTo(o.name);
|
||||
return rc != 0 ? rc : Boolean.compare(this.reexport, o.reexport);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("requires %s%s;",
|
||||
reexport ? "public " : "", name);
|
||||
}
|
||||
}
|
||||
private final String moduleName;
|
||||
private final Set<Dependence> requires;
|
||||
private final Map<String, Set<String>> exports;
|
||||
private final Set<String> uses;
|
||||
private final Map<String, Set<String>> provides;
|
||||
|
||||
private Module(String name,
|
||||
Set<Dependence> requires,
|
||||
Map<String, Set<String>> exports,
|
||||
Set<String> uses,
|
||||
Map<String, Set<String>> provides) {
|
||||
this.moduleName = name;
|
||||
this.requires = Collections.unmodifiableSet(requires);
|
||||
this.exports = Collections.unmodifiableMap(exports);
|
||||
this.uses = Collections.unmodifiableSet(uses);
|
||||
this.provides = Collections.unmodifiableMap(provides);
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
public Set<Dependence> requires() {
|
||||
return requires;
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> exports() {
|
||||
return exports;
|
||||
}
|
||||
|
||||
public Set<String> uses() {
|
||||
return uses;
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> provides() {
|
||||
return provides;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object ob) {
|
||||
if (!(ob instanceof Module)) {
|
||||
return false;
|
||||
}
|
||||
Module that = (Module) ob;
|
||||
return (moduleName.equals(that.moduleName)
|
||||
&& requires.equals(that.requires)
|
||||
&& exports.equals(that.exports));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hc = moduleName.hashCode();
|
||||
hc = hc * 43 + requires.hashCode();
|
||||
hc = hc * 43 + exports.hashCode();
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("module %s {%n", moduleName));
|
||||
requires.stream()
|
||||
.sorted()
|
||||
.map(d -> String.format(" requires %s%s;%n", d.reexport ? "public " : "", d.name))
|
||||
.forEach(sb::append);
|
||||
exports.entrySet().stream()
|
||||
.filter(e -> e.getValue().isEmpty())
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.map(e -> String.format(" exports %s;%n", e.getKey()))
|
||||
.forEach(sb::append);
|
||||
exports.entrySet().stream()
|
||||
.filter(e -> !e.getValue().isEmpty())
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.map(e -> String.format(" exports %s to%n%s;%n", e.getKey(),
|
||||
e.getValue().stream().sorted()
|
||||
.map(mn -> String.format(" %s", mn))
|
||||
.collect(Collectors.joining(",\n"))))
|
||||
.forEach(sb::append);
|
||||
uses.stream().sorted()
|
||||
.map(s -> String.format(" uses %s;%n", s))
|
||||
.forEach(sb::append);
|
||||
provides.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.flatMap(e -> e.getValue().stream().sorted()
|
||||
.map(impl -> String.format(" provides %s with %s;%n", e.getKey(), impl)))
|
||||
.forEach(sb::append);
|
||||
sb.append("}").append("\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Module Builder
|
||||
*/
|
||||
static class Builder {
|
||||
private String name;
|
||||
final Set<Dependence> requires = new HashSet<>();
|
||||
final Map<String, Set<String>> exports = new HashMap<>();
|
||||
final Set<String> uses = new HashSet<>();
|
||||
final Map<String, Set<String>> provides = new HashMap<>();
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
public Builder name(String n) {
|
||||
name = n;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder require(String d, boolean reexport) {
|
||||
requires.add(new Dependence(d, reexport));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder export(String p) {
|
||||
Objects.requireNonNull(p);
|
||||
if (exports.containsKey(p)) {
|
||||
throw new RuntimeException(name + " already exports " + p +
|
||||
" " + exports.get(p));
|
||||
}
|
||||
return exportTo(p, Collections.emptySet());
|
||||
}
|
||||
|
||||
public Builder exportTo(String p, String mn) {
|
||||
Objects.requireNonNull(p);
|
||||
Objects.requireNonNull(mn);
|
||||
Set<String> ms = exports.get(p);
|
||||
if (ms != null && ms.isEmpty()) {
|
||||
throw new RuntimeException(name + " already has unqualified exports " + p);
|
||||
}
|
||||
exports.computeIfAbsent(p, _k -> new HashSet<>()).add(mn);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder exportTo(String p, Set<String> ms) {
|
||||
Objects.requireNonNull(p);
|
||||
Objects.requireNonNull(ms);
|
||||
if (exports.containsKey(p)) {
|
||||
throw new RuntimeException(name + " already exports " + p +
|
||||
" " + exports.get(p));
|
||||
}
|
||||
exports.put(p, new HashSet<>(ms));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder use(String cn) {
|
||||
uses.add(cn);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder provide(String s, String impl) {
|
||||
provides.computeIfAbsent(s, _k -> new HashSet<>()).add(impl);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder merge(Module m1, Module m2) {
|
||||
if (!m1.name().equals(m2.name())) {
|
||||
throw new IllegalArgumentException(m1.name() + " != " + m2.name());
|
||||
}
|
||||
name = m1.name();
|
||||
// ## reexports
|
||||
requires.addAll(m1.requires());
|
||||
requires.addAll(m2.requires());
|
||||
Stream.concat(m1.exports().keySet().stream(), m2.exports().keySet().stream())
|
||||
.distinct()
|
||||
.forEach(pn -> {
|
||||
Set<String> s1 = m2.exports().get(pn);
|
||||
Set<String> s2 = m2.exports().get(pn);
|
||||
if (s1 == null || s2 == null) {
|
||||
exportTo(pn, s1 != null ? s1 : s2);
|
||||
} else if (s1.isEmpty() || s2.isEmpty()) {
|
||||
// unqualified exports
|
||||
export(pn);
|
||||
} else {
|
||||
exportTo(pn, Stream.concat(s1.stream(), s2.stream())
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
});
|
||||
uses.addAll(m1.uses());
|
||||
uses.addAll(m2.uses());
|
||||
m1.provides().keySet().stream()
|
||||
.forEach(s -> m1.provides().get(s).stream()
|
||||
.forEach(impl -> provide(s, impl)));
|
||||
m2.provides().keySet().stream()
|
||||
.forEach(s -> m2.provides().get(s).stream()
|
||||
.forEach(impl -> provide(s, impl)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Module build() {
|
||||
Module m = new Module(name, requires, exports, uses, provides);
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name != null ? name : "Unknown";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,357 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package build.tools.module;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import build.tools.module.Module.Builder;
|
||||
|
||||
/**
|
||||
* Source reader of module-info.java
|
||||
*/
|
||||
public class ModuleInfoReader {
|
||||
private final Path sourcefile;
|
||||
private final Builder builder;
|
||||
private ModuleInfoReader(Path file) {
|
||||
this.sourcefile = file;
|
||||
this.builder = new Builder();
|
||||
}
|
||||
|
||||
public static Builder builder(Path file) throws IOException {
|
||||
ModuleInfoReader reader = new ModuleInfoReader(file);
|
||||
reader.readFile();
|
||||
return reader.builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the source file.
|
||||
*/
|
||||
void readFile() throws IOException {
|
||||
List<String> lines = Files.readAllLines(sourcefile);
|
||||
boolean done = false;
|
||||
int lineNumber = 0;
|
||||
boolean inBlockComment = false;
|
||||
boolean inRequires = false;
|
||||
boolean reexports = false;
|
||||
boolean inProvides = false;
|
||||
boolean inWith = false;
|
||||
String serviceIntf = null;
|
||||
String providerClass = null;
|
||||
boolean inUses = false;
|
||||
boolean inExports = false;
|
||||
boolean inExportsTo = false;
|
||||
String qualifiedExports = null;
|
||||
Counter counter = new Counter();
|
||||
|
||||
for (String line : lines) {
|
||||
lineNumber++;
|
||||
if (inBlockComment) {
|
||||
int c = line.indexOf("*/");
|
||||
if (c >= 0) {
|
||||
line = line.substring(c + 2, line.length());
|
||||
inBlockComment = false;
|
||||
} else {
|
||||
// skip lines until end of comment block
|
||||
continue;
|
||||
}
|
||||
}
|
||||
inBlockComment = beginBlockComment(line);
|
||||
|
||||
line = trimComment(line).trim();
|
||||
// ignore empty lines
|
||||
if (line.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
String values;
|
||||
if (inRequires || inExports | inUses | (inWith && providerClass == null)) {
|
||||
values = line;
|
||||
} else {
|
||||
String[] s = line.split("\\s+");
|
||||
String keyword = s[0].trim();
|
||||
int nextIndex = keyword.length();
|
||||
switch (keyword) {
|
||||
case "module":
|
||||
if (s.length != 3 || !s[2].trim().equals("{")) {
|
||||
throw new RuntimeException(sourcefile + ", line " +
|
||||
lineNumber + ", is malformed");
|
||||
}
|
||||
builder.name(s[1].trim());
|
||||
continue; // next line
|
||||
case "requires":
|
||||
inRequires = true;
|
||||
counter.numRequires++;
|
||||
if (s.length >= 2) {
|
||||
String ss = s[1].trim();
|
||||
if (ss.equals("public")) {
|
||||
nextIndex = line.indexOf(ss) + ss.length();
|
||||
reexports = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "exports":
|
||||
inExports = true;
|
||||
inExportsTo = false;
|
||||
counter.numExports++;
|
||||
qualifiedExports = null;
|
||||
if (s.length >= 3) {
|
||||
qualifiedExports = s[1].trim();
|
||||
nextIndex = line.indexOf(qualifiedExports, nextIndex)
|
||||
+ qualifiedExports.length();
|
||||
if (s[2].trim().equals("to")) {
|
||||
inExportsTo = true;
|
||||
nextIndex = line.indexOf("to", nextIndex) + "to".length();
|
||||
} else {
|
||||
throw new RuntimeException(sourcefile + ", line " +
|
||||
lineNumber + ", is malformed: " + s[2]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "to":
|
||||
if (!inExports || qualifiedExports == null) {
|
||||
throw new RuntimeException(sourcefile + ", line " +
|
||||
lineNumber + ", is malformed");
|
||||
}
|
||||
inExportsTo = true;
|
||||
break;
|
||||
case "uses":
|
||||
inUses = true;
|
||||
counter.numUses++;
|
||||
break;
|
||||
case "provides":
|
||||
inProvides = true;
|
||||
inWith = false;
|
||||
counter.numProvides++;
|
||||
serviceIntf = null;
|
||||
providerClass = null;
|
||||
if (s.length >= 2) {
|
||||
serviceIntf = s[1].trim();
|
||||
nextIndex = line.indexOf(serviceIntf) + serviceIntf.length();
|
||||
}
|
||||
if (s.length >= 3) {
|
||||
if (s[2].trim().equals("with")) {
|
||||
inWith = true;
|
||||
nextIndex = line.indexOf("with") + "with".length();
|
||||
} else {
|
||||
throw new RuntimeException(sourcefile + ", line " +
|
||||
lineNumber + ", is malformed: " + s[2]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "with":
|
||||
if (!inProvides || serviceIntf == null) {
|
||||
throw new RuntimeException(sourcefile + ", line " +
|
||||
lineNumber + ", is malformed");
|
||||
}
|
||||
inWith = true;
|
||||
nextIndex = line.indexOf("with") + "with".length();
|
||||
break;
|
||||
case "}":
|
||||
counter.validate(builder);
|
||||
done = true;
|
||||
continue; // next line
|
||||
default:
|
||||
throw new RuntimeException(sourcefile + ", \"" +
|
||||
keyword + "\" on line " +
|
||||
lineNumber + ", is not recognized");
|
||||
}
|
||||
values = line.substring(nextIndex, line.length()).trim();
|
||||
}
|
||||
|
||||
int len = values.length();
|
||||
if (len == 0) {
|
||||
continue; // next line
|
||||
}
|
||||
char lastchar = values.charAt(len - 1);
|
||||
if (lastchar != ',' && lastchar != ';') {
|
||||
throw new RuntimeException(sourcefile + ", line " +
|
||||
lineNumber + ", is malformed:" +
|
||||
" ',' or ';' is missing.");
|
||||
}
|
||||
|
||||
values = values.substring(0, len - 1).trim();
|
||||
// parse the values specified for a keyword specified
|
||||
for (String s : values.split(",")) {
|
||||
s = s.trim();
|
||||
if (s.length() > 0) {
|
||||
if (inRequires) {
|
||||
if (builder.requires.contains(s)) {
|
||||
throw new RuntimeException(sourcefile + ", line "
|
||||
+ lineNumber + " duplicated requires: \"" + s + "\"");
|
||||
}
|
||||
builder.require(s, reexports);
|
||||
} else if (inExports) {
|
||||
if (!inExportsTo && qualifiedExports == null) {
|
||||
builder.export(s);
|
||||
} else {
|
||||
builder.exportTo(qualifiedExports, s);
|
||||
}
|
||||
} else if (inUses) {
|
||||
builder.use(s);
|
||||
} else if (inProvides) {
|
||||
if (!inWith) {
|
||||
serviceIntf = s;
|
||||
} else {
|
||||
providerClass = s;
|
||||
builder.provide(serviceIntf, providerClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastchar == ';') {
|
||||
inRequires = false;
|
||||
reexports = false;
|
||||
inExports = false;
|
||||
inExportsTo = false;
|
||||
inProvides = false;
|
||||
inWith = false;
|
||||
inUses = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (inBlockComment) {
|
||||
throw new RuntimeException(sourcefile + ", line " +
|
||||
lineNumber + ", missing \"*/\" to end a block comment");
|
||||
}
|
||||
if (!done) {
|
||||
throw new RuntimeException(sourcefile + ", line " +
|
||||
lineNumber + ", missing \"}\" to end module definition" +
|
||||
" for \"" + builder + "\"");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// the naming convention for the module names without dashes
|
||||
private static final Pattern CLASS_NAME_PATTERN = Pattern.compile("[\\w\\.\\*_$/]+");
|
||||
private static boolean beginBlockComment(String line) {
|
||||
int pos = 0;
|
||||
while (pos >= 0 && pos < line.length()) {
|
||||
int c = line.indexOf("/*", pos);
|
||||
if (c < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c > 0 && !Character.isWhitespace(line.charAt(c - 1))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int c1 = line.indexOf("//", pos);
|
||||
if (c1 >= 0 && c1 < c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int c2 = line.indexOf("*/", c + 2);
|
||||
if (c2 < 0) {
|
||||
return true;
|
||||
}
|
||||
pos = c + 2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private static String trimComment(String line) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int pos = 0;
|
||||
while (pos >= 0 && pos < line.length()) {
|
||||
int c1 = line.indexOf("//", pos);
|
||||
if (c1 > 0 && !Character.isWhitespace(line.charAt(c1 - 1))) {
|
||||
// not a comment
|
||||
c1 = -1;
|
||||
}
|
||||
|
||||
int c2 = line.indexOf("/*", pos);
|
||||
if (c2 > 0 && !Character.isWhitespace(line.charAt(c2 - 1))) {
|
||||
// not a comment
|
||||
c2 = -1;
|
||||
}
|
||||
|
||||
int c = line.length();
|
||||
int n = line.length();
|
||||
if (c1 >= 0 || c2 >= 0) {
|
||||
if (c1 >= 0) {
|
||||
c = c1;
|
||||
}
|
||||
if (c2 >= 0 && c2 < c) {
|
||||
c = c2;
|
||||
}
|
||||
int c3 = line.indexOf("*/", c2 + 2);
|
||||
if (c == c2 && c3 > c2) {
|
||||
n = c3 + 2;
|
||||
}
|
||||
}
|
||||
if (c > 0) {
|
||||
if (sb.length() > 0) {
|
||||
// add a whitespace if multiple comments on one line
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(line.substring(pos, c));
|
||||
}
|
||||
pos = n;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
static class Counter {
|
||||
int numRequires;
|
||||
int numExports;
|
||||
int numUses;
|
||||
int numProvides;
|
||||
|
||||
void validate(Builder builder) {
|
||||
assertEquals("requires", numRequires, builder.requires.size(),
|
||||
() -> builder.requires.stream()
|
||||
.map(Module.Dependence::toString));
|
||||
assertEquals("exports", numExports, builder.exports.size(),
|
||||
() -> builder.exports.entrySet().stream()
|
||||
.map(e -> "exports " + e.getKey() + " to " + e.getValue()));
|
||||
assertEquals("uses", numUses, builder.uses.size(),
|
||||
() -> builder.uses.stream());
|
||||
assertEquals("provides", numProvides,
|
||||
(int)builder.provides.values().stream()
|
||||
.flatMap(s -> s.stream())
|
||||
.count(),
|
||||
() -> builder.provides.entrySet().stream()
|
||||
.map(e -> "provides " + e.getKey() + " with " + e.getValue()));
|
||||
}
|
||||
|
||||
private static void assertEquals(String msg, int expected, int got,
|
||||
Supplier<Stream<String>> supplier) {
|
||||
if (expected != got){
|
||||
System.err.println("ERROR: mismatched " + msg +
|
||||
" expected: " + expected + " got: " + got );
|
||||
supplier.get().sorted()
|
||||
.forEach(System.err::println);
|
||||
throw new AssertionError("mismatched " + msg +
|
||||
" expected: " + expected + " got: " + got + " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package build.tools.module;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.stream.XMLEventReader;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.events.Attribute;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class ModulesXmlReader {
|
||||
|
||||
private ModulesXmlReader() {}
|
||||
|
||||
public static Set<Module> readModules(Path modulesXml)
|
||||
throws XMLStreamException, IOException
|
||||
{
|
||||
Set<Module> modules = new HashSet<>();
|
||||
try (InputStream in = new BufferedInputStream(Files.newInputStream(modulesXml))) {
|
||||
Set<Module> mods = ModulesXmlReader.load(in);
|
||||
modules.addAll(mods);
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
|
||||
private static final String MODULES = "modules";
|
||||
private static final String MODULE = "module";
|
||||
private static final String NAME = "name";
|
||||
private static final String DEPEND = "depend";
|
||||
private static final String EXPORT = "export";
|
||||
private static final String TO = "to";
|
||||
private static final QName REEXPORTS = new QName("re-exports");
|
||||
private static Set<Module> load(InputStream in)
|
||||
throws XMLStreamException, IOException
|
||||
{
|
||||
Set<Module> modules = new HashSet<>();
|
||||
XMLInputFactory factory = XMLInputFactory.newInstance();
|
||||
XMLEventReader stream = factory.createXMLEventReader(in);
|
||||
Module.Builder mb = null;
|
||||
String modulename = null;
|
||||
String pkg = null;
|
||||
Set<String> permits = new HashSet<>();
|
||||
while (stream.hasNext()) {
|
||||
XMLEvent event = stream.nextEvent();
|
||||
if (event.isStartElement()) {
|
||||
String startTag = event.asStartElement().getName().getLocalPart();
|
||||
switch (startTag) {
|
||||
case MODULES:
|
||||
break;
|
||||
case MODULE:
|
||||
if (mb != null) {
|
||||
throw new RuntimeException("end tag for module is missing");
|
||||
}
|
||||
modulename = getNextTag(stream, NAME);
|
||||
mb = new Module.Builder();
|
||||
mb.name(modulename);
|
||||
break;
|
||||
case NAME:
|
||||
throw new RuntimeException(event.toString());
|
||||
case DEPEND:
|
||||
boolean reexports = false;
|
||||
Attribute attr = event.asStartElement().getAttributeByName(REEXPORTS);
|
||||
if (attr != null) {
|
||||
String value = attr.getValue();
|
||||
if (value.equals("true") || value.equals("false")) {
|
||||
reexports = Boolean.parseBoolean(value);
|
||||
} else {
|
||||
throw new RuntimeException("unexpected attribute " + attr.toString());
|
||||
}
|
||||
}
|
||||
mb.require(getData(stream), reexports);
|
||||
break;
|
||||
case EXPORT:
|
||||
pkg = getNextTag(stream, NAME);
|
||||
break;
|
||||
case TO:
|
||||
permits.add(getData(stream));
|
||||
break;
|
||||
default:
|
||||
}
|
||||
} else if (event.isEndElement()) {
|
||||
String endTag = event.asEndElement().getName().getLocalPart();
|
||||
switch (endTag) {
|
||||
case MODULE:
|
||||
modules.add(mb.build());
|
||||
mb = null;
|
||||
break;
|
||||
case EXPORT:
|
||||
if (pkg == null) {
|
||||
throw new RuntimeException("export-to is malformed");
|
||||
}
|
||||
mb.exportTo(pkg, permits);
|
||||
pkg = null;
|
||||
permits.clear();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
} else if (event.isCharacters()) {
|
||||
String s = event.asCharacters().getData();
|
||||
if (!s.trim().isEmpty()) {
|
||||
throw new RuntimeException("export-to is malformed");
|
||||
}
|
||||
}
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
|
||||
private static String getData(XMLEventReader reader)
|
||||
throws XMLStreamException
|
||||
{
|
||||
XMLEvent e = reader.nextEvent();
|
||||
if (e.isCharacters())
|
||||
return e.asCharacters().getData();
|
||||
|
||||
throw new RuntimeException(e.toString());
|
||||
}
|
||||
|
||||
private static String getNextTag(XMLEventReader reader, String tag)
|
||||
throws XMLStreamException
|
||||
{
|
||||
XMLEvent e = reader.nextTag();
|
||||
if (e.isStartElement()) {
|
||||
String t = e.asStartElement().getName().getLocalPart();
|
||||
if (!tag.equals(t)) {
|
||||
throw new RuntimeException(e + " expected: " + tag);
|
||||
}
|
||||
return getData(reader);
|
||||
}
|
||||
throw new RuntimeException("export-to name is missing:" + e);
|
||||
}
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package build.tools.module;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public final class ModulesXmlWriter {
|
||||
|
||||
private ModulesXmlWriter() {}
|
||||
|
||||
public static void writeModules(Set<Module> modules, Path path)
|
||||
throws IOException, XMLStreamException
|
||||
{
|
||||
writeXML(modules, path);
|
||||
}
|
||||
|
||||
private static final String MODULES = "modules";
|
||||
private static final String MODULE = "module";
|
||||
private static final String NAME = "name";
|
||||
private static final String DEPEND = "depend";
|
||||
private static final String EXPORT = "export";
|
||||
private static final String TO = "to";
|
||||
private static final QName REEXPORTS = new QName("re-exports");
|
||||
|
||||
private static void writeXML(Set<Module> modules, Path path)
|
||||
throws IOException, XMLStreamException
|
||||
{
|
||||
XMLOutputFactory xof = XMLOutputFactory.newInstance();
|
||||
try (OutputStream out = Files.newOutputStream(path)) {
|
||||
int depth = 0;
|
||||
XMLStreamWriter xtw = xof.createXMLStreamWriter(out, "UTF-8");
|
||||
xtw.writeStartDocument("utf-8","1.0");
|
||||
writeStartElement(xtw, MODULES, depth);
|
||||
modules.stream()
|
||||
.sorted(Comparator.comparing(Module::name))
|
||||
.forEach(m -> writeModuleElement(xtw, m, depth+1));
|
||||
writeEndElement(xtw, depth);
|
||||
xtw.writeCharacters("\n");
|
||||
xtw.writeEndDocument();
|
||||
xtw.flush();
|
||||
xtw.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeElement(XMLStreamWriter xtw,
|
||||
String element,
|
||||
String value,
|
||||
int depth) {
|
||||
try {
|
||||
writeStartElement(xtw, element, depth);
|
||||
xtw.writeCharacters(value);
|
||||
xtw.writeEndElement();
|
||||
} catch (XMLStreamException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeDependElement(XMLStreamWriter xtw,
|
||||
Module.Dependence d,
|
||||
int depth) {
|
||||
try {
|
||||
writeStartElement(xtw, DEPEND, depth);
|
||||
if (d.reexport) {
|
||||
xtw.writeAttribute("re-exports", "true");
|
||||
}
|
||||
xtw.writeCharacters(d.name);
|
||||
xtw.writeEndElement();
|
||||
} catch (XMLStreamException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeExportElement(XMLStreamWriter xtw,
|
||||
String pkg,
|
||||
int depth) {
|
||||
writeExportElement(xtw, pkg, Collections.emptySet(), depth);
|
||||
}
|
||||
|
||||
private static void writeExportElement(XMLStreamWriter xtw,
|
||||
String pkg,
|
||||
Set<String> permits,
|
||||
int depth) {
|
||||
try {
|
||||
writeStartElement(xtw, EXPORT, depth);
|
||||
writeElement(xtw, NAME, pkg, depth+1);
|
||||
if (!permits.isEmpty()) {
|
||||
permits.stream().sorted()
|
||||
.forEach(m -> writeElement(xtw, TO, m, depth + 1));
|
||||
}
|
||||
writeEndElement(xtw, depth);
|
||||
} catch (XMLStreamException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
private static void writeModuleElement(XMLStreamWriter xtw,
|
||||
Module m,
|
||||
int depth) {
|
||||
try {
|
||||
writeStartElement(xtw, MODULE, depth);
|
||||
writeElement(xtw, NAME, m.name(), depth+1);
|
||||
m.requires().stream().sorted(Comparator.comparing(d -> d.name))
|
||||
.forEach(d -> writeDependElement(xtw, d, depth+1));
|
||||
m.exports().keySet().stream()
|
||||
.filter(pn -> m.exports().get(pn).isEmpty())
|
||||
.sorted()
|
||||
.forEach(pn -> writeExportElement(xtw, pn, depth+1));
|
||||
m.exports().entrySet().stream()
|
||||
.filter(e -> !e.getValue().isEmpty())
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.forEach(e -> writeExportElement(xtw, e.getKey(), e.getValue(), depth+1));
|
||||
writeEndElement(xtw, depth);
|
||||
} catch (XMLStreamException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Two spaces; the default indentation. */
|
||||
public static final String DEFAULT_INDENT = " ";
|
||||
|
||||
/** stack[depth] indicates what's been written into the current scope. */
|
||||
private static String[] stack = new String[] { "\n",
|
||||
"\n" + DEFAULT_INDENT,
|
||||
"\n" + DEFAULT_INDENT + DEFAULT_INDENT,
|
||||
"\n" + DEFAULT_INDENT + DEFAULT_INDENT + DEFAULT_INDENT};
|
||||
|
||||
private static void writeStartElement(XMLStreamWriter xtw,
|
||||
String name,
|
||||
int depth)
|
||||
throws XMLStreamException
|
||||
{
|
||||
xtw.writeCharacters(stack[depth]);
|
||||
xtw.writeStartElement(name);
|
||||
}
|
||||
|
||||
private static void writeEndElement(XMLStreamWriter xtw, int depth)
|
||||
throws XMLStreamException
|
||||
{
|
||||
xtw.writeCharacters(stack[depth]);
|
||||
xtw.writeEndElement();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user