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