1 /*
2 * Copyright (c) 2016, 2020, 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 package org.openjdk.asmtools.common;
24
25 import org.openjdk.asmtools.jdis.Indenter;
26
27 import java.util.*;
28 import java.util.stream.Collectors;
29
30 import static java.lang.String.format;
31
32 /**
33 * Internal presentation of a module
34 */
35 public final class Module extends Indenter {
36
37 //* A module name and module_flags
38 public final Header header;
39 //* A service dependence's of this module
40 public final Set<Uses> uses;
41 //* Modules on which the current module has a dependence.
42 public final Set<Dependence> requires;
43 //* A module exports, may be qualified or unqualified.
44 public final Map<Exported, Set<String>> exports;
45 //* Packages, to be opened by the current module
46 public final Map<Opened, Set<String>> opens;
47 //* A service that a module provides one or more implementations of.
48 public final Map<Provided, Set<String>> provides;
49
50 private Module(Builder builder) {
51 this.header = builder.header;
52 this.requires = Collections.unmodifiableSet(builder.requires);
53 this.exports = Collections.unmodifiableMap(builder.exports);
54 this.opens = Collections.unmodifiableMap(builder.opens);
55 this.uses = Collections.unmodifiableSet(builder.uses);
56 this.provides = Collections.unmodifiableMap(builder.provides);
57 }
58
59 public String getModuleFlags () {
60 return Modifier.getModuleModifiers(header.getFlags());
61 }
62 public String getModuleName () { return header.getModuleName(); }
63 public String getModuleVersion() { return header.getModuleVersion(); };
64
65 @Override
66 public String toString() {
67 StringBuilder sb = new StringBuilder();
68 int l = 0;
69 requires.stream()
70 .sorted()
71 .forEach(d -> sb.append(getIndentString()).append(format("requires %s;%s%n",
72 d.toString(),
73 d.getModuleVersion() == null ? "" : " // @" + d.getModuleVersion())));
74 //
75 l = newLine(sb,l);
76 exports.entrySet().stream()
77 .filter(e -> e.getValue().isEmpty())
78 .sorted(Map.Entry.comparingByKey())
79 .map(e -> format("%sexports %s;%n", getIndentString(), e.getKey().toString()))
80 .forEach(sb::append);
81 exports.entrySet().stream()
82 .filter(e -> !e.getValue().isEmpty())
83 .sorted(Map.Entry.comparingByKey())
84 .map(e -> format("%sexports %s to%n%s;%n", getIndentString(), e.getKey().toString(),
85 e.getValue().stream().sorted()
86 .map(mn -> format("%s %s", getIndentString(), mn))
87 .collect(Collectors.joining(",\n"))))
88 .forEach(sb::append);
89 //
90 l = newLine(sb,l);
91 opens.entrySet().stream()
92 .filter(e -> e.getValue().isEmpty())
93 .sorted(Map.Entry.comparingByKey())
94 .map(e -> format("%sopens %s;%n", getIndentString(), e.getKey().toString()))
95 .forEach(sb::append);
96 opens.entrySet().stream()
97 .filter(e -> !e.getValue().isEmpty())
98 .sorted(Map.Entry.comparingByKey())
99 .map(e -> format("%sopens %s to%n%s;%n", getIndentString(), e.getKey().toString(),
100 e.getValue().stream().sorted()
101 .map(mn -> format("%s %s", getIndentString(), mn))
102 .collect(Collectors.joining(",\n"))))
103 .forEach(sb::append);
104 //
105 l = newLine(sb,l);
106 uses.stream().sorted()
107 .map(s -> format("%suses %s;%n", getIndentString(), s))
108 .forEach(sb::append);
109 //
110 l = newLine(sb,l);
111 provides.entrySet().stream()
112 .filter(e -> !e.getValue().isEmpty())
113 .sorted(Map.Entry.comparingByKey())
114 .map(e -> format("%sprovides %s with%n%s;%n", getIndentString(), e.getKey().toString(),
115 e.getValue().stream().sorted()
116 .map(mn -> format("%s %s", getIndentString(), mn))
117 .collect(Collectors.joining(",\n"))))
118 .forEach(sb::append);
119 //
120 if( Character.isWhitespace(sb.charAt(sb.length()-1)) )
121 sb.deleteCharAt(sb.length()-1);
122 return sb.toString();
123 }
124
125 private int newLine(StringBuilder sb, int length) {
126 if(sb.length() > length) {
127 sb.append("\n");
128 return sb.length() + 1;
129 }
130 return length;
131 }
132
133 /**
134 * Modules flags
135 */
136 public enum Modifier {
137 ACC_NONE(0x0000, "", ""),
138 ACC_OPEN(0x0020, "open", "ACC_OPEN"),
139 ACC_TRANSITIVE(0x0020, "transitive", "ACC_TRANSITIVE"),
140 ACC_STATIC_PHASE(0x0040, "static", "ACC_STATIC_PHASE"),
141 ACC_SYNTHETIC(0x1000, "", "ACC_SYNTHETIC"),
142 ACC_MANDATED(0x8000, "", "ACC_MANDATED");
143 private final int value;
144 private final String keyword;
145 private final String flag;
146 Modifier(int value, String keyword, String flagName) {
147 this.value = value;
148 this.keyword = keyword;
149 this.flag = flagName;
150 }
151
152 public int asInt() { return value; }
153
154 public static String getModuleModifiers(int flag) {
155 return asString(flag, false, ACC_TRANSITIVE);
156 }
157
158 public static String getModuleFlags(int flag) {
159 return asString(flag, true, ACC_TRANSITIVE);
160 }
161
162 public static String getStatementModifiers(int flag) {
163 return asString(flag, false, ACC_OPEN);
164 }
165
166 public static String getStatementFlags(int flag) {
167 return asString(flag, true, ACC_OPEN);
168 }
169
170 private static String asString(int value, boolean flagFormat, Modifier skipped ) {
171 String buf = "";
172 for(Module.Modifier m : values()) {
173 if( m != skipped && (value & m.value) != 0) {
174 buf += ((flagFormat) ? m.flag : m.keyword) + " ";
175 value ^= m.value;
176 }
177 }
178 if( flagFormat && value != 0 )
179 buf += String.format("0x%04X ", value);
180 return buf;
181 }
182 }
183
184 // A module header consists of a module name and module flags
185 public final static class Header extends VersionedFlaggedTargetType{
186 Header(String typeName, int flag) { this(typeName, flag, null); }
187 Header(String typeName, int flag, String moduleVersion) { super(typeName, flag, moduleVersion); }
188 public String getModuleName() { return getTypeName(); }
189 public int getModuleFlags() { return getFlags(); }
190 public String getModuleVersion() { return getVersion(); }
191 }
192
193 //* A module on which the current module has a dependence.
194 public final static class Dependence extends VersionedFlaggedTargetType {
195 public Dependence(String moduleName, int flag) {this(moduleName, flag, null);}
196 public Dependence(String moduleName, int flag, String moduleVersion) {super(moduleName, flag, moduleVersion);}
197 public Dependence(String moduleName, boolean transitive, boolean staticPhase) { this(moduleName,transitive,staticPhase,null);}
198 public Dependence(String moduleName, boolean transitive, boolean staticPhase, String moduleVersion) {
199 this(moduleName,
200 (transitive ? Modifier.ACC_TRANSITIVE.value : Modifier.ACC_NONE.value) |
201 (staticPhase ? Modifier.ACC_STATIC_PHASE.value : Modifier.ACC_NONE.value), moduleVersion);
202 }
203 public String getModuleVersion() { return getVersion(); }
204 }
205
206 public final static class Uses extends TargetType {
207 public Uses(String typeName) { super(typeName); }
208 }
209
210 //* A provided type of the current module.
211 public final static class Provided extends TargetType {
212 public Provided(String typeName) { super(typeName); }
213 }
214
215 //* An opened package of the current module.
216 public final static class Opened extends FlaggedTargetType {
217 public Opened(String typeName) {
218 super(typeName, 0);
219 }
220 public Opened(String typeName, int opensFlags) {
221 super(typeName, opensFlags);
222 }
223 }
224
225 //* An exported package of the current module.
226 public final static class Exported extends FlaggedTargetType {
227 public Exported(String typeName) {
228 super(typeName, 0);
229 }
230
231 public Exported(String typeName, int exportsFlags) {
232 super(typeName, exportsFlags);
233 }
234 }
235
236 public static class VersionedFlaggedTargetType extends FlaggedTargetType {
237 private String version;
238
239 VersionedFlaggedTargetType(String typeName, int flag) {
240 this(typeName,flag, null);
241 }
242
243 VersionedFlaggedTargetType(String typeName, int flag, String version) {
244 super(typeName, flag);
245 this.version = version != null && !version.isEmpty() ? version : null;
246 }
247 public String getVersion() { return version; }
248
249 @Override
250 public int hashCode() {
251 int code = version == null ? 0 : version.hashCode();
252 return code + super.hashCode();
253 }
254 }
255
256 public static class FlaggedTargetType extends TargetType {
257 private int flag;
258
259 FlaggedTargetType(String typeName, int flag) {
260 super(typeName);
261 this.flag = flag;
262 }
263
264 public boolean isFlagged() {
265 return true;
266 }
267
268 public int getFlags() {
269 return flag;
270 }
271
272 public void setFlag(int value) { flag = value; }
273
274 @Override
275 public int hashCode() {
276 return super.hashCode() + flag;
277 }
278
279 @Override
280 public boolean equals(Object o) {
281 return super.equals(o) && ((FlaggedTargetType) o).flag == this.flag;
282 }
283
284 @Override
285 public String toString() {
286 return Modifier.getStatementModifiers(this.flag)+ super.toString();
287 }
288 }
289
290 public static class TargetType implements Comparable<TargetType> {
291 private String typeName;
292
293 TargetType(String typeName) { this.typeName = typeName; }
294
295 public String getTypeName() {
296 return typeName;
297 }
298
299 public void setTypeName(String value) { typeName = value; }
300
301 public boolean isFlagged() {
302 return false;
303 }
304
305 @Override
306 public int hashCode() { return typeName.hashCode() * 11; }
307
308 @Override
309 public boolean equals(Object o) {
310 if (o instanceof TargetType) {
311 TargetType t = (TargetType) o;
312 return this.typeName.equals(t.getTypeName());
313 }
314 return false;
315 }
316
317 @Override
318 public int compareTo(TargetType t) {
319 return this.typeName.compareTo(t.getTypeName());
320 }
321
322 @Override
323 public String toString() {
324 return typeName;
325 }
326 }
327
328 /**
329 * The module builder.
330 */
331 public static final class Builder {
332 final Header header;
333 final Set<Dependence> requires = new HashSet<>();
334 final Map<Exported, Set<String>> exports = new HashMap<>();
335 final Map<Opened, Set<String>> opens = new HashMap<>();
336 final Set<Uses> uses = new HashSet<>();
337 final Map<Provided, Set<String>> provides = new HashMap<>();
338
339
340 public Builder() {
341 this("", Modifier.ACC_NONE.asInt(), null);
342 }
343
344 public Builder(String moduleName, int moduleFlags, String moduleVersion) {
345 header = new Header( moduleName,moduleFlags, moduleVersion);
346 }
347
348 public Builder setModuleFlags(int moduleFlags) {
349 header.setFlag(header.getFlags() | moduleFlags);
350 return this;
351 }
352
353 public Builder setModuleFlags(Modifier... moduleFlags) {
354 for (Modifier m : moduleFlags)
355 setModuleFlags(m.value);
356 return this;
357 }
358
359 public Builder setModuleName(String value) {
360 header.setTypeName(value);
361 return this;
362 }
363
364 public Builder require(String d, boolean transitive, boolean staticPhase, String version) {
365 requires.add(new Dependence(d, transitive, staticPhase, version));
366 return this;
367 }
368
369 public Builder require(String d, int requiresFlag, String version) {
370 requires.add(new Dependence(d, requiresFlag, version));
371 return this;
372 }
373
374 public Builder require(String d, int requiresFlag) {
375 requires.add(new Dependence(d, requiresFlag, null));
376 return this;
377 }
378
379 public Builder opens(Opened p, Set<String> ms) {
380 return add(opens, p, ms);
381 }
382
383 public Builder opens(String packageName, int exportFlags, Set<String> ms) {
384 return add(opens, new Opened(packageName, exportFlags), ms);
385 }
386
387 public Builder opens(String packageName, int exportFlags) {
388 return add(opens, new Opened(packageName, exportFlags), new HashSet<>());
389 }
390
391
392 public Builder exports(Exported p, Set<String> ms) {
393 return add(exports, p, ms);
394 }
395
396 public Builder exports(String packageName, int exportFlags, Set<String> ms) {
397 return add(exports, new Exported(packageName, exportFlags), ms);
398 }
399
400 public Builder exports(String packageName, int exportFlags) {
401 return add(exports, new Exported(packageName, exportFlags), new HashSet<>());
402 }
403
404 public Builder uses(String serviceName) {
405 uses.add(new Uses(serviceName));
406 return this;
407 }
408
409
410 public Builder uses(Set<String> serviceNames) {
411 uses.addAll(serviceNames.stream().map(Uses::new).collect(Collectors.toList()));
412 return this;
413 }
414
415 public Builder provides(Provided t, Set<String> implementations) {
416 return add(provides, t, implementations);
417 }
418
419 public Builder provides(String serviceName, Set<String> implementations) {
420 return add(provides, new Provided(serviceName), implementations);
421 }
422
423
424 /**
425 * @return The new module
426 */
427 public Module build() {
428 return new Module(this);
429 }
430
431 private <T extends TargetType> Builder add( Map<T, Set<String>> collection, T source, Set<String> target) {
432 Objects.requireNonNull(source);
433 Objects.requireNonNull(target);
434 if (!collection.containsKey(source))
435 collection.put(source, new HashSet<>());
436 collection.get(source).addAll(target);
437 return this;
438 }
439 }
440 }