1.1 --- a/src/share/classes/com/sun/tools/jdeps/Analyzer.java Mon Oct 28 12:29:34 2013 -0700 1.2 +++ b/src/share/classes/com/sun/tools/jdeps/Analyzer.java Wed Oct 30 08:35:52 2013 -0700 1.3 @@ -26,9 +26,11 @@ 1.4 1.5 import com.sun.tools.classfile.Dependency.Location; 1.6 import com.sun.tools.jdeps.PlatformClassPath.JDKArchive; 1.7 +import java.util.ArrayList; 1.8 import java.util.HashMap; 1.9 import java.util.List; 1.10 import java.util.Map; 1.11 +import java.util.Objects; 1.12 import java.util.Set; 1.13 import java.util.SortedMap; 1.14 import java.util.SortedSet; 1.15 @@ -52,7 +54,7 @@ 1.16 1.17 private final Type type; 1.18 private final Map<Archive, ArchiveDeps> results = new HashMap<>(); 1.19 - private final Map<String, Archive> map = new HashMap<>(); 1.20 + private final Map<Location, Archive> map = new HashMap<>(); 1.21 private final Archive NOT_FOUND 1.22 = new Archive(JdepsTask.getMessage("artifact.not.found")); 1.23 1.24 @@ -69,6 +71,17 @@ 1.25 * Performs the dependency analysis on the given archives. 1.26 */ 1.27 public void run(List<Archive> archives) { 1.28 + // build a map from Location to Archive 1.29 + for (Archive archive: archives) { 1.30 + for (Location l: archive.getClasses()) { 1.31 + if (!map.containsKey(l)) { 1.32 + map.put(l, archive); 1.33 + } else { 1.34 + // duplicated class warning? 1.35 + } 1.36 + } 1.37 + } 1.38 + // traverse and analyze all dependencies 1.39 for (Archive archive : archives) { 1.40 ArchiveDeps deps; 1.41 if (type == Type.CLASS || type == Type.VERBOSE) { 1.42 @@ -76,33 +89,9 @@ 1.43 } else { 1.44 deps = new PackageVisitor(archive); 1.45 } 1.46 - archive.visit(deps); 1.47 + archive.visitDependences(deps); 1.48 results.put(archive, deps); 1.49 } 1.50 - 1.51 - // set the required dependencies 1.52 - for (ArchiveDeps result: results.values()) { 1.53 - for (Set<String> set : result.deps.values()) { 1.54 - for (String target : set) { 1.55 - Archive source = getArchive(target); 1.56 - if (result.archive != source) { 1.57 - String profile = ""; 1.58 - if (source instanceof JDKArchive) { 1.59 - profile = result.profile != null ? result.profile.toString() : ""; 1.60 - if (result.getTargetProfile(target) == null) { 1.61 - profile += ", JDK internal API"; 1.62 - // override the value if it accesses any JDK internal 1.63 - result.requireArchives.put(source, profile); 1.64 - continue; 1.65 - } 1.66 - } 1.67 - if (!result.requireArchives.containsKey(source)) { 1.68 - result.requireArchives.put(source, profile); 1.69 - } 1.70 - } 1.71 - } 1.72 - } 1.73 - } 1.74 } 1.75 1.76 public boolean hasDependences(Archive archive) { 1.77 @@ -117,94 +106,143 @@ 1.78 * Visits the source archive to its destination archive of 1.79 * a recorded dependency. 1.80 */ 1.81 - void visitArchiveDependence(Archive origin, Archive target, String profile); 1.82 + void visitArchiveDependence(Archive origin, Archive target, Profile profile); 1.83 /** 1.84 * Visits a recorded dependency from origin to target which can be 1.85 * a fully-qualified classname, a package name, a profile or 1.86 * archive name depending on the Analyzer's type. 1.87 */ 1.88 - void visitDependence(String origin, Archive source, String target, Archive archive, String profile); 1.89 + void visitDependence(String origin, Archive source, String target, Archive archive, Profile profile); 1.90 } 1.91 1.92 public void visitArchiveDependences(Archive source, Visitor v) { 1.93 ArchiveDeps r = results.get(source); 1.94 - for (Map.Entry<Archive,String> e : r.requireArchives.entrySet()) { 1.95 - v.visitArchiveDependence(r.archive, e.getKey(), e.getValue()); 1.96 + for (ArchiveDeps.Dep d: r.requireArchives()) { 1.97 + v.visitArchiveDependence(r.archive, d.archive, d.profile); 1.98 } 1.99 } 1.100 1.101 public void visitDependences(Archive source, Visitor v) { 1.102 ArchiveDeps r = results.get(source); 1.103 - for (String origin : r.deps.keySet()) { 1.104 - for (String target : r.deps.get(origin)) { 1.105 - Archive archive = getArchive(target); 1.106 - assert source == getArchive(origin); 1.107 - Profile profile = r.getTargetProfile(target); 1.108 - 1.109 + for (Map.Entry<String, SortedSet<ArchiveDeps.Dep>> e: r.deps.entrySet()) { 1.110 + String origin = e.getKey(); 1.111 + for (ArchiveDeps.Dep d: e.getValue()) { 1.112 // filter intra-dependency unless in verbose mode 1.113 - if (type == Type.VERBOSE || archive != source) { 1.114 - v.visitDependence(origin, source, target, archive, 1.115 - profile != null ? profile.toString() : ""); 1.116 + if (type == Type.VERBOSE || d.archive != source) { 1.117 + v.visitDependence(origin, source, d.target, d.archive, d.profile); 1.118 } 1.119 } 1.120 } 1.121 } 1.122 1.123 - public Archive getArchive(String name) { 1.124 - return map.containsKey(name) ? map.get(name) : NOT_FOUND; 1.125 - } 1.126 - 1.127 + /** 1.128 + * ArchiveDeps contains the dependencies for an Archive that 1.129 + * can have one or more classes. 1.130 + */ 1.131 private abstract class ArchiveDeps implements Archive.Visitor { 1.132 final Archive archive; 1.133 - final Map<Archive,String> requireArchives; 1.134 - final SortedMap<String, SortedSet<String>> deps; 1.135 - Profile profile = null; 1.136 + final SortedMap<String, SortedSet<Dep>> deps; 1.137 ArchiveDeps(Archive archive) { 1.138 this.archive = archive; 1.139 - this.requireArchives = new HashMap<>(); 1.140 this.deps = new TreeMap<>(); 1.141 } 1.142 1.143 - void add(String loc) { 1.144 - Archive a = map.get(loc); 1.145 - if (a == null) { 1.146 - map.put(loc, archive); 1.147 - } else if (a != archive) { 1.148 - // duplicated class warning? 1.149 - } 1.150 - } 1.151 - 1.152 - void add(String origin, String target) { 1.153 - SortedSet<String> set = deps.get(origin); 1.154 + void add(String origin, String target, Archive targetArchive, String pkgName) { 1.155 + SortedSet<Dep> set = deps.get(origin); 1.156 if (set == null) { 1.157 deps.put(origin, set = new TreeSet<>()); 1.158 } 1.159 - if (!set.contains(target)) { 1.160 - set.add(target); 1.161 - // find the corresponding profile 1.162 - Profile p = getTargetProfile(target); 1.163 - if (profile == null || (p != null && profile.profile < p.profile)) { 1.164 - profile = p; 1.165 + Profile p = targetArchive instanceof JDKArchive 1.166 + ? Profile.getProfile(pkgName) : null; 1.167 + set.add(new Dep(target, targetArchive, p)); 1.168 + } 1.169 + 1.170 + /** 1.171 + * Returns the list of Archive dependences. The returned 1.172 + * list contains one {@code Dep} instance per one archive 1.173 + * and with the minimum profile this archive depends on. 1.174 + */ 1.175 + List<Dep> requireArchives() { 1.176 + Map<Archive,Profile> map = new HashMap<>(); 1.177 + for (Set<Dep> set: deps.values()) { 1.178 + for (Dep d: set) { 1.179 + if (this.archive != d.archive) { 1.180 + Profile p = map.get(d.archive); 1.181 + if (p == null || (d.profile != null && p.profile < d.profile.profile)) { 1.182 + map.put(d.archive, d.profile); 1.183 + } 1.184 + } 1.185 } 1.186 } 1.187 + List<Dep> list = new ArrayList<>(); 1.188 + for (Map.Entry<Archive,Profile> e: map.entrySet()) { 1.189 + list.add(new Dep("", e.getKey(), e.getValue())); 1.190 + } 1.191 + return list; 1.192 + } 1.193 + 1.194 + /** 1.195 + * Dep represents a dependence where the target can be 1.196 + * a classname or packagename and the archive and profile 1.197 + * the target belongs to. 1.198 + */ 1.199 + class Dep implements Comparable<Dep> { 1.200 + final String target; 1.201 + final Archive archive; 1.202 + final Profile profile; 1.203 + Dep(String target, Archive archive, Profile p) { 1.204 + this.target = target; 1.205 + this.archive = archive; 1.206 + this.profile = p; 1.207 + } 1.208 + 1.209 + @Override 1.210 + public boolean equals(Object o) { 1.211 + if (o instanceof Dep) { 1.212 + Dep d = (Dep)o; 1.213 + return this.archive == d.archive && this.target.equals(d.target); 1.214 + } 1.215 + return false; 1.216 + } 1.217 + 1.218 + @Override 1.219 + public int hashCode() { 1.220 + int hash = 3; 1.221 + hash = 17 * hash + Objects.hashCode(this.archive); 1.222 + hash = 17 * hash + Objects.hashCode(this.target); 1.223 + return hash; 1.224 + } 1.225 + 1.226 + @Override 1.227 + public int compareTo(Dep o) { 1.228 + if (this.target.equals(o.target)) { 1.229 + if (this.archive == o.archive) { 1.230 + return 0; 1.231 + } else { 1.232 + return this.archive.getFileName().compareTo(o.archive.getFileName()); 1.233 + } 1.234 + } 1.235 + return this.target.compareTo(o.target); 1.236 + } 1.237 } 1.238 public abstract void visit(Location o, Location t); 1.239 - public abstract Profile getTargetProfile(String target); 1.240 } 1.241 1.242 private class ClassVisitor extends ArchiveDeps { 1.243 ClassVisitor(Archive archive) { 1.244 super(archive); 1.245 } 1.246 - public void visit(Location l) { 1.247 - add(l.getClassName()); 1.248 - } 1.249 + @Override 1.250 public void visit(Location o, Location t) { 1.251 - add(o.getClassName(), t.getClassName()); 1.252 - } 1.253 - public Profile getTargetProfile(String target) { 1.254 - int i = target.lastIndexOf('.'); 1.255 - return (i > 0) ? Profile.getProfile(target.substring(0, i)) : null; 1.256 + Archive targetArchive = 1.257 + this.archive.getClasses().contains(t) ? this.archive : map.get(t); 1.258 + if (targetArchive == null) { 1.259 + map.put(t, targetArchive = NOT_FOUND); 1.260 + } 1.261 + 1.262 + String origin = o.getClassName(); 1.263 + String target = t.getClassName(); 1.264 + add(origin, target, targetArchive, t.getPackageName()); 1.265 } 1.266 } 1.267 1.268 @@ -212,18 +250,21 @@ 1.269 PackageVisitor(Archive archive) { 1.270 super(archive); 1.271 } 1.272 + @Override 1.273 public void visit(Location o, Location t) { 1.274 - add(packageOf(o), packageOf(t)); 1.275 + Archive targetArchive = 1.276 + this.archive.getClasses().contains(t) ? this.archive : map.get(t); 1.277 + if (targetArchive == null) { 1.278 + map.put(t, targetArchive = NOT_FOUND); 1.279 + } 1.280 + 1.281 + String origin = packageOf(o); 1.282 + String target = packageOf(t); 1.283 + add(origin, target, targetArchive, t.getPackageName()); 1.284 } 1.285 - public void visit(Location l) { 1.286 - add(packageOf(l)); 1.287 - } 1.288 - private String packageOf(Location loc) { 1.289 - String pkg = loc.getPackageName(); 1.290 + public String packageOf(Location o) { 1.291 + String pkg = o.getPackageName(); 1.292 return pkg.isEmpty() ? "<unnamed>" : pkg; 1.293 } 1.294 - public Profile getTargetProfile(String target) { 1.295 - return Profile.getProfile(target); 1.296 - } 1.297 } 1.298 }