/* * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package org.openjdk.asmtools.common; import java.util.*; import java.util.stream.Collectors; import static java.lang.String.format; /** * Internal presentation of a module */ public final class Module { //* A module name and module_flags public final Header header; //* A service dependence's of this module public final Set uses; //* Modules on which the current module has a dependence. public final Set requires; //* A module exports, may be qualified or unqualified. public final Map> exports; //* Packages, to be opened by the current module public final Map> opens; //* A service that a module provides one or more implementations of. public final Map> provides; private Module(Builder builder) { this.header = builder.header; this.requires = Collections.unmodifiableSet(builder.requires); this.exports = Collections.unmodifiableMap(builder.exports); this.opens = Collections.unmodifiableMap(builder.opens); this.uses = Collections.unmodifiableSet(builder.uses); this.provides = Collections.unmodifiableMap(builder.provides); } public String getModuleFlags () { return Modifier.getModuleModifiers(header.getFlags()); } public String getModuleName () { return header.getModuleName(); } public String getModuleVersion() { return header.getModuleVersion(); }; @Override public String toString() { StringBuilder sb = new StringBuilder(); int l = 0; requires.stream() .sorted() .forEach(d -> sb.append(format(" requires %s;%s%n", d.toString(), d.getModuleVersion() == null ? "" : " // @" + d.getModuleVersion()))); // l = newLine(sb,l); exports.entrySet().stream() .filter(e -> e.getValue().isEmpty()) .sorted(Map.Entry.comparingByKey()) .map(e -> format(" exports %s;%n", e.getKey().toString())) .forEach(sb::append); exports.entrySet().stream() .filter(e -> !e.getValue().isEmpty()) .sorted(Map.Entry.comparingByKey()) .map(e -> format(" exports %s to%n%s;%n", e.getKey().toString(), e.getValue().stream().sorted() .map(mn -> format(" %s", mn)) .collect(Collectors.joining(",\n")))) .forEach(sb::append); // l = newLine(sb,l); opens.entrySet().stream() .filter(e -> e.getValue().isEmpty()) .sorted(Map.Entry.comparingByKey()) .map(e -> format(" opens %s;%n", e.getKey().toString())) .forEach(sb::append); opens.entrySet().stream() .filter(e -> !e.getValue().isEmpty()) .sorted(Map.Entry.comparingByKey()) .map(e -> format(" opens %s to%n%s;%n", e.getKey().toString(), e.getValue().stream().sorted() .map(mn -> format(" %s", mn)) .collect(Collectors.joining(",\n")))) .forEach(sb::append); // l = newLine(sb,l); uses.stream().sorted() .map(s -> format(" uses %s;%n", s)) .forEach(sb::append); // l = newLine(sb,l); provides.entrySet().stream() .filter(e -> !e.getValue().isEmpty()) .sorted(Map.Entry.comparingByKey()) .map(e -> format(" provides %s with%n%s;%n", e.getKey().toString(), e.getValue().stream().sorted() .map(mn -> format(" %s", mn)) .collect(Collectors.joining(",\n")))) .forEach(sb::append); // if( Character.isWhitespace(sb.charAt(sb.length()-1)) ) sb.deleteCharAt(sb.length()-1); return sb.toString(); } private int newLine(StringBuilder sb, int length) { if(sb.length() > length) { sb.append("\n"); return sb.length() + 1; } return length; } /** * Modules flags */ public enum Modifier { ACC_NONE(0x0000, "", ""), ACC_OPEN(0x0020, "open", "ACC_OPEN"), ACC_TRANSITIVE(0x0020, "transitive", "ACC_TRANSITIVE"), ACC_STATIC_PHASE(0x0040, "static", "ACC_STATIC_PHASE"), ACC_SYNTHETIC(0x1000, "", "ACC_SYNTHETIC"), ACC_MANDATED(0x8000, "", "ACC_MANDATED"); private final int value; private final String keyword; private final String flag; Modifier(int value, String keyword, String flagName) { this.value = value; this.keyword = keyword; this.flag = flagName; } public int asInt() { return value; } public static String getModuleModifiers(int flag) { return asString(flag, false, ACC_TRANSITIVE); } public static String getModuleFlags(int flag) { return asString(flag, true, ACC_TRANSITIVE); } public static String getStatementModifiers(int flag) { return asString(flag, false, ACC_OPEN); } public static String getStatementFlags(int flag) { return asString(flag, true, ACC_OPEN); } private static String asString(int value, boolean flagFormat, Modifier skipped ) { String buf = ""; for(Module.Modifier m : values()) { if( m != skipped && (value & m.value) != 0) { buf += ((flagFormat) ? m.flag : m.keyword) + " "; value ^= m.value; } } if( flagFormat && value != 0 ) buf += String.format("0x%04X ", value); return buf; } } // A module header consists of a module name and module flags public final static class Header extends VersionedFlaggedTargetType{ Header(String typeName, int flag) { this(typeName, flag, null); } Header(String typeName, int flag, String moduleVersion) { super(typeName, flag, moduleVersion); } public String getModuleName() { return getTypeName(); } public int getModuleFlags() { return getFlags(); } public String getModuleVersion() { return getVersion(); } } //* A module on which the current module has a dependence. public final static class Dependence extends VersionedFlaggedTargetType { public Dependence(String moduleName, int flag) {this(moduleName, flag, null);} public Dependence(String moduleName, int flag, String moduleVersion) {super(moduleName, flag, moduleVersion);} public Dependence(String moduleName, boolean transitive, boolean staticPhase) { this(moduleName,transitive,staticPhase,null);} public Dependence(String moduleName, boolean transitive, boolean staticPhase, String moduleVersion) { this(moduleName, (transitive ? Modifier.ACC_TRANSITIVE.value : Modifier.ACC_NONE.value) | (staticPhase ? Modifier.ACC_STATIC_PHASE.value : Modifier.ACC_NONE.value), moduleVersion); } public String getModuleVersion() { return getVersion(); } } public final static class Uses extends TargetType { public Uses(String typeName) { super(typeName); } } //* A provided type of the current module. public final static class Provided extends TargetType { public Provided(String typeName) { super(typeName); } } //* An opened package of the current module. public final static class Opened extends FlaggedTargetType { public Opened(String typeName) { super(typeName, 0); } public Opened(String typeName, int opensFlags) { super(typeName, opensFlags); } } //* An exported package of the current module. public final static class Exported extends FlaggedTargetType { public Exported(String typeName) { super(typeName, 0); } public Exported(String typeName, int exportsFlags) { super(typeName, exportsFlags); } } public static class VersionedFlaggedTargetType extends FlaggedTargetType { private String version; VersionedFlaggedTargetType(String typeName, int flag) { this(typeName,flag, null); } VersionedFlaggedTargetType(String typeName, int flag, String version) { super(typeName, flag); this.version = version != null && !version.isEmpty() ? version : null; } public String getVersion() { return version; } @Override public int hashCode() { int code = version == null ? 0 : version.hashCode(); return code + super.hashCode(); } } public static class FlaggedTargetType extends TargetType { private int flag; FlaggedTargetType(String typeName, int flag) { super(typeName); this.flag = flag; } public boolean isFlagged() { return true; } public int getFlags() { return flag; } public void setFlag(int value) { flag = value; } @Override public int hashCode() { return super.hashCode() + flag; } @Override public boolean equals(Object o) { return super.equals(o) && ((FlaggedTargetType) o).flag == this.flag; } @Override public String toString() { return Modifier.getStatementModifiers(this.flag)+ super.toString(); } } public static class TargetType implements Comparable { private String typeName; TargetType(String typeName) { this.typeName = typeName; } public String getTypeName() { return typeName; } public void setTypeName(String value) { typeName = value; } public boolean isFlagged() { return false; } @Override public int hashCode() { return typeName.hashCode() * 11; } @Override public boolean equals(Object o) { if (o instanceof TargetType) { TargetType t = (TargetType) o; return this.typeName.equals(t.getTypeName()); } return false; } @Override public int compareTo(TargetType t) { return this.typeName.compareTo(t.getTypeName()); } @Override public String toString() { return typeName; } } /** * The module builder. */ public static final class Builder { final Header header; final Set requires = new HashSet<>(); final Map> exports = new HashMap<>(); final Map> opens = new HashMap<>(); final Set uses = new HashSet<>(); final Map> provides = new HashMap<>(); public Builder() { this("", Modifier.ACC_NONE.asInt(), null); } public Builder(String moduleName, int moduleFlags, String moduleVersion) { header = new Header( moduleName,moduleFlags, moduleVersion); } public Builder setModuleFlags(int moduleFlags) { header.setFlag(header.getFlags() | moduleFlags); return this; } public Builder setModuleFlags(Modifier... moduleFlags) { for (Modifier m : moduleFlags) setModuleFlags(m.value); return this; } public Builder setModuleName(String value) { header.setTypeName(value); return this; } public Builder require(String d, boolean transitive, boolean staticPhase, String version) { requires.add(new Dependence(d, transitive, staticPhase, version)); return this; } public Builder require(String d, int requiresFlag, String version) { requires.add(new Dependence(d, requiresFlag, version)); return this; } public Builder require(String d, int requiresFlag) { requires.add(new Dependence(d, requiresFlag, null)); return this; } public Builder opens(Opened p, Set ms) { return add(opens, p, ms); } public Builder opens(String packageName, int exportFlags, Set ms) { return add(opens, new Opened(packageName, exportFlags), ms); } public Builder opens(String packageName, int exportFlags) { return add(opens, new Opened(packageName, exportFlags), new HashSet<>()); } public Builder exports(Exported p, Set ms) { return add(exports, p, ms); } public Builder exports(String packageName, int exportFlags, Set ms) { return add(exports, new Exported(packageName, exportFlags), ms); } public Builder exports(String packageName, int exportFlags) { return add(exports, new Exported(packageName, exportFlags), new HashSet<>()); } public Builder uses(String serviceName) { uses.add(new Uses(serviceName)); return this; } public Builder uses(Set serviceNames) { uses.addAll(serviceNames.stream().map(Uses::new).collect(Collectors.toList())); return this; } public Builder provides(Provided t, Set implementations) { return add(provides, t, implementations); } public Builder provides(String serviceName, Set implementations) { return add(provides, new Provided(serviceName), implementations); } /** * @return The new module */ public Module build() { return new Module(this); } private Builder add( Map> collection, T source, Set target) { Objects.requireNonNull(source); Objects.requireNonNull(target); if (!collection.containsKey(source)) collection.put(source, new HashSet<>()); collection.get(source).addAll(target); return this; } } }