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 }