# HG changeset patch # User chegar # Date 1410862013 -7200 # Node ID e6383382c9391396fe1501bf801d545e27dfb054 # Parent 11289cf5c8fc14443cfdfbfb982eb94225981242 8058118: Generate modules.list during the build Reviewed-by: alanb, mchung diff -r 11289cf5c8fc -r e6383382c939 jdk/make/src/classes/build/tools/module/GenJdepsModulesXml.java --- a/jdk/make/src/classes/build/tools/module/GenJdepsModulesXml.java Tue Sep 16 09:01:57 2014 +0200 +++ b/jdk/make/src/classes/build/tools/module/GenJdepsModulesXml.java Tue Sep 16 12:06:53 2014 +0200 @@ -25,29 +25,17 @@ package build.tools.module; -import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; -import java.util.Objects; import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import javax.xml.namespace.QName; -import javax.xml.stream.*; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.XMLEvent; +import java.util.stream.Collectors; /** * GenJdepsModulesXml augments the input modules.xml file(s) @@ -97,14 +85,14 @@ Set modules = new HashSet<>(); for (; i < args.length; i++) { Path p = Paths.get(args[i]); - try (InputStream in = new BufferedInputStream(Files.newInputStream(p))) { - Set mods = gentool.load(in); - modules.addAll(mods); - } + modules.addAll(ModulesXmlReader.readModules(p) + .stream() + .map(gentool::buildIncludes) + .collect(Collectors.toSet())); } Files.createDirectories(outfile.getParent()); - gentool.writeXML(modules, outfile); + ModulesXmlWriter.writeModules(modules, outfile); } final Path modulepath; @@ -112,228 +100,21 @@ this.modulepath = modulepath; } - 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 String INCLUDE = "include"; - private static final QName REEXPORTS = new QName("re-exports"); - private Set load(InputStream in) throws XMLStreamException, IOException { - Set modules = new HashSet<>(); - XMLInputFactory factory = XMLInputFactory.newInstance(); - XMLEventReader stream = factory.createXMLEventReader(in); - Module.Builder mb = null; - String modulename = null; - String pkg = null; - Set 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 INCLUDE: - throw new RuntimeException("unexpected " + event); - 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: - buildIncludes(mb, modulename); - 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 String getData(XMLEventReader reader) throws XMLStreamException { - XMLEvent e = reader.nextEvent(); - if (e.isCharacters()) { - return e.asCharacters().getData(); - } - throw new RuntimeException(e.toString()); - } - - private 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); - } - private void writeXML(Set 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 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 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 void writeExportElement(XMLStreamWriter xtw, String pkg, int depth) { - writeExportElement(xtw, pkg, Collections.emptySet(), depth); - } - - private void writeExportElement(XMLStreamWriter xtw, String pkg, - Set 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 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)); - m.packages().stream().sorted() - .forEach(p -> writeElement(xtw, INCLUDE, p, 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 void writeStartElement(XMLStreamWriter xtw, String name, int depth) - throws XMLStreamException - { - xtw.writeCharacters(stack[depth]); - xtw.writeStartElement(name); - } - - private void writeEndElement(XMLStreamWriter xtw, int depth) throws XMLStreamException { - xtw.writeCharacters(stack[depth]); - xtw.writeEndElement(); - } - - private String packageName(Path p) { + private static String packageName(Path p) { return packageName(p.toString().replace(File.separatorChar, '/')); } - private String packageName(String name) { + private static String packageName(String name) { int i = name.lastIndexOf('/'); return (i > 0) ? name.substring(0, i).replace('/', '.') : ""; } - private boolean includes(String name) { - return name.endsWith(".class") && !name.equals("module-info.class"); + private static boolean includes(String name) { + return name.endsWith(".class"); } - public void buildIncludes(Module.Builder mb, String modulename) throws IOException { - Path mclasses = modulepath.resolve(modulename); + public Module buildIncludes(Module module) { + Module.Builder mb = new Module.Builder(module); + Path mclasses = modulepath.resolve(module.name()); try { Files.find(mclasses, Integer.MAX_VALUE, (Path p, BasicFileAttributes attr) -> includes(p.getFileName().toString())) @@ -341,145 +122,9 @@ .forEach(mb::include); } catch (NoSuchFileException e) { // aggregate module may not have class - } - } - - static class Module { - static class Dependence { - final String name; - final boolean reexport; - Dependence(String name) { - this(name, false); - } - Dependence(String name, boolean reexport) { - this.name = name; - this.reexport = 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; - } - } - private final String moduleName; - private final Set requires; - private final Map> exports; - private final Set packages; - - private Module(String name, - Set requires, - Map> exports, - Set packages) { - this.moduleName = name; - this.requires = Collections.unmodifiableSet(requires); - this.exports = Collections.unmodifiableMap(exports); - this.packages = Collections.unmodifiableSet(packages); - } - - public String name() { - return moduleName; - } - - public Set requires() { - return requires; - } - - public Map> exports() { - return exports; - } - - public Set packages() { - return packages; - } - - @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) - && packages.equals(that.packages)); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); } - - @Override - public int hashCode() { - int hc = moduleName.hashCode(); - hc = hc * 43 + requires.hashCode(); - hc = hc * 43 + exports.hashCode(); - hc = hc * 43 + packages.hashCode(); - return hc; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("module ").append(moduleName).append(" {").append("\n"); - requires.stream().sorted().forEach(d -> - sb.append(String.format(" requires %s%s%n", d.reexport ? "public " : "", d.name))); - exports.entrySet().stream().filter(e -> e.getValue().isEmpty()) - .sorted(Map.Entry.comparingByKey()) - .forEach(e -> sb.append(String.format(" exports %s%n", e.getKey()))); - exports.entrySet().stream().filter(e -> !e.getValue().isEmpty()) - .sorted(Map.Entry.comparingByKey()) - .forEach(e -> sb.append(String.format(" exports %s to %s%n", e.getKey(), e.getValue()))); - packages.stream().sorted().forEach(pn -> sb.append(String.format(" includes %s%n", pn))); - sb.append("}"); - return sb.toString(); - } - - static class Builder { - private String name; - private final Set requires = new HashSet<>(); - private final Map> exports = new HashMap<>(); - private final Set packages = new HashSet<>(); - - 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 include(String p) { - packages.add(p); - return this; - } - - public Builder export(String p) { - return exportTo(p, Collections.emptySet()); - } - - public Builder exportTo(String p, Set ms) { - Objects.requireNonNull(p); - Objects.requireNonNull(ms); - if (exports.containsKey(p)) { - throw new RuntimeException(name + " already exports " + p); - } - exports.put(p, new HashSet<>(ms)); - return this; - } - - public Module build() { - Module m = new Module(name, requires, exports, packages); - return m; - } - } + return mb.build(); } } diff -r 11289cf5c8fc -r e6383382c939 jdk/make/src/classes/build/tools/module/GenModulesList.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/src/classes/build/tools/module/GenModulesList.java Tue Sep 16 12:06:53 2014 +0200 @@ -0,0 +1,159 @@ +/* + * 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.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; + +/** + * $ java build.tools.module.GenModulesList \ + * -o modules.list \ + * top/modules.xml ... + */ +public final class GenModulesList { + private final static String USAGE = + "Usage: GenModulesList -o path-to-modules-xml"; + + private Set modules = new HashSet<>(); + private HashMap nameToModule = new HashMap<>(); + + public static void main(String[] args) throws Exception { + GenModulesList gen = new GenModulesList(); + gen.run(args); + } + + void run(String[] args) throws Exception { + Path outfile = null; + int i = 0; + while (i < args.length) { + String arg = args[i]; + if (arg.equals("-o")) { + outfile = Paths.get(args[i+1]); + i = i+2; + } else { + break; + } + } + if (outfile == null || i >= args.length) { + System.err.println(USAGE); + System.exit(-1); + } + + for (; i < args.length; i++) { + Path p = Paths.get(args[i]); + modules.addAll(ModulesXmlReader.readModules(p)); + } + + modules.stream() + .forEach(m -> nameToModule.put(m.name(), m)); + + Path parent = outfile.getParent(); + if (parent != null) + Files.createDirectories(parent); + + Iterable sortedModules = (new TopoSorter(modules)).result(); + try (PrintWriter writer = new PrintWriter(outfile.toFile())) { + for (Module m : sortedModules) { + if (isNotAggregator(m)) { + String deps = getModuleDependences(m).stream() + .filter(GenModulesList::isNotAggregator) + .map(Module::name) + .collect(Collectors.joining(" ")); + writer.format("%s: %s%n", m.name(), deps); + } + } + } + } + + private Module nameToModule(String name) { + return nameToModule.get(name); + } + + private Set getModuleDependences(Module m) { + return m.requires().stream() + .map(d -> d.name()) + .map(this::nameToModule) + .collect(Collectors.toSet()); + } + + static boolean isNotAggregator(Module m) { + return isNotAggregator(m.name()); + } + + static boolean isNotAggregator(String name) { + return AGGREGATORS.contains(name) ? false : true; + } + + static final List AGGREGATORS = Arrays.asList(new String[] { + "java.se", "java.compact1", "java.compact2", + "java.compact3", "jdk.compact3"}); + + class TopoSorter { + final Deque result = new LinkedList<>(); + final Deque nodes = new LinkedList<>(); + + TopoSorter(Collection nodes) { + nodes.stream() + .forEach(m -> this.nodes.add(m)); + + sort(); + } + + public Iterable result() { + return result; + } + + private void sort() { + Deque visited = new LinkedList<>(); + Deque done = new LinkedList<>(); + Module node; + while ((node = nodes.poll()) != null) { + if (!visited.contains(node)) { + visit(node, visited, done); + } + } + } + + private void visit(Module m, Deque visited, Deque done) { + if (visited.contains(m)) { + if (!done.contains(m)) { + throw new IllegalArgumentException("Cyclic detected: " + + m + " " + getModuleDependences(m)); + } + return; + } + visited.add(m); + getModuleDependences(m).stream() + .forEach(x -> visit(x, visited, done)); + done.add(m); + result.addLast(m); + } + } +} diff -r 11289cf5c8fc -r e6383382c939 jdk/make/src/classes/build/tools/module/Module.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/src/classes/build/tools/module/Module.java Tue Sep 16 12:06:53 2014 +0200 @@ -0,0 +1,178 @@ +/* + * 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.*; + +public class Module { + static class 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; + } + + @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; + } + } + private final String moduleName; + private final Set requires; + private final Map> exports; + private final Set packages; + + private Module(String name, + Set requires, + Map> exports, + Set packages) { + this.moduleName = name; + this.requires = Collections.unmodifiableSet(requires); + this.exports = Collections.unmodifiableMap(exports); + this.packages = Collections.unmodifiableSet(packages); + } + + public String name() { + return moduleName; + } + + public Set requires() { + return requires; + } + + public Map> exports() { + return exports; + } + + public Set packages() { + return packages; + } + + @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) + && packages.equals(that.packages)); + } + + @Override + public int hashCode() { + int hc = moduleName.hashCode(); + hc = hc * 43 + requires.hashCode(); + hc = hc * 43 + exports.hashCode(); + hc = hc * 43 + packages.hashCode(); + return hc; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("module ").append(moduleName).append(" {").append("\n"); + requires.stream().sorted().forEach(d -> + sb.append(String.format(" requires %s%s%n", d.reexport ? "public " : "", d.name))); + exports.entrySet().stream().filter(e -> e.getValue().isEmpty()) + .sorted(Map.Entry.comparingByKey()) + .forEach(e -> sb.append(String.format(" exports %s%n", e.getKey()))); + exports.entrySet().stream().filter(e -> !e.getValue().isEmpty()) + .sorted(Map.Entry.comparingByKey()) + .forEach(e -> sb.append(String.format(" exports %s to %s%n", e.getKey(), e.getValue()))); + packages.stream().sorted().forEach(pn -> sb.append(String.format(" includes %s%n", pn))); + sb.append("}"); + return sb.toString(); + } + + static class Builder { + private String name; + private final Set requires = new HashSet<>(); + private final Map> exports = new HashMap<>(); + private final Set packages = new HashSet<>(); + + public Builder() { + } + + public Builder(Module module) { + name = module.name(); + requires.addAll(module.requires()); + exports.putAll(module.exports()); + packages.addAll(module.packages()); + } + + 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 include(String p) { + packages.add(p); + return this; + } + + public Builder export(String p) { + return exportTo(p, Collections.emptySet()); + } + + public Builder exportTo(String p, Set ms) { + Objects.requireNonNull(p); + Objects.requireNonNull(ms); + if (exports.containsKey(p)) { + throw new RuntimeException(name + " already exports " + p); + } + exports.put(p, new HashSet<>(ms)); + return this; + } + + public Module build() { + Module m = new Module(name, requires, exports, packages); + return m; + } + } +} diff -r 11289cf5c8fc -r e6383382c939 jdk/make/src/classes/build/tools/module/ModulesXmlReader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/src/classes/build/tools/module/ModulesXmlReader.java Tue Sep 16 12:06:53 2014 +0200 @@ -0,0 +1,165 @@ +/* + * 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 readModules(Path modulesXml) + throws XMLStreamException, IOException + { + Set modules = new HashSet<>(); + try (InputStream in = new BufferedInputStream(Files.newInputStream(modulesXml))) { + Set 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 String INCLUDE = "include"; + private static final QName REEXPORTS = new QName("re-exports"); + private static Set load(InputStream in) + throws XMLStreamException, IOException + { + Set modules = new HashSet<>(); + XMLInputFactory factory = XMLInputFactory.newInstance(); + XMLEventReader stream = factory.createXMLEventReader(in); + Module.Builder mb = null; + String modulename = null; + String pkg = null; + Set 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 INCLUDE: + throw new RuntimeException("unexpected " + event); + 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); + } +} diff -r 11289cf5c8fc -r e6383382c939 jdk/make/src/classes/build/tools/module/ModulesXmlWriter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/make/src/classes/build/tools/module/ModulesXmlWriter.java Tue Sep 16 12:06:53 2014 +0200 @@ -0,0 +1,179 @@ +/* + * 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 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 String INCLUDE = "include"; + private static final QName REEXPORTS = new QName("re-exports"); + + private static void writeXML(Set 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 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)); + m.packages().stream().sorted() + .forEach(p -> writeElement(xtw, INCLUDE, p, 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(); + } +}