blob: 76345638fa139e10fdd9bf312c9e034d6e403284 [file] [log] [blame]
/*
* Copyright (c) 2016, 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.
*
* 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 sampleapi.generator;
import com.sun.source.tree.ModuleTree.ModuleKind;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.parser.Scanner;
import com.sun.tools.javac.parser.ScannerFactory;
import com.sun.tools.javac.parser.Tokens;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCDirective;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Names;
import com.sun.tools.javac.util.ListBuffer;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.ArrayList;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import sampleapi.SampleApi;
import sampleapi.util.PoorDocCommentTable;
import static com.sun.tools.javac.parser.Tokens.Comment.CommentStyle.JAVADOC;
/**
*
* This class is responsible for loading module description from an XML and then generating the
* <code>module-info.java</code>. It is using {@link PackageGenerator} class for parsing content of the module.
*/
public class ModuleGenerator {
private static final String UNNAMED = "UNNAMED";
private static final String MODULE_INFO = "module-info.java";
public String name;
public String id;
public ModuleKind kind;
public final List<Exports> exportss = new ArrayList<>();
public final List<Opens> openss = new ArrayList<>();
public final List<Requires> requiress = new ArrayList<>();
public final List<Uses> usess = new ArrayList<>();
public final List<Provides> providess = new ArrayList<>();
public final List<PackageGenerator> packages = new ArrayList<>();
private ModuleGenerator() {
}
public static ModuleGenerator load(Element rootElement) {
ModuleGenerator result = new ModuleGenerator();
result.name = rootElement.getAttribute("name");
result.id = rootElement.getAttribute("id");
String kind = rootElement.getAttribute("kind");
result.kind = kind.isEmpty() ? ModuleKind.STRONG :
ModuleKind.valueOf(kind.toUpperCase());
//exports
NodeList exportsList = rootElement.getElementsByTagName("exports");
for (int i = 0; i < exportsList.getLength(); i++) {
Element exportsEl = (Element) exportsList.item(i);
Exports exports = new Exports(exportsEl.getAttribute("package"));
NodeList toList = exportsEl.getElementsByTagName("to");
for (int j = 0; j < toList.getLength(); j++) {
Element toElement = (Element) toList.item(j);
exports.modules.add(toElement.getAttribute("module"));
}
result.exportss.add(exports);
}
//opens
NodeList opensList = rootElement.getElementsByTagName("opens");
for (int i = 0; i < opensList.getLength(); i++) {
Element opensEl = (Element) opensList.item(i);
Opens opens = new Opens(opensEl.getAttribute("package"));
NodeList toList = opensEl.getElementsByTagName("to");
for (int j = 0; j < toList.getLength(); j++) {
Element toElement = (Element) toList.item(j);
opens.modules.add(toElement.getAttribute("module"));
}
result.openss.add(opens);
}
//requires
NodeList requiresList = rootElement.getElementsByTagName("requires");
for (int i = 0; i < requiresList.getLength(); i++) {
Element requiresEl = (Element) requiresList.item(i);
result.requiress.add(new Requires(requiresEl.getAttribute("module"),
Boolean.parseBoolean(requiresEl.getAttribute("transitive")),
Boolean.parseBoolean(requiresEl.getAttribute("static"))));
}
//uses
NodeList usesList = rootElement.getElementsByTagName("uses");
for (int i = 0; i < usesList.getLength(); i++) {
Element usesEl = (Element) usesList.item(i);
result.usess.add(new Uses(usesEl.getAttribute("service")));
}
//provides
NodeList providesList = rootElement.getElementsByTagName("provides");
for (int i = 0; i < providesList.getLength(); i++) {
Element providesEl = (Element) providesList.item(i);
Provides provides = new Provides(providesEl.getAttribute("service"));
NodeList implList = providesEl.getElementsByTagName("implementation");
for (int j = 0; j < implList.getLength(); j++) {
Element implElement = (Element) implList.item(j);
provides.implementations.add(implElement.getAttribute("class"));
}
result.providess.add(provides);
}
//packages
NodeList packagesList = rootElement.getElementsByTagName("package");
for (int i = 0; i < packagesList.getLength(); i++) {
result.packages.add(PackageGenerator
.processDataSet((Element) packagesList.item(i)));
}
return result;
}
public void generate(Path outDir, SampleApi api) throws IOException {
Files.createDirectories(outDir);
Path moduleSourceDir;
if (!name.equals(UNNAMED)) {
moduleSourceDir = outDir.resolve(name);
Files.createDirectory(moduleSourceDir);
generateModuleInfo(moduleSourceDir, api);
} else {
moduleSourceDir = outDir;
}
packages.forEach(pkg -> pkg.generate(moduleSourceDir));
}
private void generateModuleInfo(Path moduleSourceDir, SampleApi api) throws IOException {
TreeMaker make = TreeMaker.instance(api.getContext());
Names names = Names.instance(api.getContext());
JCTree.JCExpression modQual = make.QualIdent(
new Symbol.ModuleSymbol(names.fromString(name), null));
ListBuffer<JCDirective> exportsBuffer = new ListBuffer<>();
exportss.forEach(e -> {
ListBuffer<JCExpression> modulesBuffer = new ListBuffer<>();
e.modules.stream()
.map(m -> api.isId(m) ? api.moduleById(m).name : m)
.forEach(m -> {
modulesBuffer.add(make.Ident(
names.fromString(m)));
});
exportsBuffer.add(make.Exports(
make.Ident(names.fromString(api.isId(e.pkg) ?
api.packageById(e.pkg).packageName : e.pkg)),
(modulesBuffer.size() > 0) ? modulesBuffer.toList() : null));
});
openss.forEach(o -> {
ListBuffer<JCExpression> modulesBuffer = new ListBuffer<>();
o.modules.stream()
.map(m -> api.isId(m) ? api.moduleById(m).name : m)
.forEach(m -> {
modulesBuffer.add(make.Ident(
names.fromString(m)));
});
exportsBuffer.add(make.Opens(
make.Ident(names.fromString(api.isId(o.pkg) ?
api.packageById(o.pkg).packageName : o.pkg)),
(modulesBuffer.size() > 0) ? modulesBuffer.toList() : null));
});
ListBuffer<JCDirective> requiresBuffer = new ListBuffer<>();
requiress.forEach(r -> requiresBuffer.add(make.Requires(
r.transitive, r.statc,
make.Ident(names.fromString(api.isId(r.module)
? api.moduleById(r.module).name : r.module)))));
ListBuffer<JCDirective> usesBuffer = new ListBuffer<>();
usess.forEach(u -> usesBuffer
.add(make.Uses(make.Ident(names.fromString(api.isId(u.service)
? api.classById(u.service) : u.service)))));
ListBuffer<JCDirective> providesBuffer = new ListBuffer<>();
providess.forEach(p -> {
ListBuffer<JCExpression> implementationsBuffer = new ListBuffer<>();
p.implementations.stream()
.map(c -> api.isId(c) ? api.classById(c) : c)
.forEach(i -> {
implementationsBuffer.add(make.Ident(names.fromString(i)));
});
providesBuffer.add(make.Provides(
make.Ident(names.fromString(api.isId(p.service) ?
api.classById(p.service) : p.service)),
implementationsBuffer.toList()));
});
ListBuffer<JCDirective> fullList = new ListBuffer<>();
fullList.addAll(exportsBuffer.toList());
fullList.addAll(requiresBuffer.toList());
fullList.addAll(usesBuffer.toList());
fullList.addAll(providesBuffer.toList());
JCTree.JCModuleDecl mod = make.ModuleDef(
make.Modifiers(0), //TODO how to support this?
kind, modQual, fullList.toList());
ListBuffer<JCTree> top = new ListBuffer<>();
top.add(mod);
JCTree.JCCompilationUnit compilationUnit = make.TopLevel(top.toList());
try (OutputStream module_info = Files.newOutputStream(moduleSourceDir.resolve(MODULE_INFO))) {
module_info.write(compilationUnit.toString().getBytes());
}
}
public static class Requires {
public String module;
public boolean transitive;
public boolean statc;
private Requires(String module, boolean transitive, boolean statc) {
this.module = module;
this.transitive = transitive;
this.statc = this.statc;
}
}
public static class Exports {
public final String pkg;
public final List<String> modules = new ArrayList<>();
private Exports(String pkg) {
this.pkg = pkg;
}
}
public static class Opens {
public final String pkg;
public final List<String> modules = new ArrayList<>();
private Opens(String pkg) {
this.pkg = pkg;
}
}
public static class Uses {
public final String service;
private Uses(String service) {
this.service = service;
}
}
public static class Provides {
public final String service;
public final List<String> implementations = new ArrayList<>();
private Provides(String service) {
this.service = service;
}
}
}