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.net.URI; 30 import java.util.Collections; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.Map; 34 import java.util.Set; 35 36 /** 37 * Jdeps internal representation of module for dependency analysis. 38 */ 39 class Module extends Archive { 40 static final Module UNNAMED_MODULE = new UnnamedModule(); 41 static final String JDK_UNSUPPORTED = "jdk.unsupported"; 42 43 static final boolean DEBUG = Boolean.getBoolean("jdeps.debug"); 44 static void trace(String fmt, Object... args) { 45 trace(DEBUG, fmt, args); 46 } 47 48 static void trace(boolean traceOn, String fmt, Object... args) { 49 if (traceOn) { 50 System.err.format(fmt, args); 51 } 52 } 53 54 private final ModuleDescriptor descriptor; 55 private final Map<String, Set<String>> exports; 56 private final Map<String, Set<String>> opens; 57 private final boolean isSystem; 58 private final URI location; 59 60 protected Module(String name) { 61 this(name, null, false); 62 } 63 64 protected Module(String name, ModuleDescriptor descriptor, boolean isSystem) { 65 super(name); 66 this.descriptor = descriptor; 67 this.location = null; 68 this.exports = Collections.emptyMap(); 69 this.opens = Collections.emptyMap(); 70 this.isSystem = isSystem; 71 } 72 73 private Module(String name, 74 URI location, 75 ModuleDescriptor descriptor, 76 Map<String, Set<String>> exports, 77 Map<String, Set<String>> opens, 78 boolean isSystem, 79 ClassFileReader reader) { 80 super(name, location, reader); 81 this.descriptor = descriptor; 82 this.location = location; 83 this.exports = Collections.unmodifiableMap(exports); 84 this.opens = Collections.unmodifiableMap(opens); 85 this.isSystem = isSystem; 86 } 87 88 /** 89 * Returns module name 90 */ 91 public String name() { 92 return descriptor != null ? descriptor.name() : getName(); 93 } 94 95 public boolean isNamed() { 96 return descriptor != null; 97 } 98 99 public boolean isAutomatic() { 100 return descriptor != null && descriptor.isAutomatic(); 101 } 102 103 public Module getModule() { 104 return this; 105 } 106 107 public ModuleDescriptor descriptor() { 108 return descriptor; 109 } 110 111 public URI location() { 112 return location; 113 } 114 115 public boolean isJDK() { 116 String mn = name(); 117 return isSystem && 118 (mn.startsWith("java.") || mn.startsWith("jdk.")); 119 } 120 121 public boolean isSystem() { 122 return isSystem; 123 } 124 125 public Map<String, Set<String>> exports() { 126 return exports; 127 } 128 129 public Set<String> packages() { 130 return descriptor.packages(); 131 } 132 133 public boolean isJDKUnsupported() { 134 return JDK_UNSUPPORTED.equals(this.name()); 135 } 136 137 /** 138 * Converts this module to a normal module with the given dependences 139 * 140 * @throws IllegalArgumentException if this module is not an automatic module 141 */ 142 public Module toNormalModule(Map<String, Boolean> requires) { 143 if (!isAutomatic()) { 144 throw new IllegalArgumentException(name() + " not an automatic module"); 145 } 146 return new NormalModule(this, requires); 147 } 148 149 /** 150 * Tests if the package of the given name is exported. 151 */ 152 public boolean isExported(String pn) { 153 return exports.containsKey(pn) && exports.get(pn).isEmpty(); 154 } 155 156 /** 157 * Tests if the package of the given name is exported to the target 158 * in a qualified fashion. 159 */ 160 public boolean isExported(String pn, String target) { 161 return isExported(pn) 162 || exports.containsKey(pn) && exports.get(pn).contains(target); 163 } 164 165 /** 166 * Tests if the package of the given name is open. 167 */ 168 public boolean isOpen(String pn) { 169 return opens.containsKey(pn) && opens.get(pn).isEmpty(); 170 } 171 172 /** 173 * Tests if the package of the given name is open to the target 174 * in a qualified fashion. 175 */ 176 public boolean isOpen(String pn, String target) { 177 return isOpen(pn) 178 || opens.containsKey(pn) && opens.get(pn).contains(target); 179 } 180 181 @Override 182 public String toString() { 183 return name(); 184 } 185 186 public final static class Builder { 187 final String name; 188 final ModuleDescriptor descriptor; 189 final boolean isSystem; 190 ClassFileReader reader; 191 URI location; 192 193 public Builder(ModuleDescriptor md) { 194 this(md, false); 195 } 196 197 public Builder(ModuleDescriptor md, boolean isSystem) { 198 this.name = md.name(); 199 this.descriptor = md; 200 this.isSystem = isSystem; 201 } 202 203 public Builder location(URI location) { 204 this.location = location; 205 return this; 206 } 207 208 public Builder classes(ClassFileReader reader) { 209 this.reader = reader; 210 return this; 211 } 212 213 public Module build() { 214 if (descriptor.isAutomatic() && isSystem) { 215 throw new InternalError("JDK module: " + name + " can't be automatic module"); 216 } 217 218 Map<String, Set<String>> exports = new HashMap<>(); 219 Map<String, Set<String>> opens = new HashMap<>(); 220 221 if (descriptor.isAutomatic()) { 222 // ModuleDescriptor::exports and opens returns an empty set 223 descriptor.packages().forEach(pn -> exports.put(pn, Collections.emptySet())); 224 descriptor.packages().forEach(pn -> opens.put(pn, Collections.emptySet())); 225 } else { 226 descriptor.exports().stream() 227 .forEach(exp -> exports.computeIfAbsent(exp.source(), _k -> new HashSet<>()) 228 .addAll(exp.targets())); 229 descriptor.opens().stream() 230 .forEach(exp -> opens.computeIfAbsent(exp.source(), _k -> new HashSet<>()) 231 .addAll(exp.targets())); 232 } 233 return new Module(name, location, descriptor, exports, opens, isSystem, reader); 234 } 235 } 236 237 private static class UnnamedModule extends Module { 238 private UnnamedModule() { 239 super("unnamed", null, false); 240 } 241 242 @Override 243 public String name() { 244 return "unnamed"; 245 } 246 247 @Override 248 public boolean isExported(String pn) { 249 return true; 250 } 251 } 252 253 /** 254 * A normal module has a module-info.class 255 */ 256 private static class NormalModule extends Module { 257 private final ModuleDescriptor md; 258 259 /** 260 * Converts the given automatic module to a normal module. 261 * 262 * Replace this module's dependences with the given requires and also 263 * declare service providers, if specified in META-INF/services configuration file 264 */ 265 private NormalModule(Module m, Map<String, Boolean> requires) { 266 super(m.name(), m.location, m.descriptor, m.exports, m.opens, m.isSystem, m.reader()); 267 268 ModuleDescriptor.Builder builder = ModuleDescriptor.newModule(m.name()); 269 requires.keySet().forEach(mn -> { 270 if (requires.get(mn).equals(Boolean.TRUE)) { 271 builder.requires(Set.of(ModuleDescriptor.Requires.Modifier.TRANSITIVE), mn); 272 } else { 273 builder.requires(mn); 274 } 275 }); 276 // exports all packages 277 m.descriptor.packages().forEach(builder::exports); 278 m.descriptor.uses().forEach(builder::uses); 279 m.descriptor.provides().forEach(builder::provides); 280 this.md = builder.build(); 281 } 282 283 @Override 284 public ModuleDescriptor descriptor() { 285 return md; 286 } 287 } 288 }