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

changeset 2172
aa91bc6e8480
parent 2139
defadd528513
child 2214
4a2ed1900428
equal deleted inserted replaced
2171:44e3ba40e00c 2172:aa91bc6e8480
188 new HiddenOption(false, "-fullversion") { 188 new HiddenOption(false, "-fullversion") {
189 void process(JdepsTask task, String opt, String arg) { 189 void process(JdepsTask task, String opt, String arg) {
190 task.options.fullVersion = true; 190 task.options.fullVersion = true;
191 } 191 }
192 }, 192 },
193 new HiddenOption(false, "-showlabel") {
194 void process(JdepsTask task, String opt, String arg) {
195 task.options.showLabel = true;
196 }
197 },
193 new HiddenOption(true, "-depth") { 198 new HiddenOption(true, "-depth") {
194 void process(JdepsTask task, String opt, String arg) throws BadArgs { 199 void process(JdepsTask task, String opt, String arg) throws BadArgs {
195 try { 200 try {
196 task.options.depth = Integer.parseInt(arg); 201 task.options.depth = Integer.parseInt(arg);
197 } catch (NumberFormatException e) { 202 } catch (NumberFormatException e) {
277 return true; 282 return true;
278 } 283 }
279 284
280 private void generateDotFiles(Path dir, Analyzer analyzer) throws IOException { 285 private void generateDotFiles(Path dir, Analyzer analyzer) throws IOException {
281 Path summary = dir.resolve("summary.dot"); 286 Path summary = dir.resolve("summary.dot");
282 try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary)); 287 boolean verbose = options.verbose == Analyzer.Type.VERBOSE;
283 DotFileFormatter formatter = new DotFileFormatter(sw, "summary")) { 288 DotGraph<?> graph = verbose ? new DotSummaryForPackage()
284 for (Archive archive : sourceLocations) { 289 : new DotSummaryForArchive();
285 analyzer.visitArchiveDependences(archive, formatter); 290 for (Archive archive : sourceLocations) {
286 } 291 analyzer.visitArchiveDependences(archive, graph);
287 } 292 if (verbose || options.showLabel) {
293 // traverse detailed dependences to generate package-level
294 // summary or build labels for edges
295 analyzer.visitDependences(archive, graph);
296 }
297 }
298 try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary))) {
299 graph.writeTo(sw);
300 }
301 // output individual .dot file for each archive
288 if (options.verbose != Analyzer.Type.SUMMARY) { 302 if (options.verbose != Analyzer.Type.SUMMARY) {
289 for (Archive archive : sourceLocations) { 303 for (Archive archive : sourceLocations) {
290 if (analyzer.hasDependences(archive)) { 304 if (analyzer.hasDependences(archive)) {
291 Path dotfile = dir.resolve(archive.getFileName() + ".dot"); 305 Path dotfile = dir.resolve(archive.getFileName() + ".dot");
292 try (PrintWriter pw = new PrintWriter(Files.newOutputStream(dotfile)); 306 try (PrintWriter pw = new PrintWriter(Files.newOutputStream(dotfile));
363 } else { 377 } else {
364 warning("warn.invalid.arg", s); 378 warning("warn.invalid.arg", s);
365 } 379 }
366 } 380 }
367 } 381 }
382 sourceLocations.addAll(archives);
368 383
369 List<Archive> classpaths = new ArrayList<>(); // for class file lookup 384 List<Archive> classpaths = new ArrayList<>(); // for class file lookup
385 classpaths.addAll(getClassPathArchives(options.classpath));
370 if (options.includePattern != null) { 386 if (options.includePattern != null) {
371 archives.addAll(getClassPathArchives(options.classpath)); 387 archives.addAll(classpaths);
372 } else {
373 classpaths.addAll(getClassPathArchives(options.classpath));
374 } 388 }
375 classpaths.addAll(PlatformClassPath.getArchives()); 389 classpaths.addAll(PlatformClassPath.getArchives());
376 390
377 // add all archives to the source locations for reporting 391 // add all classpath archives to the source locations for reporting
378 sourceLocations.addAll(archives);
379 sourceLocations.addAll(classpaths); 392 sourceLocations.addAll(classpaths);
380 393
381 // Work queue of names of classfiles to be searched. 394 // Work queue of names of classfiles to be searched.
382 // Entries will be unique, and for classes that do not yet have 395 // Entries will be unique, and for classes that do not yet have
383 // dependencies in the results map. 396 // dependencies in the results map.
555 boolean fullVersion; 568 boolean fullVersion;
556 boolean showProfile; 569 boolean showProfile;
557 boolean showSummary; 570 boolean showSummary;
558 boolean wildcard; 571 boolean wildcard;
559 boolean apiOnly; 572 boolean apiOnly;
573 boolean showLabel;
560 String dotOutputDir; 574 String dotOutputDir;
561 String classpath = ""; 575 String classpath = "";
562 int depth = 1; 576 int depth = 1;
563 Analyzer.Type verbose = Analyzer.Type.PACKAGE; 577 Analyzer.Type verbose = Analyzer.Type.PACKAGE;
564 Set<String> packageNames = new HashSet<>(); 578 Set<String> packageNames = new HashSet<>();
625 } 639 }
626 } 640 }
627 return result; 641 return result;
628 } 642 }
629 643
630
631 /** 644 /**
632 * Returns the file name of the archive for non-JRE class or 645 * If the given archive is JDK archive and non-null Profile,
633 * internal JRE classes. It returns empty string for SE API. 646 * this method returns the profile name only if -profile option is specified;
647 * a null profile indicates it accesses a private JDK API and this method
648 * will return "JDK internal API".
649 *
650 * For non-JDK archives, this method returns the file name of the archive.
634 */ 651 */
635 private static String getArchiveName(Archive source, String profile) { 652 private String getProfileArchiveInfo(Archive source, Profile profile) {
636 String name = source.getFileName(); 653 if (options.showProfile && profile != null)
637 if (source instanceof JDKArchive) 654 return profile.toString();
638 return profile.isEmpty() ? "JDK internal API (" + name + ")" : ""; 655
639 return name; 656 if (source instanceof JDKArchive) {
657 return profile == null ? "JDK internal API (" + source.getFileName() + ")" : "";
658 }
659 return source.getFileName();
660 }
661
662 /**
663 * Returns the profile name or "JDK internal API" for JDK archive;
664 * otherwise empty string.
665 */
666 private String profileName(Archive archive, Profile profile) {
667 if (archive instanceof JDKArchive) {
668 return Objects.toString(profile, "JDK internal API");
669 } else {
670 return "";
671 }
640 } 672 }
641 673
642 class RawOutputFormatter implements Analyzer.Visitor { 674 class RawOutputFormatter implements Analyzer.Visitor {
643 private final PrintWriter writer; 675 private final PrintWriter writer;
644 RawOutputFormatter(PrintWriter writer) { 676 RawOutputFormatter(PrintWriter writer) {
646 } 678 }
647 679
648 private String pkg = ""; 680 private String pkg = "";
649 @Override 681 @Override
650 public void visitDependence(String origin, Archive source, 682 public void visitDependence(String origin, Archive source,
651 String target, Archive archive, String profile) { 683 String target, Archive archive, Profile profile) {
652 if (!origin.equals(pkg)) { 684 if (!origin.equals(pkg)) {
653 pkg = origin; 685 pkg = origin;
654 writer.format(" %s (%s)%n", origin, source.getFileName()); 686 writer.format(" %s (%s)%n", origin, source.getFileName());
655 } 687 }
656 String name = (options.showProfile && !profile.isEmpty()) 688 writer.format(" -> %-50s %s%n", target, getProfileArchiveInfo(archive, profile));
657 ? profile
658 : getArchiveName(archive, profile);
659 writer.format(" -> %-50s %s%n", target, name);
660 } 689 }
661 690
662 @Override 691 @Override
663 public void visitArchiveDependence(Archive origin, Archive target, String profile) { 692 public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
664 writer.format("%s -> %s", origin, target); 693 writer.format("%s -> %s", origin.getPathName(), target.getPathName());
665 if (options.showProfile && !profile.isEmpty()) { 694 if (options.showProfile && profile != null) {
666 writer.format(" (%s)%n", profile); 695 writer.format(" (%s)%n", profile);
667 } else { 696 } else {
668 writer.format("%n"); 697 writer.format("%n");
669 } 698 }
670 } 699 }
671 } 700 }
672 701
673 class DotFileFormatter implements Analyzer.Visitor, AutoCloseable { 702 class DotFileFormatter extends DotGraph<String> implements AutoCloseable {
674 private final PrintWriter writer; 703 private final PrintWriter writer;
675 private final String name; 704 private final String name;
676 DotFileFormatter(PrintWriter writer, String name) {
677 this.writer = writer;
678 this.name = name;
679 writer.format("digraph \"%s\" {%n", name);
680 }
681 DotFileFormatter(PrintWriter writer, Archive archive) { 705 DotFileFormatter(PrintWriter writer, Archive archive) {
682 this.writer = writer; 706 this.writer = writer;
683 this.name = archive.getFileName(); 707 this.name = archive.getFileName();
684 writer.format("digraph \"%s\" {%n", name); 708 writer.format("digraph \"%s\" {%n", name);
685 writer.format(" // Path: %s%n", archive.toString()); 709 writer.format(" // Path: %s%n", archive.getPathName());
686 } 710 }
687 711
688 @Override 712 @Override
689 public void close() { 713 public void close() {
690 writer.println("}"); 714 writer.println("}");
691 } 715 }
692 716
693 private final Set<String> edges = new HashSet<>();
694 private String node = "";
695 @Override 717 @Override
696 public void visitDependence(String origin, Archive source, 718 public void visitDependence(String origin, Archive source,
697 String target, Archive archive, String profile) { 719 String target, Archive archive, Profile profile) {
698 if (!node.equals(origin)) {
699 edges.clear();
700 node = origin;
701 }
702 // if -P option is specified, package name -> profile will 720 // if -P option is specified, package name -> profile will
703 // be shown and filter out multiple same edges. 721 // be shown and filter out multiple same edges.
704 if (!edges.contains(target)) { 722 String name = getProfileArchiveInfo(archive, profile);
705 StringBuilder sb = new StringBuilder(); 723 writeEdge(writer, new Edge(origin, target, getProfileArchiveInfo(archive, profile)));
706 String name = options.showProfile && !profile.isEmpty() 724 }
707 ? profile
708 : getArchiveName(archive, profile);
709 writer.format(" %-50s -> %s;%n",
710 String.format("\"%s\"", origin),
711 name.isEmpty() ? String.format("\"%s\"", target)
712 : String.format("\"%s (%s)\"", target, name));
713 edges.add(target);
714 }
715 }
716
717 @Override 725 @Override
718 public void visitArchiveDependence(Archive origin, Archive target, String profile) { 726 public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
719 String name = options.showProfile && !profile.isEmpty() 727 throw new UnsupportedOperationException();
720 ? profile : ""; 728 }
721 writer.format(" %-30s -> \"%s\";%n", 729 }
722 String.format("\"%s\"", origin.getFileName()), 730
723 name.isEmpty() 731 class DotSummaryForArchive extends DotGraph<Archive> {
724 ? target.getFileName() 732 @Override
725 : String.format("%s (%s)", target.getFileName(), name)); 733 public void visitDependence(String origin, Archive source,
734 String target, Archive archive, Profile profile) {
735 Edge e = findEdge(source, archive);
736 assert e != null;
737 // add the dependency to the label if enabled and not compact1
738 if (profile == Profile.COMPACT1) {
739 return;
740 }
741 e.addLabel(origin, target, profileName(archive, profile));
742 }
743 @Override
744 public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
745 // add an edge with the archive's name with no tag
746 // so that there is only one node for each JDK archive
747 // while there may be edges to different profiles
748 Edge e = addEdge(origin, target, "");
749 if (target instanceof JDKArchive) {
750 // add a label to print the profile
751 if (profile == null) {
752 e.addLabel("JDK internal API");
753 } else if (options.showProfile && !options.showLabel) {
754 e.addLabel(profile.toString());
755 }
756 }
757 }
758 }
759
760 // DotSummaryForPackage generates the summary.dot file for verbose mode
761 // (-v or -verbose option) that includes all class dependencies.
762 // The summary.dot file shows package-level dependencies.
763 class DotSummaryForPackage extends DotGraph<String> {
764 private String packageOf(String cn) {
765 int i = cn.lastIndexOf('.');
766 return i > 0 ? cn.substring(0, i) : "<unnamed>";
767 }
768 @Override
769 public void visitDependence(String origin, Archive source,
770 String target, Archive archive, Profile profile) {
771 // add a package dependency edge
772 String from = packageOf(origin);
773 String to = packageOf(target);
774 Edge e = addEdge(from, to, getProfileArchiveInfo(archive, profile));
775
776 // add the dependency to the label if enabled and not compact1
777 if (!options.showLabel || profile == Profile.COMPACT1) {
778 return;
779 }
780
781 // trim the package name of origin to shorten the label
782 int i = origin.lastIndexOf('.');
783 String n1 = i < 0 ? origin : origin.substring(i+1);
784 e.addLabel(n1, target, profileName(archive, profile));
785 }
786 @Override
787 public void visitArchiveDependence(Archive origin, Archive target, Profile profile) {
788 // nop
789 }
790 }
791 abstract class DotGraph<T> implements Analyzer.Visitor {
792 private final Set<Edge> edges = new LinkedHashSet<>();
793 private Edge curEdge;
794 public void writeTo(PrintWriter writer) {
795 writer.format("digraph \"summary\" {%n");
796 for (Edge e: edges) {
797 writeEdge(writer, e);
798 }
799 writer.println("}");
800 }
801
802 void writeEdge(PrintWriter writer, Edge e) {
803 writer.format(" %-50s -> \"%s\"%s;%n",
804 String.format("\"%s\"", e.from.toString()),
805 e.tag.isEmpty() ? e.to
806 : String.format("%s (%s)", e.to, e.tag),
807 getLabel(e));
808 }
809
810 Edge addEdge(T origin, T target, String tag) {
811 Edge e = new Edge(origin, target, tag);
812 if (e.equals(curEdge)) {
813 return curEdge;
814 }
815
816 if (edges.contains(e)) {
817 for (Edge e1 : edges) {
818 if (e.equals(e1)) {
819 curEdge = e1;
820 }
821 }
822 } else {
823 edges.add(e);
824 curEdge = e;
825 }
826 return curEdge;
827 }
828
829 Edge findEdge(T origin, T target) {
830 for (Edge e : edges) {
831 if (e.from.equals(origin) && e.to.equals(target)) {
832 return e;
833 }
834 }
835 return null;
836 }
837
838 String getLabel(Edge e) {
839 String label = e.label.toString();
840 return label.isEmpty() ? "" : String.format("[label=\"%s\",fontsize=9]", label);
841 }
842
843 class Edge {
844 final T from;
845 final T to;
846 final String tag; // optional tag
847 final StringBuilder label = new StringBuilder();
848 Edge(T from, T to, String tag) {
849 this.from = from;
850 this.to = to;
851 this.tag = tag;
852 }
853 void addLabel(String s) {
854 label.append(s).append("\\n");
855 }
856 void addLabel(String origin, String target, String profile) {
857 label.append(origin).append(" -> ").append(target);
858 if (!profile.isEmpty()) {
859 label.append(" (" + profile + ")");
860 }
861 label.append("\\n");
862 }
863 @Override @SuppressWarnings("unchecked")
864 public boolean equals(Object o) {
865 if (o instanceof DotGraph<?>.Edge) {
866 DotGraph<?>.Edge e = (DotGraph<?>.Edge)o;
867 return this.from.equals(e.from) &&
868 this.to.equals(e.to) &&
869 this.tag.equals(e.tag);
870 }
871 return false;
872 }
873 @Override
874 public int hashCode() {
875 int hash = 7;
876 hash = 67 * hash + Objects.hashCode(this.from) +
877 Objects.hashCode(this.to) + Objects.hashCode(this.tag);
878 return hash;
879 }
726 } 880 }
727 } 881 }
728 } 882 }

mercurial