1/* 2 * Copyright (c) 2015, 2017, 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 */ 23 24package toolbox; 25 26import java.io.File; 27import java.io.IOException; 28import java.nio.file.Files; 29import java.nio.file.Path; 30import java.nio.file.Paths; 31import java.util.ArrayList; 32import java.util.Arrays; 33import java.util.LinkedHashSet; 34import java.util.List; 35import java.util.Set; 36import java.util.stream.Collectors; 37 38/** 39 * Builder for module declarations. 40 */ 41public class ModuleBuilder { 42 43 private final ToolBox tb; 44 private final String name; 45 private String comment = ""; 46 private List<String> requires = new ArrayList<>(); 47 private List<String> exports = new ArrayList<>(); 48 private List<String> opens = new ArrayList<>(); 49 private List<String> uses = new ArrayList<>(); 50 private List<String> provides = new ArrayList<>(); 51 private List<String> content = new ArrayList<>(); 52 private Set<Path> modulePath = new LinkedHashSet<>(); 53 54 /** 55 * Creates a builder for a module. 56 * @param tb a Toolbox that can be used to compile the module declaration. 57 * @param name the name of the module to be built 58 */ 59 public ModuleBuilder(ToolBox tb, String name) { 60 this.tb = tb; 61 this.name = name; 62 } 63 64 /** 65 * Sets the doc comment for the declaration. 66 * @param comment the content of the comment, excluding the initial 67 * '/**', leading whitespace and asterisks, and the final trailing 'a;/'. 68 * @return this builder 69 */ 70 public ModuleBuilder comment(String comment) { 71 this.comment = comment; 72 return this; 73 } 74 75 /** 76 * Adds a "requires" directive to the declaration. 77 * @param module the name of the module that is required 78 * @param modulePath a path in while to locate the modules 79 * if the declaration is compiled 80 * @return this builder 81 */ 82 public ModuleBuilder requires(String module, Path... modulePath) { 83 addDirective(requires, "requires " + module + ";"); 84 this.modulePath.addAll(Arrays.asList(modulePath)); 85 return this; 86 87 } 88 89 /** 90 * Adds a "requires static" directive to the declaration. 91 * @param module the name of the module that is required 92 * @param modulePath a path in which to locate the modules 93 * if the declaration is compiled 94 * @return this builder 95 */ 96 public ModuleBuilder requiresStatic(String module, Path... modulePath) { 97 addDirective(requires, "requires static " + module + ";"); 98 this.modulePath.addAll(Arrays.asList(modulePath)); 99 return this; 100 } 101 102 /** 103 * Adds a "requires transitive" directive to the declaration. 104 * @param module the name of the module that is required 105 * @param modulePath a path in which to locate the modules 106 * if the declaration is compiled 107 * @return this builder 108 */ 109 public ModuleBuilder requiresTransitive(String module, Path... modulePath) { 110 addDirective(requires, "requires transitive " + module + ";"); 111 this.modulePath.addAll(Arrays.asList(modulePath)); 112 return this; 113 } 114 115 /** 116 * Adds a "requires static transitive" directive to the declaration. 117 * @param module the name of the module that is required 118 * @param modulePath a path in which to locate the modules 119 * if the declaration is compiled 120 * @return this builder 121 */ 122 public ModuleBuilder requiresStaticTransitive(String module, Path... modulePath) { 123 addDirective(requires, "requires static transitive " + module + ";"); 124 this.modulePath.addAll(Arrays.asList(modulePath)); 125 return this; 126 } 127 128 /** 129 * Adds an unqualified "exports" directive to the declaration. 130 * @param pkg the name of the package to be exported 131 * @return this builder 132 */ 133 public ModuleBuilder exports(String pkg) { 134 return addDirective(exports, "exports " + pkg + ";"); 135 } 136 137 /** 138 * Adds a qualified "exports" directive to the declaration. 139 * @param pkg the name of the package to be exported 140 * @param module the name of the module to which it is to be exported 141 * @return this builder 142 */ 143 public ModuleBuilder exportsTo(String pkg, String module) { 144 return addDirective(exports, "exports " + pkg + " to " + module + ";"); 145 } 146 147 /** 148 * Adds an unqualified "opens" directive to the declaration. 149 * @param pkg the name of the package to be opened 150 * @return this builder 151 */ 152 public ModuleBuilder opens(String pkg) { 153 return addDirective(opens, "opens " + pkg + ";"); 154 } 155 156 /** 157 * Adds a qualified "opens" directive to the declaration. 158 * @param pkg the name of the package to be opened 159 * @param module the name of the module to which it is to be opened 160 * @return this builder 161 */ 162 public ModuleBuilder opensTo(String pkg, String module) { 163 return addDirective(opens, "opens " + pkg + " to " + module + ";"); 164 } 165 166 /** 167 * Adds a "uses" directive to the declaration. 168 * @param service the name of the service type 169 * @return this builder 170 */ 171 public ModuleBuilder uses(String service) { 172 return addDirective(uses, "uses " + service + ";"); 173 } 174 175 /** 176 * Adds a "provides" directive to the declaration. 177 * @param service the name of the service type 178 * @param implementation the name of the implementation type 179 * @return this builder 180 */ 181 public ModuleBuilder provides(String service, String implementation) { 182 return addDirective(provides, "provides " + service + " with " + implementation + ";"); 183 } 184 185 private ModuleBuilder addDirective(List<String> directives, String directive) { 186 directives.add(directive); 187 return this; 188 } 189 190 /** 191 * Adds type definitions to the module. 192 * @param content a series of strings, each representing the content of 193 * a compilation unit to be included with the module 194 * @return this builder 195 */ 196 public ModuleBuilder classes(String... content) { 197 this.content.addAll(Arrays.asList(content)); 198 return this; 199 } 200 201 /** 202 * Writes the module declaration and associated additional compilation 203 * units to a module directory within a given directory. 204 * @param srcDir the directory in which a directory will be created 205 * to contain the source files for the module 206 * @return the directory containing the source files for the module 207 */ 208 public Path write(Path srcDir) throws IOException { 209 Files.createDirectories(srcDir); 210 List<String> sources = new ArrayList<>(); 211 StringBuilder sb = new StringBuilder(); 212 if (!comment.isEmpty()) { 213 sb.append("/**\n * ") 214 .append(comment.replace("\n", "\n * ")) 215 .append("\n */\n"); 216 } 217 sb.append("module ").append(name).append(" {\n"); 218 requires.forEach(r -> sb.append(" " + r + "\n")); 219 exports.forEach(e -> sb.append(" " + e + "\n")); 220 opens.forEach(o -> sb.append(" " + o + "\n")); 221 uses.forEach(u -> sb.append(" " + u + "\n")); 222 provides.forEach(p -> sb.append(" " + p + "\n")); 223 sb.append("}"); 224 sources.add(sb.toString()); 225 sources.addAll(content); 226 Path moduleSrc = srcDir.resolve(name); 227 tb.writeJavaFiles(moduleSrc, sources.toArray(new String[]{})); 228 return moduleSrc; 229 } 230 231 /** 232 * Writes the source files for the module to an interim directory, 233 * and then compiles them to a given directory. 234 * @param modules the directory in which a directory will be created 235 * to contain the compiled class files for the module 236 * @throws IOException if an error occurs while compiling the files 237 */ 238 public void build(Path modules) throws IOException { 239 build(Paths.get(modules + "Src"), modules); 240 } 241 242 /** 243 * Writes the source files for the module to a specified directory, 244 * and then compiles them to a given directory. 245 * @param srcDir the directory in which a directory will be created 246 * to contain the source files for the module 247 * @param modules the directory in which a directory will be created 248 * to contain the compiled class files for the module 249 * @throws IOException if an error occurs while compiling the files 250 */ 251 public void build(Path src, Path modules) throws IOException { 252 Path moduleSrc = write(src); 253 String mp = modulePath.stream() 254 .map(Path::toString) 255 .collect(Collectors.joining(File.pathSeparator)); 256 new JavacTask(tb) 257 .outdir(Files.createDirectories(modules.resolve(name))) 258 .options("--module-path", mp) 259 .files(tb.findJavaFiles(moduleSrc)) 260 .run() 261 .writeAll(); 262 } 263} 264