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