src/share/classes/com/sun/tools/jdeps/JdepsTask.java

changeset 2172
aa91bc6e8480
parent 2139
defadd528513
child 2214
4a2ed1900428
     1.1 --- a/src/share/classes/com/sun/tools/jdeps/JdepsTask.java	Mon Oct 28 12:29:34 2013 -0700
     1.2 +++ b/src/share/classes/com/sun/tools/jdeps/JdepsTask.java	Wed Oct 30 08:35:52 2013 -0700
     1.3 @@ -190,6 +190,11 @@
     1.4                  task.options.fullVersion = true;
     1.5              }
     1.6          },
     1.7 +        new HiddenOption(false, "-showlabel") {
     1.8 +            void process(JdepsTask task, String opt, String arg) {
     1.9 +                task.options.showLabel = true;
    1.10 +            }
    1.11 +        },
    1.12          new HiddenOption(true, "-depth") {
    1.13              void process(JdepsTask task, String opt, String arg) throws BadArgs {
    1.14                  try {
    1.15 @@ -279,12 +284,21 @@
    1.16  
    1.17      private void generateDotFiles(Path dir, Analyzer analyzer) throws IOException {
    1.18          Path summary = dir.resolve("summary.dot");
    1.19 -        try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary));
    1.20 -             DotFileFormatter formatter = new DotFileFormatter(sw, "summary")) {
    1.21 -            for (Archive archive : sourceLocations) {
    1.22 -                 analyzer.visitArchiveDependences(archive, formatter);
    1.23 +        boolean verbose = options.verbose == Analyzer.Type.VERBOSE;
    1.24 +        DotGraph<?> graph = verbose ? new DotSummaryForPackage()
    1.25 +                                    : new DotSummaryForArchive();
    1.26 +        for (Archive archive : sourceLocations) {
    1.27 +            analyzer.visitArchiveDependences(archive, graph);
    1.28 +            if (verbose || options.showLabel) {
    1.29 +                // traverse detailed dependences to generate package-level
    1.30 +                // summary or build labels for edges
    1.31 +                analyzer.visitDependences(archive, graph);
    1.32              }
    1.33          }
    1.34 +        try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary))) {
    1.35 +            graph.writeTo(sw);
    1.36 +        }
    1.37 +        // output individual .dot file for each archive
    1.38          if (options.verbose != Analyzer.Type.SUMMARY) {
    1.39              for (Archive archive : sourceLocations) {
    1.40                  if (analyzer.hasDependences(archive)) {
    1.41 @@ -365,17 +379,16 @@
    1.42                  }
    1.43              }
    1.44          }
    1.45 +        sourceLocations.addAll(archives);
    1.46  
    1.47          List<Archive> classpaths = new ArrayList<>(); // for class file lookup
    1.48 +        classpaths.addAll(getClassPathArchives(options.classpath));
    1.49          if (options.includePattern != null) {
    1.50 -            archives.addAll(getClassPathArchives(options.classpath));
    1.51 -        } else {
    1.52 -            classpaths.addAll(getClassPathArchives(options.classpath));
    1.53 +            archives.addAll(classpaths);
    1.54          }
    1.55          classpaths.addAll(PlatformClassPath.getArchives());
    1.56  
    1.57 -        // add all archives to the source locations for reporting
    1.58 -        sourceLocations.addAll(archives);
    1.59 +        // add all classpath archives to the source locations for reporting
    1.60          sourceLocations.addAll(classpaths);
    1.61  
    1.62          // Work queue of names of classfiles to be searched.
    1.63 @@ -557,6 +570,7 @@
    1.64          boolean showSummary;
    1.65          boolean wildcard;
    1.66          boolean apiOnly;
    1.67 +        boolean showLabel;
    1.68          String dotOutputDir;
    1.69          String classpath = "";
    1.70          int depth = 1;
    1.71 @@ -627,16 +641,34 @@
    1.72          return result;
    1.73      }
    1.74  
    1.75 +    /**
    1.76 +     * If the given archive is JDK archive and non-null Profile,
    1.77 +     * this method returns the profile name only if -profile option is specified;
    1.78 +     * a null profile indicates it accesses a private JDK API and this method
    1.79 +     * will return "JDK internal API".
    1.80 +     *
    1.81 +     * For non-JDK archives, this method returns the file name of the archive.
    1.82 +     */
    1.83 +    private String getProfileArchiveInfo(Archive source, Profile profile) {
    1.84 +        if (options.showProfile && profile != null)
    1.85 +            return profile.toString();
    1.86 +
    1.87 +        if (source instanceof JDKArchive) {
    1.88 +            return profile == null ? "JDK internal API (" + source.getFileName() + ")" : "";
    1.89 +        }
    1.90 +        return source.getFileName();
    1.91 +    }
    1.92  
    1.93      /**
    1.94 -     * Returns the file name of the archive for non-JRE class or
    1.95 -     * internal JRE classes.  It returns empty string for SE API.
    1.96 +     * Returns the profile name or "JDK internal API" for JDK archive;
    1.97 +     * otherwise empty string.
    1.98       */
    1.99 -    private static String getArchiveName(Archive source, String profile) {
   1.100 -        String name = source.getFileName();
   1.101 -        if (source instanceof JDKArchive)
   1.102 -            return profile.isEmpty() ? "JDK internal API (" + name + ")" : "";
   1.103 -        return name;
   1.104 +    private String profileName(Archive archive, Profile profile) {
   1.105 +        if (archive instanceof JDKArchive) {
   1.106 +            return Objects.toString(profile, "JDK internal API");
   1.107 +        } else {
   1.108 +            return "";
   1.109 +        }
   1.110      }
   1.111  
   1.112      class RawOutputFormatter implements Analyzer.Visitor {
   1.113 @@ -648,21 +680,18 @@
   1.114          private String pkg = "";
   1.115          @Override
   1.116          public void visitDependence(String origin, Archive source,
   1.117 -                                    String target, Archive archive, String profile) {
   1.118 +                                    String target, Archive archive, Profile profile) {
   1.119              if (!origin.equals(pkg)) {
   1.120                  pkg = origin;
   1.121                  writer.format("   %s (%s)%n", origin, source.getFileName());
   1.122              }
   1.123 -            String name = (options.showProfile && !profile.isEmpty())
   1.124 -                                ? profile
   1.125 -                                : getArchiveName(archive, profile);
   1.126 -            writer.format("      -> %-50s %s%n", target, name);
   1.127 +            writer.format("      -> %-50s %s%n", target, getProfileArchiveInfo(archive, profile));
   1.128          }
   1.129  
   1.130          @Override
   1.131 -        public void visitArchiveDependence(Archive origin, Archive target, String profile) {
   1.132 -            writer.format("%s -> %s", origin, target);
   1.133 -            if (options.showProfile && !profile.isEmpty()) {
   1.134 +        public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
   1.135 +            writer.format("%s -> %s", origin.getPathName(), target.getPathName());
   1.136 +            if (options.showProfile && profile != null) {
   1.137                  writer.format(" (%s)%n", profile);
   1.138              } else {
   1.139                  writer.format("%n");
   1.140 @@ -670,19 +699,14 @@
   1.141          }
   1.142      }
   1.143  
   1.144 -    class DotFileFormatter implements Analyzer.Visitor, AutoCloseable {
   1.145 +    class DotFileFormatter extends DotGraph<String> implements AutoCloseable {
   1.146          private final PrintWriter writer;
   1.147          private final String name;
   1.148 -        DotFileFormatter(PrintWriter writer, String name) {
   1.149 -            this.writer = writer;
   1.150 -            this.name = name;
   1.151 -            writer.format("digraph \"%s\" {%n", name);
   1.152 -        }
   1.153          DotFileFormatter(PrintWriter writer, Archive archive) {
   1.154              this.writer = writer;
   1.155              this.name = archive.getFileName();
   1.156              writer.format("digraph \"%s\" {%n", name);
   1.157 -            writer.format("    // Path: %s%n", archive.toString());
   1.158 +            writer.format("    // Path: %s%n", archive.getPathName());
   1.159          }
   1.160  
   1.161          @Override
   1.162 @@ -690,39 +714,169 @@
   1.163              writer.println("}");
   1.164          }
   1.165  
   1.166 -        private final Set<String> edges = new HashSet<>();
   1.167 -        private String node = "";
   1.168          @Override
   1.169          public void visitDependence(String origin, Archive source,
   1.170 -                                    String target, Archive archive, String profile) {
   1.171 -            if (!node.equals(origin)) {
   1.172 -                edges.clear();
   1.173 -                node = origin;
   1.174 -            }
   1.175 +                                    String target, Archive archive, Profile profile) {
   1.176              // if -P option is specified, package name -> profile will
   1.177              // be shown and filter out multiple same edges.
   1.178 -            if (!edges.contains(target)) {
   1.179 -                StringBuilder sb = new StringBuilder();
   1.180 -                String name = options.showProfile && !profile.isEmpty()
   1.181 -                                  ? profile
   1.182 -                                  : getArchiveName(archive, profile);
   1.183 -                writer.format("   %-50s -> %s;%n",
   1.184 -                                 String.format("\"%s\"", origin),
   1.185 -                                 name.isEmpty() ? String.format("\"%s\"", target)
   1.186 -                                                :  String.format("\"%s (%s)\"", target, name));
   1.187 -                edges.add(target);
   1.188 +            String name = getProfileArchiveInfo(archive, profile);
   1.189 +            writeEdge(writer, new Edge(origin, target, getProfileArchiveInfo(archive, profile)));
   1.190 +        }
   1.191 +        @Override
   1.192 +        public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
   1.193 +            throw new UnsupportedOperationException();
   1.194 +        }
   1.195 +    }
   1.196 +
   1.197 +    class DotSummaryForArchive extends DotGraph<Archive> {
   1.198 +        @Override
   1.199 +        public void visitDependence(String origin, Archive source,
   1.200 +                                    String target, Archive archive, Profile profile) {
   1.201 +            Edge e = findEdge(source, archive);
   1.202 +            assert e != null;
   1.203 +            // add the dependency to the label if enabled and not compact1
   1.204 +            if (profile == Profile.COMPACT1) {
   1.205 +                return;
   1.206 +            }
   1.207 +            e.addLabel(origin, target, profileName(archive, profile));
   1.208 +        }
   1.209 +        @Override
   1.210 +        public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
   1.211 +            // add an edge with the archive's name with no tag
   1.212 +            // so that there is only one node for each JDK archive
   1.213 +            // while there may be edges to different profiles
   1.214 +            Edge e = addEdge(origin, target, "");
   1.215 +            if (target instanceof JDKArchive) {
   1.216 +                // add a label to print the profile
   1.217 +                if (profile == null) {
   1.218 +                    e.addLabel("JDK internal API");
   1.219 +                } else if (options.showProfile && !options.showLabel) {
   1.220 +                    e.addLabel(profile.toString());
   1.221 +                }
   1.222              }
   1.223          }
   1.224 +    }
   1.225  
   1.226 +    // DotSummaryForPackage generates the summary.dot file for verbose mode
   1.227 +    // (-v or -verbose option) that includes all class dependencies.
   1.228 +    // The summary.dot file shows package-level dependencies.
   1.229 +    class DotSummaryForPackage extends DotGraph<String> {
   1.230 +        private String packageOf(String cn) {
   1.231 +            int i = cn.lastIndexOf('.');
   1.232 +            return i > 0 ? cn.substring(0, i) : "<unnamed>";
   1.233 +        }
   1.234          @Override
   1.235 -        public void visitArchiveDependence(Archive origin, Archive target, String profile) {
   1.236 -             String name = options.showProfile && !profile.isEmpty()
   1.237 -                                ? profile : "";
   1.238 -             writer.format("   %-30s -> \"%s\";%n",
   1.239 -                           String.format("\"%s\"", origin.getFileName()),
   1.240 -                           name.isEmpty()
   1.241 -                               ? target.getFileName()
   1.242 -                               : String.format("%s (%s)", target.getFileName(), name));
   1.243 +        public void visitDependence(String origin, Archive source,
   1.244 +                                    String target, Archive archive, Profile profile) {
   1.245 +            // add a package dependency edge
   1.246 +            String from = packageOf(origin);
   1.247 +            String to = packageOf(target);
   1.248 +            Edge e = addEdge(from, to, getProfileArchiveInfo(archive, profile));
   1.249 +
   1.250 +            // add the dependency to the label if enabled and not compact1
   1.251 +            if (!options.showLabel || profile == Profile.COMPACT1) {
   1.252 +                return;
   1.253 +            }
   1.254 +
   1.255 +            // trim the package name of origin to shorten the label
   1.256 +            int i = origin.lastIndexOf('.');
   1.257 +            String n1 = i < 0 ? origin : origin.substring(i+1);
   1.258 +            e.addLabel(n1, target, profileName(archive, profile));
   1.259 +        }
   1.260 +        @Override
   1.261 +        public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
   1.262 +            // nop
   1.263 +        }
   1.264 +    }
   1.265 +    abstract class DotGraph<T> implements Analyzer.Visitor  {
   1.266 +        private final Set<Edge> edges = new LinkedHashSet<>();
   1.267 +        private Edge curEdge;
   1.268 +        public void writeTo(PrintWriter writer) {
   1.269 +            writer.format("digraph \"summary\" {%n");
   1.270 +            for (Edge e: edges) {
   1.271 +                writeEdge(writer, e);
   1.272 +            }
   1.273 +            writer.println("}");
   1.274 +        }
   1.275 +
   1.276 +        void writeEdge(PrintWriter writer, Edge e) {
   1.277 +            writer.format("   %-50s -> \"%s\"%s;%n",
   1.278 +                          String.format("\"%s\"", e.from.toString()),
   1.279 +                          e.tag.isEmpty() ? e.to
   1.280 +                                          : String.format("%s (%s)", e.to, e.tag),
   1.281 +                          getLabel(e));
   1.282 +        }
   1.283 +
   1.284 +        Edge addEdge(T origin, T target, String tag) {
   1.285 +            Edge e = new Edge(origin, target, tag);
   1.286 +            if (e.equals(curEdge)) {
   1.287 +                return curEdge;
   1.288 +            }
   1.289 +
   1.290 +            if (edges.contains(e)) {
   1.291 +                for (Edge e1 : edges) {
   1.292 +                   if (e.equals(e1)) {
   1.293 +                       curEdge = e1;
   1.294 +                   }
   1.295 +                }
   1.296 +            } else {
   1.297 +                edges.add(e);
   1.298 +                curEdge = e;
   1.299 +            }
   1.300 +            return curEdge;
   1.301 +        }
   1.302 +
   1.303 +        Edge findEdge(T origin, T target) {
   1.304 +            for (Edge e : edges) {
   1.305 +                if (e.from.equals(origin) && e.to.equals(target)) {
   1.306 +                    return e;
   1.307 +                }
   1.308 +            }
   1.309 +            return null;
   1.310 +        }
   1.311 +
   1.312 +        String getLabel(Edge e) {
   1.313 +            String label = e.label.toString();
   1.314 +            return label.isEmpty() ? "" : String.format("[label=\"%s\",fontsize=9]", label);
   1.315 +        }
   1.316 +
   1.317 +        class Edge {
   1.318 +            final T from;
   1.319 +            final T to;
   1.320 +            final String tag;  // optional tag
   1.321 +            final StringBuilder label = new StringBuilder();
   1.322 +            Edge(T from, T to, String tag) {
   1.323 +                this.from = from;
   1.324 +                this.to = to;
   1.325 +                this.tag = tag;
   1.326 +            }
   1.327 +            void addLabel(String s) {
   1.328 +                label.append(s).append("\\n");
   1.329 +            }
   1.330 +            void addLabel(String origin, String target, String profile) {
   1.331 +                label.append(origin).append(" -> ").append(target);
   1.332 +                if (!profile.isEmpty()) {
   1.333 +                    label.append(" (" + profile + ")");
   1.334 +                }
   1.335 +                label.append("\\n");
   1.336 +            }
   1.337 +            @Override @SuppressWarnings("unchecked")
   1.338 +            public boolean equals(Object o) {
   1.339 +                if (o instanceof DotGraph<?>.Edge) {
   1.340 +                    DotGraph<?>.Edge e = (DotGraph<?>.Edge)o;
   1.341 +                    return this.from.equals(e.from) &&
   1.342 +                           this.to.equals(e.to) &&
   1.343 +                           this.tag.equals(e.tag);
   1.344 +                }
   1.345 +                return false;
   1.346 +            }
   1.347 +            @Override
   1.348 +            public int hashCode() {
   1.349 +                int hash = 7;
   1.350 +                hash = 67 * hash + Objects.hashCode(this.from) +
   1.351 +                       Objects.hashCode(this.to) + Objects.hashCode(this.tag);
   1.352 +                return hash;
   1.353 +            }
   1.354          }
   1.355      }
   1.356  }

mercurial