8027481: jdeps to handle classes with the same package name and correct profile for javax.crypto.*

Wed, 30 Oct 2013 08:35:52 -0700

author
mchung
date
Wed, 30 Oct 2013 08:35:52 -0700
changeset 2172
aa91bc6e8480
parent 2171
44e3ba40e00c
child 2173
537fa895fd74

8027481: jdeps to handle classes with the same package name and correct profile for javax.crypto.*
Reviewed-by: alanb, dfuchs

src/share/classes/com/sun/tools/jdeps/Analyzer.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/jdeps/Archive.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/jdeps/JdepsTask.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/jdeps/Profile.java file | annotate | diff | comparison | revisions
test/tools/jdeps/Basic.java file | annotate | diff | comparison | revisions
test/tools/jdeps/Test.java file | annotate | diff | comparison | revisions
test/tools/jdeps/javax/activity/NotCompactProfile.java file | annotate | diff | comparison | revisions
test/tools/jdeps/p/Bar.java file | annotate | diff | comparison | revisions
     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 +}

mercurial