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