Wed, 30 Oct 2013 08:35:52 -0700
8027481: jdeps to handle classes with the same package name and correct profile for javax.crypto.*
Reviewed-by: alanb, dfuchs
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 }
2.1 --- a/src/share/classes/com/sun/tools/jdeps/Archive.java Mon Oct 28 12:29:34 2013 -0700 2.2 +++ b/src/share/classes/com/sun/tools/jdeps/Archive.java Wed Oct 30 08:35:52 2013 -0700 2.3 @@ -67,6 +67,7 @@ 2.4 deps.put(origin, set); 2.5 } 2.6 } 2.7 + 2.8 public void addClass(Location origin, Location target) { 2.9 Set<Location> set = deps.get(origin); 2.10 if (set == null) { 2.11 @@ -76,21 +77,27 @@ 2.12 set.add(target); 2.13 } 2.14 2.15 - public void visit(Visitor v) { 2.16 + public Set<Location> getClasses() { 2.17 + return deps.keySet(); 2.18 + } 2.19 + 2.20 + public void visitDependences(Visitor v) { 2.21 for (Map.Entry<Location,Set<Location>> e: deps.entrySet()) { 2.22 - v.visit(e.getKey()); 2.23 for (Location target : e.getValue()) { 2.24 v.visit(e.getKey(), target); 2.25 } 2.26 } 2.27 } 2.28 2.29 - public String toString() { 2.30 + public String getPathName() { 2.31 return path != null ? path.toString() : filename; 2.32 } 2.33 2.34 + public String toString() { 2.35 + return filename; 2.36 + } 2.37 + 2.38 interface Visitor { 2.39 - void visit(Location loc); 2.40 void visit(Location origin, Location target); 2.41 } 2.42 }
3.1 --- a/src/share/classes/com/sun/tools/jdeps/JdepsTask.java Mon Oct 28 12:29:34 2013 -0700 3.2 +++ b/src/share/classes/com/sun/tools/jdeps/JdepsTask.java Wed Oct 30 08:35:52 2013 -0700 3.3 @@ -190,6 +190,11 @@ 3.4 task.options.fullVersion = true; 3.5 } 3.6 }, 3.7 + new HiddenOption(false, "-showlabel") { 3.8 + void process(JdepsTask task, String opt, String arg) { 3.9 + task.options.showLabel = true; 3.10 + } 3.11 + }, 3.12 new HiddenOption(true, "-depth") { 3.13 void process(JdepsTask task, String opt, String arg) throws BadArgs { 3.14 try { 3.15 @@ -279,12 +284,21 @@ 3.16 3.17 private void generateDotFiles(Path dir, Analyzer analyzer) throws IOException { 3.18 Path summary = dir.resolve("summary.dot"); 3.19 - try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary)); 3.20 - DotFileFormatter formatter = new DotFileFormatter(sw, "summary")) { 3.21 - for (Archive archive : sourceLocations) { 3.22 - analyzer.visitArchiveDependences(archive, formatter); 3.23 + boolean verbose = options.verbose == Analyzer.Type.VERBOSE; 3.24 + DotGraph<?> graph = verbose ? new DotSummaryForPackage() 3.25 + : new DotSummaryForArchive(); 3.26 + for (Archive archive : sourceLocations) { 3.27 + analyzer.visitArchiveDependences(archive, graph); 3.28 + if (verbose || options.showLabel) { 3.29 + // traverse detailed dependences to generate package-level 3.30 + // summary or build labels for edges 3.31 + analyzer.visitDependences(archive, graph); 3.32 } 3.33 } 3.34 + try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary))) { 3.35 + graph.writeTo(sw); 3.36 + } 3.37 + // output individual .dot file for each archive 3.38 if (options.verbose != Analyzer.Type.SUMMARY) { 3.39 for (Archive archive : sourceLocations) { 3.40 if (analyzer.hasDependences(archive)) { 3.41 @@ -365,17 +379,16 @@ 3.42 } 3.43 } 3.44 } 3.45 + sourceLocations.addAll(archives); 3.46 3.47 List<Archive> classpaths = new ArrayList<>(); // for class file lookup 3.48 + classpaths.addAll(getClassPathArchives(options.classpath)); 3.49 if (options.includePattern != null) { 3.50 - archives.addAll(getClassPathArchives(options.classpath)); 3.51 - } else { 3.52 - classpaths.addAll(getClassPathArchives(options.classpath)); 3.53 + archives.addAll(classpaths); 3.54 } 3.55 classpaths.addAll(PlatformClassPath.getArchives()); 3.56 3.57 - // add all archives to the source locations for reporting 3.58 - sourceLocations.addAll(archives); 3.59 + // add all classpath archives to the source locations for reporting 3.60 sourceLocations.addAll(classpaths); 3.61 3.62 // Work queue of names of classfiles to be searched. 3.63 @@ -557,6 +570,7 @@ 3.64 boolean showSummary; 3.65 boolean wildcard; 3.66 boolean apiOnly; 3.67 + boolean showLabel; 3.68 String dotOutputDir; 3.69 String classpath = ""; 3.70 int depth = 1; 3.71 @@ -627,16 +641,34 @@ 3.72 return result; 3.73 } 3.74 3.75 + /** 3.76 + * If the given archive is JDK archive and non-null Profile, 3.77 + * this method returns the profile name only if -profile option is specified; 3.78 + * a null profile indicates it accesses a private JDK API and this method 3.79 + * will return "JDK internal API". 3.80 + * 3.81 + * For non-JDK archives, this method returns the file name of the archive. 3.82 + */ 3.83 + private String getProfileArchiveInfo(Archive source, Profile profile) { 3.84 + if (options.showProfile && profile != null) 3.85 + return profile.toString(); 3.86 + 3.87 + if (source instanceof JDKArchive) { 3.88 + return profile == null ? "JDK internal API (" + source.getFileName() + ")" : ""; 3.89 + } 3.90 + return source.getFileName(); 3.91 + } 3.92 3.93 /** 3.94 - * Returns the file name of the archive for non-JRE class or 3.95 - * internal JRE classes. It returns empty string for SE API. 3.96 + * Returns the profile name or "JDK internal API" for JDK archive; 3.97 + * otherwise empty string. 3.98 */ 3.99 - private static String getArchiveName(Archive source, String profile) { 3.100 - String name = source.getFileName(); 3.101 - if (source instanceof JDKArchive) 3.102 - return profile.isEmpty() ? "JDK internal API (" + name + ")" : ""; 3.103 - return name; 3.104 + private String profileName(Archive archive, Profile profile) { 3.105 + if (archive instanceof JDKArchive) { 3.106 + return Objects.toString(profile, "JDK internal API"); 3.107 + } else { 3.108 + return ""; 3.109 + } 3.110 } 3.111 3.112 class RawOutputFormatter implements Analyzer.Visitor { 3.113 @@ -648,21 +680,18 @@ 3.114 private String pkg = ""; 3.115 @Override 3.116 public void visitDependence(String origin, Archive source, 3.117 - String target, Archive archive, String profile) { 3.118 + String target, Archive archive, Profile profile) { 3.119 if (!origin.equals(pkg)) { 3.120 pkg = origin; 3.121 writer.format(" %s (%s)%n", origin, source.getFileName()); 3.122 } 3.123 - String name = (options.showProfile && !profile.isEmpty()) 3.124 - ? profile 3.125 - : getArchiveName(archive, profile); 3.126 - writer.format(" -> %-50s %s%n", target, name); 3.127 + writer.format(" -> %-50s %s%n", target, getProfileArchiveInfo(archive, profile)); 3.128 } 3.129 3.130 @Override 3.131 - public void visitArchiveDependence(Archive origin, Archive target, String profile) { 3.132 - writer.format("%s -> %s", origin, target); 3.133 - if (options.showProfile && !profile.isEmpty()) { 3.134 + public void visitArchiveDependence(Archive origin, Archive target, Profile profile) { 3.135 + writer.format("%s -> %s", origin.getPathName(), target.getPathName()); 3.136 + if (options.showProfile && profile != null) { 3.137 writer.format(" (%s)%n", profile); 3.138 } else { 3.139 writer.format("%n"); 3.140 @@ -670,19 +699,14 @@ 3.141 } 3.142 } 3.143 3.144 - class DotFileFormatter implements Analyzer.Visitor, AutoCloseable { 3.145 + class DotFileFormatter extends DotGraph<String> implements AutoCloseable { 3.146 private final PrintWriter writer; 3.147 private final String name; 3.148 - DotFileFormatter(PrintWriter writer, String name) { 3.149 - this.writer = writer; 3.150 - this.name = name; 3.151 - writer.format("digraph \"%s\" {%n", name); 3.152 - } 3.153 DotFileFormatter(PrintWriter writer, Archive archive) { 3.154 this.writer = writer; 3.155 this.name = archive.getFileName(); 3.156 writer.format("digraph \"%s\" {%n", name); 3.157 - writer.format(" // Path: %s%n", archive.toString()); 3.158 + writer.format(" // Path: %s%n", archive.getPathName()); 3.159 } 3.160 3.161 @Override 3.162 @@ -690,39 +714,169 @@ 3.163 writer.println("}"); 3.164 } 3.165 3.166 - private final Set<String> edges = new HashSet<>(); 3.167 - private String node = ""; 3.168 @Override 3.169 public void visitDependence(String origin, Archive source, 3.170 - String target, Archive archive, String profile) { 3.171 - if (!node.equals(origin)) { 3.172 - edges.clear(); 3.173 - node = origin; 3.174 - } 3.175 + String target, Archive archive, Profile profile) { 3.176 // if -P option is specified, package name -> profile will 3.177 // be shown and filter out multiple same edges. 3.178 - if (!edges.contains(target)) { 3.179 - StringBuilder sb = new StringBuilder(); 3.180 - String name = options.showProfile && !profile.isEmpty() 3.181 - ? profile 3.182 - : getArchiveName(archive, profile); 3.183 - writer.format(" %-50s -> %s;%n", 3.184 - String.format("\"%s\"", origin), 3.185 - name.isEmpty() ? String.format("\"%s\"", target) 3.186 - : String.format("\"%s (%s)\"", target, name)); 3.187 - edges.add(target); 3.188 + String name = getProfileArchiveInfo(archive, profile); 3.189 + writeEdge(writer, new Edge(origin, target, getProfileArchiveInfo(archive, profile))); 3.190 + } 3.191 + @Override 3.192 + public void visitArchiveDependence(Archive origin, Archive target, Profile profile) { 3.193 + throw new UnsupportedOperationException(); 3.194 + } 3.195 + } 3.196 + 3.197 + class DotSummaryForArchive extends DotGraph<Archive> { 3.198 + @Override 3.199 + public void visitDependence(String origin, Archive source, 3.200 + String target, Archive archive, Profile profile) { 3.201 + Edge e = findEdge(source, archive); 3.202 + assert e != null; 3.203 + // add the dependency to the label if enabled and not compact1 3.204 + if (profile == Profile.COMPACT1) { 3.205 + return; 3.206 + } 3.207 + e.addLabel(origin, target, profileName(archive, profile)); 3.208 + } 3.209 + @Override 3.210 + public void visitArchiveDependence(Archive origin, Archive target, Profile profile) { 3.211 + // add an edge with the archive's name with no tag 3.212 + // so that there is only one node for each JDK archive 3.213 + // while there may be edges to different profiles 3.214 + Edge e = addEdge(origin, target, ""); 3.215 + if (target instanceof JDKArchive) { 3.216 + // add a label to print the profile 3.217 + if (profile == null) { 3.218 + e.addLabel("JDK internal API"); 3.219 + } else if (options.showProfile && !options.showLabel) { 3.220 + e.addLabel(profile.toString()); 3.221 + } 3.222 } 3.223 } 3.224 + } 3.225 3.226 + // DotSummaryForPackage generates the summary.dot file for verbose mode 3.227 + // (-v or -verbose option) that includes all class dependencies. 3.228 + // The summary.dot file shows package-level dependencies. 3.229 + class DotSummaryForPackage extends DotGraph<String> { 3.230 + private String packageOf(String cn) { 3.231 + int i = cn.lastIndexOf('.'); 3.232 + return i > 0 ? cn.substring(0, i) : "<unnamed>"; 3.233 + } 3.234 @Override 3.235 - public void visitArchiveDependence(Archive origin, Archive target, String profile) { 3.236 - String name = options.showProfile && !profile.isEmpty() 3.237 - ? profile : ""; 3.238 - writer.format(" %-30s -> \"%s\";%n", 3.239 - String.format("\"%s\"", origin.getFileName()), 3.240 - name.isEmpty() 3.241 - ? target.getFileName() 3.242 - : String.format("%s (%s)", target.getFileName(), name)); 3.243 + public void visitDependence(String origin, Archive source, 3.244 + String target, Archive archive, Profile profile) { 3.245 + // add a package dependency edge 3.246 + String from = packageOf(origin); 3.247 + String to = packageOf(target); 3.248 + Edge e = addEdge(from, to, getProfileArchiveInfo(archive, profile)); 3.249 + 3.250 + // add the dependency to the label if enabled and not compact1 3.251 + if (!options.showLabel || profile == Profile.COMPACT1) { 3.252 + return; 3.253 + } 3.254 + 3.255 + // trim the package name of origin to shorten the label 3.256 + int i = origin.lastIndexOf('.'); 3.257 + String n1 = i < 0 ? origin : origin.substring(i+1); 3.258 + e.addLabel(n1, target, profileName(archive, profile)); 3.259 + } 3.260 + @Override 3.261 + public void visitArchiveDependence(Archive origin, Archive target, Profile profile) { 3.262 + // nop 3.263 + } 3.264 + } 3.265 + abstract class DotGraph<T> implements Analyzer.Visitor { 3.266 + private final Set<Edge> edges = new LinkedHashSet<>(); 3.267 + private Edge curEdge; 3.268 + public void writeTo(PrintWriter writer) { 3.269 + writer.format("digraph \"summary\" {%n"); 3.270 + for (Edge e: edges) { 3.271 + writeEdge(writer, e); 3.272 + } 3.273 + writer.println("}"); 3.274 + } 3.275 + 3.276 + void writeEdge(PrintWriter writer, Edge e) { 3.277 + writer.format(" %-50s -> \"%s\"%s;%n", 3.278 + String.format("\"%s\"", e.from.toString()), 3.279 + e.tag.isEmpty() ? e.to 3.280 + : String.format("%s (%s)", e.to, e.tag), 3.281 + getLabel(e)); 3.282 + } 3.283 + 3.284 + Edge addEdge(T origin, T target, String tag) { 3.285 + Edge e = new Edge(origin, target, tag); 3.286 + if (e.equals(curEdge)) { 3.287 + return curEdge; 3.288 + } 3.289 + 3.290 + if (edges.contains(e)) { 3.291 + for (Edge e1 : edges) { 3.292 + if (e.equals(e1)) { 3.293 + curEdge = e1; 3.294 + } 3.295 + } 3.296 + } else { 3.297 + edges.add(e); 3.298 + curEdge = e; 3.299 + } 3.300 + return curEdge; 3.301 + } 3.302 + 3.303 + Edge findEdge(T origin, T target) { 3.304 + for (Edge e : edges) { 3.305 + if (e.from.equals(origin) && e.to.equals(target)) { 3.306 + return e; 3.307 + } 3.308 + } 3.309 + return null; 3.310 + } 3.311 + 3.312 + String getLabel(Edge e) { 3.313 + String label = e.label.toString(); 3.314 + return label.isEmpty() ? "" : String.format("[label=\"%s\",fontsize=9]", label); 3.315 + } 3.316 + 3.317 + class Edge { 3.318 + final T from; 3.319 + final T to; 3.320 + final String tag; // optional tag 3.321 + final StringBuilder label = new StringBuilder(); 3.322 + Edge(T from, T to, String tag) { 3.323 + this.from = from; 3.324 + this.to = to; 3.325 + this.tag = tag; 3.326 + } 3.327 + void addLabel(String s) { 3.328 + label.append(s).append("\\n"); 3.329 + } 3.330 + void addLabel(String origin, String target, String profile) { 3.331 + label.append(origin).append(" -> ").append(target); 3.332 + if (!profile.isEmpty()) { 3.333 + label.append(" (" + profile + ")"); 3.334 + } 3.335 + label.append("\\n"); 3.336 + } 3.337 + @Override @SuppressWarnings("unchecked") 3.338 + public boolean equals(Object o) { 3.339 + if (o instanceof DotGraph<?>.Edge) { 3.340 + DotGraph<?>.Edge e = (DotGraph<?>.Edge)o; 3.341 + return this.from.equals(e.from) && 3.342 + this.to.equals(e.to) && 3.343 + this.tag.equals(e.tag); 3.344 + } 3.345 + return false; 3.346 + } 3.347 + @Override 3.348 + public int hashCode() { 3.349 + int hash = 7; 3.350 + hash = 67 * hash + Objects.hashCode(this.from) + 3.351 + Objects.hashCode(this.to) + Objects.hashCode(this.tag); 3.352 + return hash; 3.353 + } 3.354 } 3.355 } 3.356 }
4.1 --- a/src/share/classes/com/sun/tools/jdeps/Profile.java Mon Oct 28 12:29:34 2013 -0700 4.2 +++ b/src/share/classes/com/sun/tools/jdeps/Profile.java Wed Oct 30 08:35:52 2013 -0700 4.3 @@ -81,8 +81,12 @@ 4.4 } 4.5 4.6 static class PackageToProfile { 4.7 + static String[] JAVAX_CRYPTO_PKGS = new String[] { 4.8 + "javax.crypto", 4.9 + "javax.crypto.interfaces", 4.10 + "javax.crypto.spec" 4.11 + }; 4.12 static Map<String, Profile> map = initProfiles(); 4.13 - 4.14 private static Map<String, Profile> initProfiles() { 4.15 try { 4.16 String profilesProps = System.getProperty("jdeps.profiles"); 4.17 @@ -103,6 +107,9 @@ 4.18 findProfile(cf); 4.19 } 4.20 } 4.21 + // special case for javax.crypto.* classes that are not 4.22 + // included in ct.sym since they are in jce.jar 4.23 + Collections.addAll(Profile.COMPACT1.packages, JAVAX_CRYPTO_PKGS); 4.24 } 4.25 } 4.26 } catch (IOException | ConstantPoolException e) {
5.1 --- a/test/tools/jdeps/Basic.java Mon Oct 28 12:29:34 2013 -0700 5.2 +++ b/test/tools/jdeps/Basic.java Wed Oct 30 08:35:52 2013 -0700 5.3 @@ -1,5 +1,5 @@ 5.4 /* 5.5 - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 5.6 + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 5.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.8 * 5.9 * This code is free software; you can redistribute it and/or modify it 5.10 @@ -23,9 +23,9 @@ 5.11 5.12 /* 5.13 * @test 5.14 - * @bug 8003562 8005428 8015912 5.15 + * @bug 8003562 8005428 8015912 8027481 5.16 * @summary Basic tests for jdeps tool 5.17 - * @build Test p.Foo 5.18 + * @build Test p.Foo p.Bar javax.activity.NotCompactProfile 5.19 * @run main Basic 5.20 */ 5.21 5.22 @@ -33,10 +33,12 @@ 5.23 import java.io.IOException; 5.24 import java.io.PrintWriter; 5.25 import java.io.StringWriter; 5.26 +import java.nio.file.Files; 5.27 import java.nio.file.Path; 5.28 import java.nio.file.Paths; 5.29 import java.util.*; 5.30 import java.util.regex.*; 5.31 +import static java.nio.file.StandardCopyOption.*; 5.32 5.33 public class Basic { 5.34 private static boolean symbolFileExist = initProfiles(); 5.35 @@ -74,23 +76,25 @@ 5.36 new String[] {"java.lang", "p"}, 5.37 new String[] {"compact1", "not found"}); 5.38 // test a directory 5.39 + // also test non-SE javax.activity class dependency 5.40 test(new File(testDir, "p"), 5.41 - new String[] {"java.lang", "java.util", "java.lang.management"}, 5.42 - new String[] {"compact1", "compact1", "compact3"}); 5.43 + new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto"}, 5.44 + new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"}, 5.45 + new String[] {"-classpath", testDir.getPath()}); 5.46 // test class-level dependency output 5.47 test(new File(testDir, "Test.class"), 5.48 - new String[] {"java.lang.Object", "java.lang.String", "p.Foo"}, 5.49 - new String[] {"compact1", "compact1", "not found"}, 5.50 + new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"}, 5.51 + new String[] {"compact1", "compact1", "not found", "not found"}, 5.52 new String[] {"-verbose:class"}); 5.53 // test -p option 5.54 test(new File(testDir, "Test.class"), 5.55 - new String[] {"p.Foo"}, 5.56 - new String[] {"not found"}, 5.57 + new String[] {"p.Foo", "p.Bar"}, 5.58 + new String[] {"not found", "not found"}, 5.59 new String[] {"-verbose:class", "-p", "p"}); 5.60 // test -e option 5.61 test(new File(testDir, "Test.class"), 5.62 - new String[] {"p.Foo"}, 5.63 - new String[] {"not found"}, 5.64 + new String[] {"p.Foo", "p.Bar"}, 5.65 + new String[] {"not found", "not found"}, 5.66 new String[] {"-verbose:class", "-e", "p\\..*"}); 5.67 test(new File(testDir, "Test.class"), 5.68 new String[] {"java.lang"}, 5.69 @@ -99,13 +103,34 @@ 5.70 // test -classpath and -include options 5.71 test(null, 5.72 new String[] {"java.lang", "java.util", 5.73 - "java.lang.management"}, 5.74 - new String[] {"compact1", "compact1", "compact3"}, 5.75 + "java.lang.management", "javax.crypto"}, 5.76 + new String[] {"compact1", "compact1", "compact3", "compact1"}, 5.77 new String[] {"-classpath", testDir.getPath(), "-include", "p.+|Test.class"}); 5.78 test(new File(testDir, "Test.class"), 5.79 - new String[] {"java.lang.Object", "java.lang.String", "p.Foo"}, 5.80 - new String[] {"compact1", "compact1", testDir.getName()}, 5.81 + new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"}, 5.82 + new String[] {"compact1", "compact1", testDir.getName(), testDir.getName()}, 5.83 new String[] {"-v", "-classpath", testDir.getPath(), "Test.class"}); 5.84 + 5.85 + // split package p - move p/Foo.class to dir1 and p/Bar.class to dir2 5.86 + Path testClassPath = testDir.toPath(); 5.87 + Path dirP = testClassPath.resolve("p"); 5.88 + Path dir1 = testClassPath.resolve("dir1"); 5.89 + Path subdir1P = dir1.resolve("p"); 5.90 + Path dir2 = testClassPath.resolve("dir2"); 5.91 + Path subdir2P = dir2.resolve("p"); 5.92 + if (!Files.exists(subdir1P)) 5.93 + Files.createDirectories(subdir1P); 5.94 + if (!Files.exists(subdir2P)) 5.95 + Files.createDirectories(subdir2P); 5.96 + Files.move(dirP.resolve("Foo.class"), subdir1P.resolve("Foo.class"), REPLACE_EXISTING); 5.97 + Files.move(dirP.resolve("Bar.class"), subdir2P.resolve("Bar.class"), REPLACE_EXISTING); 5.98 + StringBuilder cpath = new StringBuilder(testDir.toString()); 5.99 + cpath.append(File.pathSeparator).append(dir1.toString()); 5.100 + cpath.append(File.pathSeparator).append(dir2.toString()); 5.101 + test(new File(testDir, "Test.class"), 5.102 + new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"}, 5.103 + new String[] {"compact1", "compact1", dir1.toFile().getName(), dir2.toFile().getName()}, 5.104 + new String[] {"-v", "-classpath", cpath.toString(), "Test.class"}); 5.105 return errors; 5.106 } 5.107 5.108 @@ -148,7 +173,7 @@ 5.109 // Use the linePattern to break the given String into lines, applying 5.110 // the pattern to each line to see if we have a match 5.111 private static Map<String,String> findDeps(String out) { 5.112 - Map<String,String> result = new HashMap<>(); 5.113 + Map<String,String> result = new LinkedHashMap<>(); 5.114 Matcher lm = linePattern.matcher(out); // Line matcher 5.115 Matcher pm = null; // Pattern matcher 5.116 int lines = 0;
6.1 --- a/test/tools/jdeps/Test.java Mon Oct 28 12:29:34 2013 -0700 6.2 +++ b/test/tools/jdeps/Test.java Wed Oct 30 08:35:52 2013 -0700 6.3 @@ -1,5 +1,5 @@ 6.4 /* 6.5 - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 6.6 + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 6.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6.8 * 6.9 * This code is free software; you can redistribute it and/or modify it 6.10 @@ -24,6 +24,7 @@ 6.11 public class Test { 6.12 public void test() { 6.13 p.Foo f = new p.Foo(); 6.14 + p.Bar b = new p.Bar(); 6.15 } 6.16 private String name() { 6.17 return "this test";
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/test/tools/jdeps/javax/activity/NotCompactProfile.java Wed Oct 30 08:35:52 2013 -0700 7.3 @@ -0,0 +1,30 @@ 7.4 +/* 7.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 7.7 + * 7.8 + * This code is free software; you can redistribute it and/or modify it 7.9 + * under the terms of the GNU General Public License version 2 only, as 7.10 + * published by the Free Software Foundation. 7.11 + * 7.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 7.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 7.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 7.15 + * version 2 for more details (a copy is included in the LICENSE file that 7.16 + * accompanied this code). 7.17 + * 7.18 + * You should have received a copy of the GNU General Public License version 7.19 + * 2 along with this work; if not, write to the Free Software Foundation, 7.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 7.21 + * 7.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 7.23 + * or visit www.oracle.com if you need additional information or have any 7.24 + * questions. 7.25 + */ 7.26 + 7.27 +package javax.activity; 7.28 + 7.29 +public class NotCompactProfile { 7.30 + public static String name() { 7.31 + return "not Java SE API"; 7.32 + } 7.33 +}
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/test/tools/jdeps/p/Bar.java Wed Oct 30 08:35:52 2013 -0700 8.3 @@ -0,0 +1,33 @@ 8.4 +/* 8.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 8.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 8.7 + * 8.8 + * This code is free software; you can redistribute it and/or modify it 8.9 + * under the terms of the GNU General Public License version 2 only, as 8.10 + * published by the Free Software Foundation. 8.11 + * 8.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 8.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 8.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 8.15 + * version 2 for more details (a copy is included in the LICENSE file that 8.16 + * accompanied this code). 8.17 + * 8.18 + * You should have received a copy of the GNU General Public License version 8.19 + * 2 along with this work; if not, write to the Free Software Foundation, 8.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 8.21 + * 8.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 8.23 + * or visit www.oracle.com if you need additional information or have any 8.24 + * questions. 8.25 + */ 8.26 + 8.27 +package p; 8.28 + 8.29 +public class Bar extends javax.activity.NotCompactProfile { 8.30 + public String bar() { 8.31 + return "bar"; 8.32 + } 8.33 + public javax.crypto.Cipher getCiper() { 8.34 + return null; 8.35 + } 8.36 +}