Add a few new command line options, and ability to count references between classes and surface as an edge-weight property
1.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/Arguments.java Wed Apr 12 05:37:50 2017 -0400
1.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/Arguments.java Wed Apr 12 08:22:38 2017 -0400
1.3 @@ -43,6 +43,7 @@
1.4 */
1.5 package org.netbeans.lib.callgraph;
1.6
1.7 +import com.sun.javafx.scene.traversal.WeightedClosestCorner;
1.8 import java.io.File;
1.9 import java.io.IOException;
1.10 import java.util.Arrays;
1.11 @@ -84,7 +85,9 @@
1.12 new AggressiveCommand(),
1.13 new VerboseCommand(),
1.14 new IgnoreShallowPackagesCommand(),
1.15 - new IgnoreAnonymousClassesCommand()
1.16 + new IgnoreAnonymousClassesCommand(),
1.17 + new IncludeWeightsCommand(),
1.18 + new NoOrphansCommand()
1.19 };
1.20
1.21 static {
1.22 @@ -106,6 +109,7 @@
1.23 }
1.24 private boolean ignoreShallowPackages = false;
1.25 private boolean noSelfReferences = false;
1.26 + private boolean weights = false;
1.27 private boolean shortNames = false;
1.28 private boolean maven = false;
1.29 private boolean gradle = false;
1.30 @@ -318,6 +322,10 @@
1.31 public boolean isIgnoreAnonymous() {
1.32 return ignoreAnonymousClasses;
1.33 }
1.34 +
1.35 + public boolean isWeights() {
1.36 + return weights;
1.37 + }
1.38
1.39 private void findMavenSubfolders(List<String> errors) {
1.40 Set<File> flds = new HashSet<>(this.folders);
1.41 @@ -506,6 +514,11 @@
1.42 public boolean isOmitAbstract() {
1.43 return omitAbstract;
1.44 }
1.45 +
1.46 + private boolean noOrphans;
1.47 + public boolean isNoOphans() {
1.48 + return noOrphans;
1.49 + }
1.50
1.51 private static final Pattern ANONYMOUS = Pattern.compile(".*?\\$\\.\\d+.*");
1.52
1.53 @@ -610,6 +623,41 @@
1.54 return name;
1.55 }
1.56 }
1.57 +
1.58 + private static final class NoOrphansCommand extends Command {
1.59 + NoOrphansCommand() {
1.60 + super(CMD_NO_ORPHANS, "N", true, false);
1.61 + }
1.62 +
1.63 + @Override
1.64 + protected int doParse(int i, String[] args, Arguments toSet) {
1.65 + toSet.noOrphans = true;
1.66 + return 1;
1.67 + }
1.68 +
1.69 + @Override
1.70 + protected String help() {
1.71 + return "Do not include elements in the graph which have no "
1.72 + + "connections at all";
1.73 + }
1.74 + }
1.75 +
1.76 + private static final class IncludeWeightsCommand extends Command {
1.77 + IncludeWeightsCommand() {
1.78 + super(CMD_WEIGHTS, "w", true, false);
1.79 + }
1.80 +
1.81 + @Override
1.82 + protected int doParse(int i, String[] args, Arguments toSet) {
1.83 + toSet.weights = true;
1.84 + return 1;
1.85 + }
1.86 +
1.87 + @Override
1.88 + protected String help() {
1.89 + return "Include edge weights based on the number of times one element references another";
1.90 + }
1.91 + }
1.92
1.93 private static final class NoSelfReferencesCommand extends Command {
1.94
2.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/Callgraph.java Wed Apr 12 05:37:50 2017 -0400
2.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/Callgraph.java Wed Apr 12 08:22:38 2017 -0400
2.3 @@ -58,7 +58,6 @@
2.4 import java.util.Collections;
2.5 import java.util.HashSet;
2.6 import java.util.Iterator;
2.7 -import java.util.LinkedHashSet;
2.8 import java.util.LinkedList;
2.9 import java.util.List;
2.10 import java.util.Set;
2.11 @@ -95,8 +94,8 @@
2.12
2.13 private Callgraph() {
2.14 }
2.15 -
2.16 - static final class UH implements Thread.UncaughtExceptionHandler {
2.17 +
2.18 + static final class UH implements Thread.UncaughtExceptionHandler {
2.19
2.20 @Override
2.21 public void uncaughtException(Thread t, Throwable e) {
2.22 @@ -107,7 +106,7 @@
2.23 System.err.flush();
2.24 System.exit(1);
2.25 }
2.26 -
2.27 +
2.28 }
2.29
2.30 /**
2.31 @@ -189,9 +188,11 @@
2.32 List<Object> clazz = new ArrayList<>(5);
2.33 CharSequence lastClass = null;
2.34
2.35 - Set<Object> pkg = new LinkedHashSet<>(5);
2.36 + List<Object> pkg = new ArrayList<>(5);
2.37 CharSequence lastPackage = null;
2.38 SourceElement last = null;
2.39 + final boolean quiet = arguments.isQuiet();
2.40 + List<Object> mth = new ArrayList<>(5);
2.41 try {
2.42 // Iterate every method
2.43 outer:
2.44 @@ -199,16 +200,24 @@
2.45 if (arguments.isExcluded(sce.qname()) || arguments.isExcluded(sce.typeName())) { // Ignore matches
2.46 continue;
2.47 }
2.48 + if (arguments.isOmitAbstract() && sce.isAbstract()) {
2.49 + continue;
2.50 + }
2.51 + if (arguments.isNoOphans() && sce.isOrphan()) {
2.52 + continue;
2.53 + }
2.54 List<SourceElement> outbounds = new ArrayList<>(arguments.isReverse() ? sce.getInboundReferences() : sce.getOutboundReferences());
2.55 Collections.sort(outbounds); // also sort connections
2.56 // Iterate the current method's connections
2.57 - List<Object> mth = new ArrayList<>(5);
2.58 CharSequence currClazz = arguments.isShortNames() ? sce.typeName() : info.strings.concat(sce.packageName(), info.strings.DOT, sce.typeName());
2.59 + if (!mth.isEmpty()) {
2.60 + writeLine(mth, info, new HashSet<>(), outStream, quiet);
2.61 + mth.clear();
2.62 + }
2.63 + mth.add(arguments.isShortNames() ? sce.shortName() : sce.qname());
2.64 if (!currClazz.equals(lastClass)) {
2.65 if (classStream != null) {
2.66 - if (!arguments.isIgnoreAnonymous() || !arguments.isExcluded(currClazz)) {
2.67 - writeLine(clazz, info, emittedClassLines, classStream);
2.68 - }
2.69 + writeLine(clazz, info, emittedClassLines, classStream, quiet);
2.70 }
2.71 clazz.clear();
2.72 lastClass = currClazz;
2.73 @@ -222,7 +231,7 @@
2.74 CharSequence currPkg = sce.packageName();
2.75 if (!currPkg.equals(lastPackage)) {
2.76 if (packageStream != null) {
2.77 - writeLine(pkg, info, emittedPackageLines, packageStream);
2.78 + writeLine(pkg, info, emittedPackageLines, packageStream, quiet);
2.79 }
2.80 lastPackage = currPkg;
2.81 pkg.clear();
2.82 @@ -244,20 +253,28 @@
2.83 if (!arguments.isSelfReferences() && sce.typeName().equals(outbound.typeName())) {
2.84 continue;
2.85 }
2.86 - if (outStream != null || !arguments.isQuiet()) {
2.87 + if (outStream != null) {
2.88 if (arguments.isShortNames()) {
2.89 mth.add(outbound.shortName());
2.90 } else {
2.91 mth.add(outbound.qname());
2.92 }
2.93 - if (arguments.isExtendedProperties()) {
2.94 - mth.add(outbound.isAbstract());
2.95 + if (arguments.isWeights()) {
2.96 + Integer weight = arguments.isReverse() ?
2.97 + sce.inboundCount(outbound) : sce.outboundCount(outbound);
2.98 + mth.add(weight);
2.99 }
2.100 }
2.101 // Build the package graph output if necessary
2.102 if (packageStream != null) {
2.103 if (!outbound.packageName().equals(currPkg) && !pkg.contains(outbound.packageName())) {
2.104 pkg.add(outbound.packageName());
2.105 + if (arguments.isWeights()) {
2.106 + Integer weight = arguments.isReverse() ?
2.107 + sce.inboundPackageCount(outbound)
2.108 + : sce.outboundPackageCount(outbound);
2.109 + pkg.add(weight);
2.110 + }
2.111 }
2.112 }
2.113 // Build the class graph output if necessary
2.114 @@ -272,33 +289,44 @@
2.115 if (arguments.isExtendedProperties()) {
2.116 clazz.add(outbound.isAbstract());
2.117 }
2.118 + if (arguments.isWeights()) {
2.119 + Integer weight = arguments.isReverse() ?
2.120 + sce.inboundTypeCount(outbound)
2.121 + : sce.outboundTypeCount(outbound);
2.122 + clazz.add(weight);
2.123 + }
2.124 }
2.125 }
2.126 }
2.127 - if (!arguments.isQuiet() || outStream != null) {
2.128 - if (!mth.isEmpty()) {
2.129 - CharSequence nm = arguments.isShortNames() ? sce.shortName() : sce.qname();
2.130 - List<Object> l = mth;
2.131 - if (arguments.isExtendedProperties()) {
2.132 - mth.add(0, sce.isAbstract());
2.133 - }
2.134 - l.add(0, nm);
2.135 - CharSequence line = info.strings.concatQuoted(l);
2.136 - if (!arguments.isQuiet()) {
2.137 - System.out.println(line);
2.138 - }
2.139 - if (outStream != null) {
2.140 - outStream.println(line);
2.141 - }
2.142 - }
2.143 - }
2.144 +// if (!arguments.isQuiet() || outStream != null) {
2.145 +// if (!mth.isEmpty()) {
2.146 +// CharSequence nm = arguments.isShortNames() ? sce.shortName() : sce.qname();
2.147 +// List<Object> l = mth;
2.148 +// if (arguments.isExtendedProperties()) {
2.149 +// mth.add(0, sce.isAbstract());
2.150 +// }
2.151 +// l.add(0, nm);
2.152 +// CharSequence line = info.strings.concatQuoted(l);
2.153 +// if (!arguments.isQuiet()) {
2.154 +// System.out.println(line);
2.155 +// }
2.156 +// if (outStream != null) {
2.157 +// outStream.println(line);
2.158 +// }
2.159 +// mth.clear();
2.160 +// }
2.161 +// }
2.162 + }
2.163 + if (outStream != null && !mth.isEmpty()) {
2.164 + writeLine(mth, info, new HashSet<>(), outStream, quiet);
2.165 }
2.166 if (classStream != null && !clazz.isEmpty()) {
2.167 - writeLine(clazz, info, emittedClassLines, classStream);
2.168 + writeLine(clazz, info, emittedClassLines, classStream, quiet);
2.169 }
2.170 if (packageStream != null && !pkg.isEmpty()) {
2.171 - writeLine(pkg, info, emittedPackageLines, packageStream);
2.172 + writeLine(pkg, info, emittedPackageLines, packageStream, quiet);
2.173 }
2.174 +
2.175 } finally {
2.176 for (PrintStream ps : new PrintStream[]{outStream, packageStream, classStream}) {
2.177 if (ps != null) {
2.178 @@ -310,12 +338,15 @@
2.179 return allElements;
2.180 }
2.181
2.182 - private static void writeLine(Collection<Object> clazz, SourcesInfo info, Set<CharSequence> emittedClassLines, PrintStream classStream) {
2.183 + private static void writeLine(Collection<Object> clazz, SourcesInfo info, Set<CharSequence> emittedClassLines, PrintStream classStream, boolean quiet) {
2.184 if (!clazz.isEmpty()) {
2.185 CharSequence cs = info.strings.concatQuoted(clazz);
2.186 if (!emittedClassLines.contains(cs)) {
2.187 classStream.println(cs);
2.188 emittedClassLines.add(cs);
2.189 + if (!quiet) {
2.190 + System.out.println(cs);
2.191 + }
2.192 }
2.193 }
2.194 }
3.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/CallgraphControl.java Wed Apr 12 05:37:50 2017 -0400
3.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/CallgraphControl.java Wed Apr 12 08:22:38 2017 -0400
3.3 @@ -55,6 +55,7 @@
3.4 */
3.5 interface CallgraphControl extends Iterable<File>, FileFilter {
3.6
3.7 + static final String CMD_WEIGHTS = "weights";
3.8 static final String CMD_NOSELF = "noself";
3.9 static final String CMD_SIMPLE = "simple";
3.10 static final String CMD_ANT = "ant";
3.11 @@ -74,6 +75,7 @@
3.12 static final String CMD_REVERSE = "reverse";
3.13 static final String CMD_IGNORE_SINGLE_PACKAGE = "ignore_shallow_packages";
3.14 static final String CMD_IGNORE_ANONYMOUS = "ignore_anonymous";
3.15 + static final String CMD_NO_ORPHANS = "exclude-orphans";
3.16
3.17 boolean isDisableEightBitStrings();
3.18
3.19 @@ -115,5 +117,9 @@
3.20
3.21 boolean isReverse();
3.22
3.23 - public boolean isIgnoreAnonymous();
3.24 + boolean isIgnoreAnonymous();
3.25 +
3.26 + boolean isWeights();
3.27 +
3.28 + boolean isNoOphans();
3.29 }
4.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/ElementFinder.java Wed Apr 12 05:37:50 2017 -0400
4.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/ElementFinder.java Wed Apr 12 08:22:38 2017 -0400
4.3 @@ -50,8 +50,6 @@
4.4 import com.sun.source.util.TreePath;
4.5 import com.sun.source.util.TreeScanner;
4.6 import com.sun.source.util.Trees;
4.7 -import java.util.regex.Matcher;
4.8 -import java.util.regex.Pattern;
4.9 import javax.lang.model.element.Modifier;
4.10 import javax.lang.model.element.Element;
4.11 import javax.lang.model.element.ElementKind;
5.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/SourceElement.java Wed Apr 12 05:37:50 2017 -0400
5.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/SourceElement.java Wed Apr 12 08:22:38 2017 -0400
5.3 @@ -1,4 +1,4 @@
5.4 -/*
5.5 +/*
5.6 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5.7 *
5.8 * Copyright (C) 1997-2015 Oracle and/or its affiliates. All rights reserved.
5.9 @@ -45,9 +45,10 @@
5.10
5.11 import com.sun.source.util.TreePath;
5.12 import java.util.Collections;
5.13 -import java.util.HashSet;
5.14 +import java.util.Map;
5.15 +import java.util.Objects;
5.16 import java.util.Set;
5.17 -import java.util.concurrent.ConcurrentSkipListSet;
5.18 +import java.util.concurrent.ConcurrentHashMap;
5.19 import org.netbeans.lib.callgraph.util.ComparableCharSequence;
5.20
5.21 /**
5.22 @@ -59,8 +60,8 @@
5.23 public final class SourceElement implements Comparable<SourceElement> {
5.24
5.25 private final ComparableCharSequence name;
5.26 - private volatile Set<SourceElement> inbound;
5.27 - private volatile Set<SourceElement> outbound = new HashSet<>();
5.28 + private volatile Map<SourceElement, Integer> outboundCounts;
5.29 + private volatile Map<SourceElement, Integer> inboundCounts;
5.30 private final SourceElementKind kind;
5.31 private final ComparableCharSequence type;
5.32 private final ComparableCharSequence typeName;
5.33 @@ -91,44 +92,119 @@
5.34 public ComparableCharSequence getName() {
5.35 return name;
5.36 }
5.37 +
5.38 + public boolean isOrphan() {
5.39 + return (outboundCounts == null || outboundCounts.isEmpty())
5.40 + && (inboundCounts == null || inboundCounts.isEmpty());
5.41 + }
5.42
5.43 public synchronized Set<SourceElement> getOutboundReferences() {
5.44 - return outbound == null ? EMPTY : Collections.unmodifiableSet(outbound);
5.45 + return outboundCounts == null ? EMPTY : Collections.unmodifiableSet(outboundCounts.keySet());
5.46 }
5.47
5.48 private static final Set<SourceElement> EMPTY = Collections.emptySet();
5.49
5.50 public synchronized Set<SourceElement> getInboundReferences() {
5.51 - return inbound == null ? EMPTY : Collections.unmodifiableSet(inbound);
5.52 + return inboundCounts == null ? EMPTY : Collections.unmodifiableSet(inboundCounts.keySet());
5.53 + }
5.54 +
5.55 + public int inboundCount(SourceElement el) {
5.56 + return inboundCounts == null || !inboundCounts.containsKey(el) ? 0 : inboundCounts.get(el);
5.57 + }
5.58 +
5.59 + public int outboundCount(SourceElement el) {
5.60 + return outboundCounts == null || !outboundCounts.containsKey(el) ? 0 : outboundCounts.get(el);
5.61 + }
5.62 +
5.63 + public int inboundTypeCount(SourceElement typeProvider) {
5.64 + if (inboundCounts == null) {
5.65 + return 0;
5.66 + }
5.67 + return countTypes(typeProvider, inboundCounts);
5.68 + }
5.69 +
5.70 + public int outboundTypeCount(SourceElement typeProvider) {
5.71 + if (outboundCounts == null) {
5.72 + return 0;
5.73 + }
5.74 + return countTypes(typeProvider, outboundCounts);
5.75 + }
5.76 +
5.77 + private int countTypes(SourceElement typeProvider, Map<SourceElement, Integer> in) {
5.78 + int result = 0;
5.79 + for (Map.Entry<SourceElement, Integer> e : in.entrySet()) {
5.80 + if (typeProvider.typeName.equals(e.getKey().typeName)) {
5.81 + if (typeProvider.packageName.equals(e.getKey().packageName)) {
5.82 + result += e.getValue();
5.83 + }
5.84 + }
5.85 + }
5.86 + return result;
5.87 + }
5.88 +
5.89 + public int inboundPackageCount(SourceElement typeProvider) {
5.90 + if (inboundCounts == null) {
5.91 + return 0;
5.92 + }
5.93 + return countPackages(typeProvider, inboundCounts);
5.94 + }
5.95 +
5.96 + public int outboundPackageCount(SourceElement typeProvider) {
5.97 + if (outboundCounts == null) {
5.98 + return 0;
5.99 + }
5.100 + return countPackages(typeProvider, outboundCounts);
5.101 + }
5.102 +
5.103 + private int countPackages(SourceElement typeProvider, Map<SourceElement, Integer> in) {
5.104 + int result = 0;
5.105 + for (Map.Entry<SourceElement, Integer> e : in.entrySet()) {
5.106 + if (typeProvider.packageName.equals(e.getKey().packageName)) {
5.107 + result += e.getValue();
5.108 + }
5.109 + }
5.110 + return result;
5.111 }
5.112
5.113 void addOutboundReference(SourceElement item) {
5.114 if (item != this) {
5.115 - Set<SourceElement> outbound = this.outbound;
5.116 + Map<SourceElement, Integer> outbound = this.outboundCounts;
5.117 if (outbound == null) {
5.118 synchronized (this) {
5.119 - outbound = this.outbound;
5.120 + outbound = this.outboundCounts;
5.121 if (outbound == null) {
5.122 - this.outbound = outbound = new ConcurrentSkipListSet<>();
5.123 + this.outboundCounts = outbound = new ConcurrentHashMap<>();
5.124 }
5.125 }
5.126 }
5.127 - outbound.add(item);
5.128 + Integer val = outbound.get(item);
5.129 + if (val == null) {
5.130 + val = 1;
5.131 + } else {
5.132 + val = val + 1;
5.133 + }
5.134 + outbound.put(item, val);
5.135 }
5.136 }
5.137
5.138 synchronized void addInboundReference(SourceElement item) {
5.139 if (item != this) {
5.140 - Set<SourceElement> inbound = this.inbound;
5.141 + Map<SourceElement, Integer> inbound = this.inboundCounts;
5.142 if (inbound == null) {
5.143 synchronized (this) {
5.144 - inbound = this.inbound;
5.145 + inbound = this.inboundCounts;
5.146 if (inbound == null) {
5.147 - this.inbound = inbound = new ConcurrentSkipListSet<>();
5.148 + this.inboundCounts = inbound = new ConcurrentHashMap<>();
5.149 }
5.150 }
5.151 }
5.152 - inbound.add(item);
5.153 + Integer val = inbound.get(item);
5.154 + if (val == null) {
5.155 + val = 1;
5.156 + } else {
5.157 + val = val + 1;
5.158 + }
5.159 + inbound.put(item, val);
5.160 }
5.161 }
5.162
5.163 @@ -189,4 +265,45 @@
5.164 public int compareTo(SourceElement o) {
5.165 return qname.compareTo(o.qname);
5.166 }
5.167 +
5.168 + static class Ref {
5.169 +
5.170 + public SourceElement element;
5.171 + public int count;
5.172 +
5.173 + public Ref(SourceElement element, int count) {
5.174 + this.element = element;
5.175 + this.count = count;
5.176 + }
5.177 +
5.178 + public String toString() {
5.179 + return element + "(" + count + ")";
5.180 + }
5.181 +
5.182 + @Override
5.183 + public int hashCode() {
5.184 + int hash = 7;
5.185 + hash = 59 * hash + Objects.hashCode(this.element);
5.186 + return hash;
5.187 + }
5.188 +
5.189 + @Override
5.190 + public boolean equals(Object obj) {
5.191 + if (this == obj) {
5.192 + return true;
5.193 + }
5.194 + if (obj == null) {
5.195 + return false;
5.196 + }
5.197 + if (getClass() != obj.getClass()) {
5.198 + return false;
5.199 + }
5.200 + final Ref other = (Ref) obj;
5.201 + if (!Objects.equals(this.element, other.element)) {
5.202 + return false;
5.203 + }
5.204 + return true;
5.205 + }
5.206 +
5.207 + }
5.208 }
6.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/SourcesInfo.java Wed Apr 12 05:37:50 2017 -0400
6.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/SourcesInfo.java Wed Apr 12 08:22:38 2017 -0400
6.3 @@ -85,10 +85,10 @@
6.4
6.5 public SourcesInfo(boolean eightBitStringsDisabled, boolean aggressive) {
6.6 this.strings = new EightBitStrings(eightBitStringsDisabled, aggressive);
6.7 - DOLLARS_DOT = strings.create("$.");
6.8 + DOLLARS_DOT = strings.create("$");
6.9 DEFAULT_PACKAGE = strings.create("<defaultPackage>");
6.10 OPEN_PARENS = strings.create("(");
6.11 - CLOSE_PARENS = strings.create("}");
6.12 + CLOSE_PARENS = strings.create(")");
6.13 OPEN_CLOSE_PARENS = strings.create("()");
6.14 COMMA = strings.create(",");
6.15 }