Add a few new command line options, and ability to count references between classes and surface as an edge-weight property
authorTim Boudreau <tboudreau@netbeans.org>
Wed, 12 Apr 2017 08:22:38 -0400
changeset 18402f7fc6e6bc36f
parent 18401 74b304e4e503
child 18405 8aa453effa78
child 18408 9a9a96f43e1e
Add a few new command line options, and ability to count references between classes and surface as an edge-weight property
callgraph/src/main/java/org/netbeans/lib/callgraph/Arguments.java
callgraph/src/main/java/org/netbeans/lib/callgraph/Callgraph.java
callgraph/src/main/java/org/netbeans/lib/callgraph/CallgraphControl.java
callgraph/src/main/java/org/netbeans/lib/callgraph/javac/ElementFinder.java
callgraph/src/main/java/org/netbeans/lib/callgraph/javac/SourceElement.java
callgraph/src/main/java/org/netbeans/lib/callgraph/javac/SourcesInfo.java
     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      }