1 /* 2 * Copyright (c) 2014, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.jdeps; 27 28 import java.lang.module.ModuleDescriptor; 29 import java.lang.module.ModuleDescriptor.Exports; 30 import java.lang.module.ModuleDescriptor.Opens; 31 import java.net.URI; 32 import java.util.Collections; 33 import java.util.HashMap; 34 import java.util.HashSet; 35 import java.util.Map; 36 import java.util.Set; 37 38 /** 39 * Jdeps internal representation of module for dependency analysis. 40 */ 41 class Module extends Archive { 42 static final Module UNNAMED_MODULE = new UnnamedModule(); 43 static final String JDK_UNSUPPORTED = "jdk.unsupported"; 44 45 static final boolean DEBUG = Boolean.getBoolean("jdeps.debug"); 46 static void trace(String fmt, Object... args) { 47 trace(DEBUG, fmt, args); 48 } 49 50 static void trace(boolean traceOn, String fmt, Object... args) { 51 if (traceOn) { 52 System.err.format(fmt, args); 53 } 54 } 55 56 private final ModuleDescriptor descriptor; 57 private final Map<String, Set<String>> exports; 58 private final boolean isSystem; 59 private final URI location; 60 61 protected Module(String name) { 62 super(name); 63 this.descriptor = null; 64 this.location = null; 65 this.exports = Collections.emptyMap(); 66 this.isSystem = true; 67 } 68 69 private Module(String name, 70 URI location, 71 ModuleDescriptor descriptor, 72 Map<String, Set<String>> exports, 73 boolean isSystem, 74 ClassFileReader reader) { 75 super(name, location, reader); 76 this.descriptor = descriptor; 77 this.location = location; 78 this.exports = Collections.unmodifiableMap(exports); 79 this.isSystem = isSystem; 80 } 81 82 /** 83 * Returns module name 84 */ 85 public String name() { 86 return descriptor != null ? descriptor.name() : getName(); 87 } 88 89 public boolean isNamed() { 90 return true; 91 } 92 93 public boolean isAutomatic() { 94 return descriptor.isAutomatic(); 95 } 96 97 public Module getModule() { 98 return this; 99 } 100 101 public ModuleDescriptor descriptor() { 102 return descriptor; 103 } 104 105 public URI location() { 106 return location; 107 } 108 109 public boolean isJDK() { 110 String mn = name(); 111 return isSystem && 112 (mn.startsWith("java.") || mn.startsWith("jdk.") || mn.startsWith("javafx.")); 113 } 114 115 public boolean isSystem() { 116 return isSystem; 117 } 118 119 public Map<String, Set<String>> exports() { 120 return exports; 121 } 122 123 public Set<String> packages() { 124 return descriptor.packages(); 125 } 126 127 /** 128 * Tests if the package of the given name is exported. 129 */ 130 public boolean isExported(String pn) { 131 return exports.containsKey(pn) ? exports.get(pn).isEmpty() : false; 132 } 133 134 public boolean isJDKUnsupported() { 135 return JDK_UNSUPPORTED.equals(this.name()); 136 } 137 138 /** 139 * Converts this module to a strict module with the given dependences 140 * 141 * @throws IllegalArgumentException if this module is not an automatic module 142 */ 143 public Module toStrictModule(Map<String, Boolean> requires) { 144 if (!isAutomatic()) { 145 throw new IllegalArgumentException(name() + " already a strict module"); 146 } 147 return new StrictModule(this, requires); 148 } 149 150 /** 151 * Tests if the package of the given name is qualifiedly exported 152 * to the target. 153 */ 154 public boolean isExported(String pn, String target) { 155 return isExported(pn) || exports.containsKey(pn) && exports.get(pn).contains(target); 156 } 157 158 @Override 159 public String toString() { 160 return name(); 161 } 162 163 public final static class Builder { 164 final String name; 165 final ModuleDescriptor descriptor; 166 final boolean isSystem; 167 ClassFileReader reader; 168 URI location; 169 170 public Builder(ModuleDescriptor md) { 171 this(md, false); 172 } 173 174 public Builder(ModuleDescriptor md, boolean isSystem) { 175 this.name = md.name(); 176 this.descriptor = md; 177 this.isSystem = isSystem; 178 } 179 180 public Builder location(URI location) { 181 this.location = location; 182 return this; 183 } 184 185 public Builder classes(ClassFileReader reader) { 186 this.reader = reader; 187 return this; 188 } 189 190 public Module build() { 191 if (descriptor.isAutomatic() && isSystem) { 192 throw new InternalError("JDK module: " + name + " can't be automatic module"); 193 } 194 195 Map<String, Set<String>> exports = new HashMap<>(); 196 197 descriptor.exports().stream() 198 .forEach(exp -> exports.computeIfAbsent(exp.source(), _k -> new HashSet<>()) 199 .addAll(exp.targets())); 200 201 return new Module(name, location, descriptor, exports, isSystem, reader); 202 } 203 } 204 205 private static class UnnamedModule extends Module { 206 private UnnamedModule() { 207 super("unnamed", null, null, 208 Collections.emptyMap(), 209 false, null); 210 } 211 212 @Override 213 public String name() { 214 return "unnamed"; 215 } 216 217 @Override 218 public boolean isNamed() { 219 return false; 220 } 221 222 @Override 223 public boolean isAutomatic() { 224 return false; 225 } 226 227 @Override 228 public boolean isExported(String pn) { 229 return true; 230 } 231 } 232 233 private static class StrictModule extends Module { 234 private final ModuleDescriptor md; 235 236 /** 237 * Converts the given automatic module to a strict module. 238 * 239 * Replace this module's dependences with the given requires and also 240 * declare service providers, if specified in META-INF/services configuration file 241 */ 242 private StrictModule(Module m, Map<String, Boolean> requires) { 243 super(m.name(), m.location, m.descriptor, m.exports, m.isSystem, m.reader()); 244 245 ModuleDescriptor.Builder builder = ModuleDescriptor.module(m.name()); 246 requires.keySet().forEach(mn -> { 247 if (requires.get(mn).equals(Boolean.TRUE)) { 248 builder.requires(Set.of(ModuleDescriptor.Requires.Modifier.TRANSITIVE), mn); 249 } else { 250 builder.requires(mn); 251 } 252 }); 253 m.descriptor.exports().forEach(e -> builder.exports(e)); 254 m.descriptor.opens().forEach(o -> builder.opens(o)); 255 m.descriptor.uses().forEach(s -> builder.uses(s)); 256 m.descriptor.provides().forEach(p -> builder.provides(p)); 257 258 Set<String> concealed = new HashSet<>(m.descriptor.packages()); 259 m.descriptor.exports().stream().map(Exports::source).forEach(concealed::remove); 260 m.descriptor.opens().stream().map(Opens::source).forEach(concealed::remove); 261 concealed.forEach(builder::contains); 262 263 this.md = builder.build(); 264 } 265 266 @Override 267 public ModuleDescriptor descriptor() { 268 return md; 269 } 270 } 271 }