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 package com.sun.tools.jdeps; 26 27 import java.util.Collections; 28 import java.util.HashMap; 29 import java.util.HashSet; 30 import java.util.Map; 31 import java.util.Objects; 32 import java.util.Set; 33 34 /** 35 * JDeps internal representation of module for dependency analysis. 36 */ 37 final class Module extends Archive { 38 private final String moduleName; 39 private final Map<String, Boolean> requires; 40 private final Map<String, Set<String>> exports; 41 private final Set<String> packages; 42 43 private Module(ClassFileReader reader, String name, 44 Map<String, Boolean> requires, 45 Map<String, Set<String>> exports, 46 Set<String> packages) { 47 super(name, reader); 48 this.moduleName = name; 49 this.requires = Collections.unmodifiableMap(requires); 50 this.exports = Collections.unmodifiableMap(exports); 51 this.packages = Collections.unmodifiableSet(packages); 52 } 53 54 public String name() { 55 return moduleName; 56 } 57 58 public Map<String, Boolean> requires() { 59 return requires; 60 } 61 62 public Map<String, Set<String>> exports() { 63 return exports; 64 } 65 66 public Set<String> packages() { 67 return packages; 68 } 69 70 /** 71 * Tests if this module can read m 72 */ 73 public boolean canRead(Module m) { 74 // ## TODO: handle "re-exported=true" 75 // all JDK modules require all modules containing its direct dependences 76 // should not be an issue 77 return requires.containsKey(m.name()); 78 } 79 80 /** 81 * Tests if a given fully-qualified name is an exported type. 82 */ 83 public boolean isExported(String cn) { 84 int i = cn.lastIndexOf('.'); 85 String pn = i > 0 ? cn.substring(0, i) : ""; 86 87 return isExportedPackage(pn); 88 } 89 90 /** 91 * Tests if a given package name is exported. 92 */ 93 public boolean isExportedPackage(String pn) { 94 return exports.containsKey(pn) ? exports.get(pn).isEmpty() : false; 95 } 96 97 /** 98 * Tests if the given classname is accessible to module m 99 */ 100 public boolean isAccessibleTo(String classname, Module m) { 101 int i = classname.lastIndexOf('.'); 102 String pn = i > 0 ? classname.substring(0, i) : ""; 103 if (!packages.contains(pn)) { 104 throw new IllegalArgumentException(classname + " is not a member of module " + name()); 105 } 106 107 if (m != null && !m.canRead(this)) { 108 trace("%s not readable by %s%n", this.name(), m.name()); 109 return false; 110 } 111 112 // exported API 113 Set<String> ms = exports().get(pn); 114 String mname = m != null ? m.name() : "unnamed"; 115 if (ms == null) { 116 trace("%s not exported in %s%n", classname, this.name()); 117 } else if (!(ms.isEmpty() || ms.contains(mname))) { 118 trace("%s not permit to %s %s%n", classname, mname, ms); 119 } 120 return ms != null && (ms.isEmpty() || ms.contains(mname)); 121 } 122 123 private static final boolean traceOn = Boolean.getBoolean("jdeps.debug"); 124 private void trace(String fmt, Object... args) { 125 if (traceOn) { 126 System.err.format(fmt, args); 127 } 128 } 129 130 @Override 131 public boolean equals(Object ob) { 132 if (!(ob instanceof Module)) 133 return false; 134 Module that = (Module)ob; 135 return (moduleName.equals(that.moduleName) 136 && requires.equals(that.requires) 137 && exports.equals(that.exports) 138 && packages.equals(that.packages)); 139 } 140 141 @Override 142 public int hashCode() { 143 int hc = moduleName.hashCode(); 144 hc = hc * 43 + requires.hashCode(); 145 hc = hc * 43 + exports.hashCode(); 146 hc = hc * 43 + packages.hashCode(); 147 return hc; 148 } 149 150 @Override 151 public String toString() { 152 return name(); 153 } 154 155 public final static class Builder { 156 String name; 157 ClassFileReader reader; 158 final Map<String, Boolean> requires = new HashMap<>(); 159 final Map<String, Set<String>> exports = new HashMap<>(); 160 final Set<String> packages = new HashSet<>(); 161 162 public Builder() { 163 } 164 165 public Builder name(String n) { 166 name = n; 167 return this; 168 } 169 170 public Builder require(String d, boolean reexport) { 171 // System.err.format("%s depend %s reexports %s%n", name, d, reexport); 172 requires.put(d, reexport); 173 return this; 174 } 175 176 public Builder packages(Set<String> pkgs) { 177 packages.addAll(pkgs); 178 return this; 179 } 180 181 public Builder export(String p, Set<String> ms) { 182 Objects.requireNonNull(p); 183 Objects.requireNonNull(ms); 184 exports.put(p, new HashSet<>(ms)); 185 return this; 186 } 187 public Builder classes(ClassFileReader.ModuleClassReader reader) { 188 this.reader = reader; 189 return this; 190 } 191 192 public Module build() { 193 Module m = new Module(reader, name, requires, exports, packages); 194 return m; 195 } 196 } 197 }