--- a/langtools/src/share/classes/com/sun/tools/jdeps/Analyzer.java Tue Jul 08 15:42:04 2014 +0100
+++ b/langtools/src/share/classes/com/sun/tools/jdeps/Analyzer.java Tue Jul 08 18:26:34 2014 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,16 +26,13 @@
import com.sun.tools.classfile.Dependency.Location;
import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
-import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import java.util.TreeMap;
-import java.util.TreeSet;
/**
* Dependency Analyzer.
@@ -52,7 +49,16 @@
VERBOSE
}
+ /**
+ * Filter to be applied when analyzing the dependencies from the given archives.
+ * Only the accepted dependencies are recorded.
+ */
+ interface Filter {
+ boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive);
+ }
+
private final Type type;
+ private final Filter filter;
private final Map<Archive, ArchiveDeps> results = new HashMap<>();
private final Map<Location, Archive> map = new HashMap<>();
private final Archive NOT_FOUND
@@ -62,9 +68,11 @@
* Constructs an Analyzer instance.
*
* @param type Type of the dependency analysis
+ * @param filter
*/
- public Analyzer(Type type) {
+ public Analyzer(Type type, Filter filter) {
this.type = type;
+ this.filter = filter;
}
/**
@@ -72,6 +80,18 @@
*/
public void run(List<Archive> archives) {
// build a map from Location to Archive
+ buildLocationArchiveMap(archives);
+
+ // traverse and analyze all dependencies
+ for (Archive archive : archives) {
+ ArchiveDeps deps = new ArchiveDeps(archive, type);
+ archive.visitDependences(deps);
+ results.put(archive, deps);
+ }
+ }
+
+ private void buildLocationArchiveMap(List<Archive> archives) {
+ // build a map from Location to Archive
for (Archive archive: archives) {
for (Location l: archive.getClasses()) {
if (!map.containsKey(l)) {
@@ -81,190 +101,202 @@
}
}
}
- // traverse and analyze all dependencies
- for (Archive archive : archives) {
- ArchiveDeps deps;
- if (type == Type.CLASS || type == Type.VERBOSE) {
- deps = new ClassVisitor(archive);
- } else {
- deps = new PackageVisitor(archive);
- }
- archive.visitDependences(deps);
- results.put(archive, deps);
- }
}
public boolean hasDependences(Archive archive) {
if (results.containsKey(archive)) {
- return results.get(archive).deps.size() > 0;
+ return results.get(archive).dependencies().size() > 0;
}
return false;
}
public interface Visitor {
/**
- * Visits the source archive to its destination archive of
- * a recorded dependency.
- */
- void visitArchiveDependence(Archive origin, Archive target, Profile profile);
- /**
* Visits a recorded dependency from origin to target which can be
- * a fully-qualified classname, a package name, a profile or
+ * a fully-qualified classname, a package name, a module or
* archive name depending on the Analyzer's type.
*/
- void visitDependence(String origin, Archive source, String target, Archive archive, Profile profile);
+ public void visitDependence(String origin, Archive originArchive,
+ String target, Archive targetArchive);
}
- public void visitArchiveDependences(Archive source, Visitor v) {
- ArchiveDeps r = results.get(source);
- for (ArchiveDeps.Dep d: r.requireArchives()) {
- v.visitArchiveDependence(r.archive, d.archive, d.profile);
+ /**
+ * Visit the dependencies of the given source.
+ * If the requested level is SUMMARY, it will visit the required archives list.
+ */
+ public void visitDependences(Archive source, Visitor v, Type level) {
+ if (level == Type.SUMMARY) {
+ final ArchiveDeps result = results.get(source);
+ result.requires().stream()
+ .sorted(Comparator.comparing(Archive::getName))
+ .forEach(archive -> {
+ Profile profile = result.getTargetProfile(archive);
+ v.visitDependence(source.getName(), source,
+ profile != null ? profile.profileName() : archive.getName(), archive);
+ });
+ } else {
+ ArchiveDeps result = results.get(source);
+ if (level != type) {
+ // requesting different level of analysis
+ result = new ArchiveDeps(source, level);
+ source.visitDependences(result);
+ }
+ result.dependencies().stream()
+ .sorted(Comparator.comparing(Dep::origin)
+ .thenComparing(Dep::target))
+ .forEach(d -> v.visitDependence(d.origin(), d.originArchive(), d.target(), d.targetArchive()));
}
}
public void visitDependences(Archive source, Visitor v) {
- ArchiveDeps r = results.get(source);
- for (Map.Entry<String, SortedSet<ArchiveDeps.Dep>> e: r.deps.entrySet()) {
- String origin = e.getKey();
- for (ArchiveDeps.Dep d: e.getValue()) {
- // filter intra-dependency unless in verbose mode
- if (type == Type.VERBOSE || d.archive != source) {
- v.visitDependence(origin, source, d.target, d.archive, d.profile);
- }
- }
- }
+ visitDependences(source, v, type);
}
/**
- * ArchiveDeps contains the dependencies for an Archive that
- * can have one or more classes.
+ * ArchiveDeps contains the dependencies for an Archive that can have one or
+ * more classes.
*/
- private abstract class ArchiveDeps implements Archive.Visitor {
- final Archive archive;
- final SortedMap<String, SortedSet<Dep>> deps;
- ArchiveDeps(Archive archive) {
+ class ArchiveDeps implements Archive.Visitor {
+ protected final Archive archive;
+ protected final Set<Archive> requires;
+ protected final Set<Dep> deps;
+ protected final Type level;
+ private Profile profile;
+ ArchiveDeps(Archive archive, Type level) {
this.archive = archive;
- this.deps = new TreeMap<>();
- }
-
- void add(String origin, String target, Archive targetArchive, String pkgName) {
- SortedSet<Dep> set = deps.get(origin);
- if (set == null) {
- deps.put(origin, set = new TreeSet<>());
- }
- Profile p = targetArchive instanceof JDKArchive
- ? Profile.getProfile(pkgName) : null;
- set.add(new Dep(target, targetArchive, p));
+ this.deps = new HashSet<>();
+ this.requires = new HashSet<>();
+ this.level = level;
}
- /**
- * Returns the list of Archive dependences. The returned
- * list contains one {@code Dep} instance per one archive
- * and with the minimum profile this archive depends on.
- */
- List<Dep> requireArchives() {
- Map<Archive,Profile> map = new HashMap<>();
- for (Set<Dep> set: deps.values()) {
- for (Dep d: set) {
- if (this.archive != d.archive) {
- Profile p = map.get(d.archive);
- if (p == null || (d.profile != null && p.profile < d.profile.profile)) {
- map.put(d.archive, d.profile);
- }
- }
- }
+ Set<Dep> dependencies() {
+ return deps;
+ }
+
+ Set<Archive> requires() {
+ return requires;
+ }
+
+ Profile getTargetProfile(Archive target) {
+ return JDKArchive.isProfileArchive(target) ? profile : null;
+ }
+
+ Archive findArchive(Location t) {
+ Archive target = archive.getClasses().contains(t) ? archive : map.get(t);
+ if (target == null) {
+ map.put(t, target = NOT_FOUND);
}
- List<Dep> list = new ArrayList<>();
- for (Map.Entry<Archive,Profile> e: map.entrySet()) {
- list.add(new Dep("", e.getKey(), e.getValue()));
- }
- return list;
+ return target;
}
- /**
- * Dep represents a dependence where the target can be
- * a classname or packagename and the archive and profile
- * the target belongs to.
- */
- class Dep implements Comparable<Dep> {
- final String target;
- final Archive archive;
- final Profile profile;
- Dep(String target, Archive archive, Profile p) {
- this.target = target;
- this.archive = archive;
- this.profile = p;
+ // return classname or package name depedning on the level
+ private String getLocationName(Location o) {
+ if (level == Type.CLASS || level == Type.VERBOSE) {
+ return o.getClassName();
+ } else {
+ String pkg = o.getPackageName();
+ return pkg.isEmpty() ? "<unnamed>" : pkg;
+ }
+ }
+
+ @Override
+ public void visit(Location o, Location t) {
+ Archive targetArchive = findArchive(t);
+ if (filter.accepts(o, archive, t, targetArchive)) {
+ addDep(o, t);
+ if (!requires.contains(targetArchive)) {
+ requires.add(targetArchive);
+ }
}
+ if (targetArchive instanceof JDKArchive) {
+ Profile p = Profile.getProfile(t.getPackageName());
+ if (profile == null || (p != null && p.compareTo(profile) > 0)) {
+ profile = p;
+ }
+ }
+ }
- @Override
- public boolean equals(Object o) {
- if (o instanceof Dep) {
- Dep d = (Dep)o;
- return this.archive == d.archive && this.target.equals(d.target);
- }
- return false;
+ private Dep curDep;
+ protected Dep addDep(Location o, Location t) {
+ String origin = getLocationName(o);
+ String target = getLocationName(t);
+ Archive targetArchive = findArchive(t);
+ if (curDep != null &&
+ curDep.origin().equals(origin) &&
+ curDep.originArchive() == archive &&
+ curDep.target().equals(target) &&
+ curDep.targetArchive() == targetArchive) {
+ return curDep;
}
- @Override
- public int hashCode() {
- int hash = 3;
- hash = 17 * hash + Objects.hashCode(this.archive);
- hash = 17 * hash + Objects.hashCode(this.target);
- return hash;
- }
-
- @Override
- public int compareTo(Dep o) {
- if (this.target.equals(o.target)) {
- if (this.archive == o.archive) {
- return 0;
- } else {
- return this.archive.getFileName().compareTo(o.archive.getFileName());
+ Dep e = new Dep(origin, archive, target, targetArchive);
+ if (deps.contains(e)) {
+ for (Dep e1 : deps) {
+ if (e.equals(e1)) {
+ curDep = e1;
}
}
- return this.target.compareTo(o.target);
+ } else {
+ deps.add(e);
+ curDep = e;
}
- }
- public abstract void visit(Location o, Location t);
- }
-
- private class ClassVisitor extends ArchiveDeps {
- ClassVisitor(Archive archive) {
- super(archive);
- }
- @Override
- public void visit(Location o, Location t) {
- Archive targetArchive =
- this.archive.getClasses().contains(t) ? this.archive : map.get(t);
- if (targetArchive == null) {
- map.put(t, targetArchive = NOT_FOUND);
- }
-
- String origin = o.getClassName();
- String target = t.getClassName();
- add(origin, target, targetArchive, t.getPackageName());
+ return curDep;
}
}
- private class PackageVisitor extends ArchiveDeps {
- PackageVisitor(Archive archive) {
- super(archive);
+ /*
+ * Class-level or package-level dependency
+ */
+ class Dep {
+ final String origin;
+ final Archive originArchive;
+ final String target;
+ final Archive targetArchive;
+
+ Dep(String origin, Archive originArchive, String target, Archive targetArchive) {
+ this.origin = origin;
+ this.originArchive = originArchive;
+ this.target = target;
+ this.targetArchive = targetArchive;
}
+
+ String origin() {
+ return origin;
+ }
+
+ Archive originArchive() {
+ return originArchive;
+ }
+
+ String target() {
+ return target;
+ }
+
+ Archive targetArchive() {
+ return targetArchive;
+ }
+
@Override
- public void visit(Location o, Location t) {
- Archive targetArchive =
- this.archive.getClasses().contains(t) ? this.archive : map.get(t);
- if (targetArchive == null) {
- map.put(t, targetArchive = NOT_FOUND);
+ @SuppressWarnings("unchecked")
+ public boolean equals(Object o) {
+ if (o instanceof Dep) {
+ Dep d = (Dep) o;
+ return this.origin.equals(d.origin) &&
+ this.originArchive == d.originArchive &&
+ this.target.equals(d.target) &&
+ this.targetArchive == d.targetArchive;
}
+ return false;
+ }
- String origin = packageOf(o);
- String target = packageOf(t);
- add(origin, target, targetArchive, t.getPackageName());
- }
- public String packageOf(Location o) {
- String pkg = o.getPackageName();
- return pkg.isEmpty() ? "<unnamed>" : pkg;
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 67*hash + Objects.hashCode(this.origin)
+ + Objects.hashCode(this.originArchive)
+ + Objects.hashCode(this.target)
+ + Objects.hashCode(this.targetArchive);
+ return hash;
}
}
}