1/* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23package sampleapi.generator; 24 25import com.sun.source.tree.ModuleTree.ModuleKind; 26import com.sun.tools.javac.code.Symbol; 27import com.sun.tools.javac.parser.Scanner; 28import com.sun.tools.javac.parser.ScannerFactory; 29import com.sun.tools.javac.parser.Tokens; 30import com.sun.tools.javac.tree.JCTree; 31import com.sun.tools.javac.tree.JCTree.JCDirective; 32import com.sun.tools.javac.tree.JCTree.JCExpression; 33import com.sun.tools.javac.tree.TreeMaker; 34import com.sun.tools.javac.util.Context; 35import com.sun.tools.javac.util.Names; 36import com.sun.tools.javac.util.ListBuffer; 37import java.io.IOException; 38import java.io.OutputStream; 39import java.nio.file.Files; 40import java.nio.file.Path; 41import java.util.List; 42import java.util.ArrayList; 43import org.w3c.dom.Element; 44import org.w3c.dom.NodeList; 45 46import sampleapi.SampleApi; 47import sampleapi.util.PoorDocCommentTable; 48 49import static com.sun.tools.javac.parser.Tokens.Comment.CommentStyle.JAVADOC; 50 51/** 52 * 53 * This class is responsible for loading module description from an XML and then generating the 54 * <code>module-info.java</code>. It is using {@link PackageGenerator} class for parsing content of the module. 55 */ 56public class ModuleGenerator { 57 58 private static final String UNNAMED = "UNNAMED"; 59 private static final String MODULE_INFO = "module-info.java"; 60 61 public String name; 62 public String id; 63 public ModuleKind kind; 64 public final List<Exports> exportss = new ArrayList<>(); 65 public final List<Opens> openss = new ArrayList<>(); 66 public final List<Requires> requiress = new ArrayList<>(); 67 public final List<Uses> usess = new ArrayList<>(); 68 public final List<Provides> providess = new ArrayList<>(); 69 public final List<PackageGenerator> packages = new ArrayList<>(); 70 71 private ModuleGenerator() { 72 } 73 74 public static ModuleGenerator load(Element rootElement) { 75 ModuleGenerator result = new ModuleGenerator(); 76 result.name = rootElement.getAttribute("name"); 77 result.id = rootElement.getAttribute("id"); 78 String kind = rootElement.getAttribute("kind"); 79 result.kind = kind.isEmpty() ? ModuleKind.STRONG : 80 ModuleKind.valueOf(kind.toUpperCase()); 81 //exports 82 NodeList exportsList = rootElement.getElementsByTagName("exports"); 83 for (int i = 0; i < exportsList.getLength(); i++) { 84 Element exportsEl = (Element) exportsList.item(i); 85 Exports exports = new Exports(exportsEl.getAttribute("package")); 86 NodeList toList = exportsEl.getElementsByTagName("to"); 87 for (int j = 0; j < toList.getLength(); j++) { 88 Element toElement = (Element) toList.item(j); 89 exports.modules.add(toElement.getAttribute("module")); 90 } 91 result.exportss.add(exports); 92 } 93 //opens 94 NodeList opensList = rootElement.getElementsByTagName("opens"); 95 for (int i = 0; i < opensList.getLength(); i++) { 96 Element opensEl = (Element) opensList.item(i); 97 Opens opens = new Opens(opensEl.getAttribute("package")); 98 NodeList toList = opensEl.getElementsByTagName("to"); 99 for (int j = 0; j < toList.getLength(); j++) { 100 Element toElement = (Element) toList.item(j); 101 opens.modules.add(toElement.getAttribute("module")); 102 } 103 result.openss.add(opens); 104 } 105 //requires 106 NodeList requiresList = rootElement.getElementsByTagName("requires"); 107 for (int i = 0; i < requiresList.getLength(); i++) { 108 Element requiresEl = (Element) requiresList.item(i); 109 result.requiress.add(new Requires(requiresEl.getAttribute("module"), 110 Boolean.parseBoolean(requiresEl.getAttribute("transitive")), 111 Boolean.parseBoolean(requiresEl.getAttribute("static")))); 112 } 113 //uses 114 NodeList usesList = rootElement.getElementsByTagName("uses"); 115 for (int i = 0; i < usesList.getLength(); i++) { 116 Element usesEl = (Element) usesList.item(i); 117 result.usess.add(new Uses(usesEl.getAttribute("service"))); 118 } 119 //provides 120 NodeList providesList = rootElement.getElementsByTagName("provides"); 121 for (int i = 0; i < providesList.getLength(); i++) { 122 Element providesEl = (Element) providesList.item(i); 123 Provides provides = new Provides(providesEl.getAttribute("service")); 124 NodeList implList = providesEl.getElementsByTagName("implementation"); 125 for (int j = 0; j < implList.getLength(); j++) { 126 Element implElement = (Element) implList.item(j); 127 provides.implementations.add(implElement.getAttribute("class")); 128 } 129 result.providess.add(provides); 130 } 131 //packages 132 NodeList packagesList = rootElement.getElementsByTagName("package"); 133 for (int i = 0; i < packagesList.getLength(); i++) { 134 result.packages.add(PackageGenerator 135 .processDataSet((Element) packagesList.item(i))); 136 } 137 return result; 138 } 139 140 public void generate(Path outDir, SampleApi api) throws IOException { 141 Files.createDirectories(outDir); 142 Path moduleSourceDir; 143 if (!name.equals(UNNAMED)) { 144 moduleSourceDir = outDir.resolve(name); 145 Files.createDirectory(moduleSourceDir); 146 generateModuleInfo(moduleSourceDir, api); 147 } else { 148 moduleSourceDir = outDir; 149 } 150 packages.forEach(pkg -> pkg.generate(moduleSourceDir)); 151 } 152 153 private void generateModuleInfo(Path moduleSourceDir, SampleApi api) throws IOException { 154 TreeMaker make = TreeMaker.instance(api.getContext()); 155 Names names = Names.instance(api.getContext()); 156 JCTree.JCExpression modQual = make.QualIdent( 157 new Symbol.ModuleSymbol(names.fromString(name), null)); 158 ListBuffer<JCDirective> exportsBuffer = new ListBuffer<>(); 159 exportss.forEach(e -> { 160 ListBuffer<JCExpression> modulesBuffer = new ListBuffer<>(); 161 e.modules.stream() 162 .map(m -> api.isId(m) ? api.moduleById(m).name : m) 163 .forEach(m -> { 164 modulesBuffer.add(make.Ident( 165 names.fromString(m))); 166 }); 167 exportsBuffer.add(make.Exports( 168 make.Ident(names.fromString(api.isId(e.pkg) ? 169 api.packageById(e.pkg).packageName : e.pkg)), 170 (modulesBuffer.size() > 0) ? modulesBuffer.toList() : null)); 171 }); 172 openss.forEach(o -> { 173 ListBuffer<JCExpression> modulesBuffer = new ListBuffer<>(); 174 o.modules.stream() 175 .map(m -> api.isId(m) ? api.moduleById(m).name : m) 176 .forEach(m -> { 177 modulesBuffer.add(make.Ident( 178 names.fromString(m))); 179 }); 180 exportsBuffer.add(make.Opens( 181 make.Ident(names.fromString(api.isId(o.pkg) ? 182 api.packageById(o.pkg).packageName : o.pkg)), 183 (modulesBuffer.size() > 0) ? modulesBuffer.toList() : null)); 184 }); 185 ListBuffer<JCDirective> requiresBuffer = new ListBuffer<>(); 186 requiress.forEach(r -> requiresBuffer.add(make.Requires( 187 r.transitive, r.statc, 188 make.Ident(names.fromString(api.isId(r.module) 189 ? api.moduleById(r.module).name : r.module))))); 190 ListBuffer<JCDirective> usesBuffer = new ListBuffer<>(); 191 usess.forEach(u -> usesBuffer 192 .add(make.Uses(make.Ident(names.fromString(api.isId(u.service) 193 ? api.classById(u.service) : u.service))))); 194 ListBuffer<JCDirective> providesBuffer = new ListBuffer<>(); 195 providess.forEach(p -> { 196 ListBuffer<JCExpression> implementationsBuffer = new ListBuffer<>(); 197 p.implementations.stream() 198 .map(c -> api.isId(c) ? api.classById(c) : c) 199 .forEach(i -> { 200 implementationsBuffer.add(make.Ident(names.fromString(i))); 201 }); 202 providesBuffer.add(make.Provides( 203 make.Ident(names.fromString(api.isId(p.service) ? 204 api.classById(p.service) : p.service)), 205 implementationsBuffer.toList())); 206 }); 207 ListBuffer<JCDirective> fullList = new ListBuffer<>(); 208 fullList.addAll(exportsBuffer.toList()); 209 fullList.addAll(requiresBuffer.toList()); 210 fullList.addAll(usesBuffer.toList()); 211 fullList.addAll(providesBuffer.toList()); 212 JCTree.JCModuleDecl mod = make.ModuleDef( 213 make.Modifiers(0), //TODO how to support this? 214 kind, modQual, fullList.toList()); 215 ListBuffer<JCTree> top = new ListBuffer<>(); 216 top.add(mod); 217 JCTree.JCCompilationUnit compilationUnit = make.TopLevel(top.toList()); 218 try (OutputStream module_info = Files.newOutputStream(moduleSourceDir.resolve(MODULE_INFO))) { 219 module_info.write(compilationUnit.toString().getBytes()); 220 } 221 } 222 223 224 public static class Requires { 225 public String module; 226 public boolean transitive; 227 public boolean statc; 228 229 private Requires(String module, boolean transitive, boolean statc) { 230 this.module = module; 231 this.transitive = transitive; 232 this.statc = this.statc; 233 } 234 } 235 236 public static class Exports { 237 public final String pkg; 238 public final List<String> modules = new ArrayList<>(); 239 240 private Exports(String pkg) { 241 this.pkg = pkg; 242 } 243 } 244 245 public static class Opens { 246 public final String pkg; 247 public final List<String> modules = new ArrayList<>(); 248 249 private Opens(String pkg) { 250 this.pkg = pkg; 251 } 252 } 253 254 public static class Uses { 255 public final String service; 256 257 private Uses(String service) { 258 this.service = service; 259 } 260 } 261 262 public static class Provides { 263 public final String service; 264 public final List<String> implementations = new ArrayList<>(); 265 266 private Provides(String service) { 267 this.service = service; 268 } 269 } 270} 271