1.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/Arguments.java Sat Sep 03 02:41:36 2016 -0400
1.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/Arguments.java Wed Apr 12 05:37:43 2017 -0400
1.3 @@ -45,12 +45,17 @@
1.4
1.5 import java.io.File;
1.6 import java.io.IOException;
1.7 +import java.util.Arrays;
1.8 import java.util.Collections;
1.9 import java.util.HashSet;
1.10 import java.util.Iterator;
1.11 import java.util.LinkedList;
1.12 import java.util.List;
1.13 import java.util.Set;
1.14 +import java.util.concurrent.ConcurrentSkipListSet;
1.15 +import java.util.regex.Matcher;
1.16 +import java.util.regex.Pattern;
1.17 +import org.netbeans.lib.callgraph.util.ComparableCharSequence;
1.18
1.19 /**
1.20 * Parses and validates command-line arguments.
1.21 @@ -59,8 +64,8 @@
1.22 */
1.23 final class Arguments implements CallgraphControl {
1.24
1.25 - private Set<File> folders = new HashSet<>();
1.26 - private static final Command[] commands = new Command[]{
1.27 + private Set<File> folders = new ConcurrentSkipListSet<>();
1.28 + private static final Command[] COMMANDS = new Command[]{
1.29 new NoSelfReferencesCommand(),
1.30 new ShortNamesCommand(),
1.31 new ExtendedPropertiesCommand(),
1.32 @@ -74,12 +79,32 @@
1.33 new ClassGraphFileCommand(),
1.34 new OmitAbstractCommand(),
1.35 new DisableEightBitStringsCommand(),
1.36 - new OmitAbstractCommand(),
1.37 new QuietCommand(),
1.38 new ReverseCommand(),
1.39 new AggressiveCommand(),
1.40 - new VerboseCommand()
1.41 + new VerboseCommand(),
1.42 + new IgnoreShallowPackagesCommand(),
1.43 + new IgnoreAnonymousClassesCommand()
1.44 };
1.45 +
1.46 + static {
1.47 + if (new HashSet<>(Arrays.asList(COMMANDS)).size() != COMMANDS.length) {
1.48 + throw new AssertionError("Command list contains duplicates");
1.49 + }
1.50 + //sanity check
1.51 + for (int i = 0; i < COMMANDS.length; i++) {
1.52 + Command ca = COMMANDS[i];
1.53 + for (int j = i + 1; j < COMMANDS.length; j++) {
1.54 + Command cb = COMMANDS[j];
1.55 + if (ca.shortcut.equals(cb.shortcut)) {
1.56 + throw new AssertionError("Conflicting shortcut '" + ca.shortcut
1.57 + + "' for " + ca.getClass().getSimpleName() + " and "
1.58 + + cb.getClass().getSimpleName());
1.59 + }
1.60 + }
1.61 + }
1.62 + }
1.63 + private boolean ignoreShallowPackages = false;
1.64 private boolean noSelfReferences = false;
1.65 private boolean shortNames = false;
1.66 private boolean maven = false;
1.67 @@ -97,6 +122,8 @@
1.68 private boolean omitAbstract;
1.69 private boolean disableEightBitStrings;
1.70 private boolean reverse;
1.71 + final Set<File> ignoreFolders = new HashSet<>();
1.72 + private boolean ignoreAnonymousClasses;
1.73
1.74 Arguments(String... args) throws IOException {
1.75 this(true, args);
1.76 @@ -107,7 +134,7 @@
1.77 List<String> errors = new LinkedList<>();
1.78 for (int i = 0; i < args.length;) {
1.79 int oldPos = i;
1.80 - for (Command c : commands) {
1.81 + for (Command c : COMMANDS) {
1.82 try {
1.83 @SuppressWarnings("LeakingThisInConstructor")
1.84 int increment = c.parse(i, args, this);
1.85 @@ -147,6 +174,9 @@
1.86 }
1.87 }
1.88 }
1.89 + if (verbose && (maven || gradle || ant)) {
1.90 + System.err.println("Scanning for projects in " + this.folders());
1.91 + }
1.92 if ((maven && gradle) || (ant && gradle) || (ant && maven)) {
1.93 errors.add("--maven, --ant and --gradle are mutually exclusive");
1.94 } else if (maven) {
1.95 @@ -160,7 +190,9 @@
1.96 for (String ig : ignore) {
1.97 File ff = new File(ig);
1.98 if (ff.exists() && ff.isDirectory()) {
1.99 + ignoreFolders.add(ff);
1.100 ff = ff.getCanonicalFile();
1.101 + ignoreFolders.add(ff);
1.102 for (File f : this.folders()) {
1.103 File f1 = f.getCanonicalFile();
1.104 if (f1.equals(ff)) {
1.105 @@ -193,46 +225,100 @@
1.106 }
1.107 }
1.108 }
1.109 - if (verbose && !toIgnore.isEmpty()) {
1.110 - System.err.println("Ignoring the following projects:");
1.111 - for (File ti : toIgnore) {
1.112 - System.err.println(" - " + ti.getAbsolutePath());
1.113 + if (verbose) {
1.114 + if (toIgnore.isEmpty()) {
1.115 + System.err.println("Not ignoring any folders");
1.116 + } else {
1.117 + System.err.println("Ignoring " + toIgnore.size() + " folders due to -i");
1.118 }
1.119 }
1.120 +// if (verbose && !toIgnore.isEmpty()) {
1.121 +// System.err.println("Ignoring the following projects:");
1.122 +// for (File ti : toIgnore) {
1.123 +// System.err.println(" - " + ti.getAbsolutePath());
1.124 +// }
1.125 +// }
1.126 this.folders.removeAll(toIgnore);
1.127 if (verbose && !this.folders.isEmpty()) {
1.128 System.err.println("Will scan the following source roots:");
1.129 - for (File f : folders()) {
1.130 - System.err.println(" " + f.getAbsolutePath());
1.131 + StringBuilder sb = new StringBuilder();
1.132 +
1.133 + for (Iterator<File> it = folders().iterator(); it.hasNext();) {
1.134 + File f = it.next();
1.135 + sb.append(f.getAbsolutePath());
1.136 + if (it.hasNext()) {
1.137 + sb.append(", ");
1.138 + }
1.139 + }
1.140 + System.err.println(sb);
1.141 + if (maven || gradle || ant) {
1.142 + System.err.println("Found " + folders().size() + " source roots.");
1.143 }
1.144 }
1.145
1.146 if (packageGraphFile != null) {
1.147 File parent = packageGraphFile.getParentFile();
1.148 - if (!parent.exists() || !parent.isDirectory()) {
1.149 + if (parent == null || !parent.exists() || !parent.isDirectory()) {
1.150 errors.add("Parent folder for package graph output file does not exist: " + parent);
1.151 }
1.152 }
1.153 if (classGraphFile != null) {
1.154 File parent = classGraphFile.getParentFile();
1.155 - if (!parent.exists() || !parent.isDirectory()) {
1.156 + if (parent == null || !parent.exists() || !parent.isDirectory()) {
1.157 errors.add("Parent folder for class graph output file does not exist: " + parent);
1.158 }
1.159 }
1.160 if (outfile != null) {
1.161 File parent = outfile.getParentFile();
1.162 - if (!parent.exists() || !parent.isDirectory()) {
1.163 + if (parent == null || !parent.exists() || !parent.isDirectory()) {
1.164 errors.add("Parent folder for output file does not exist: " + parent);
1.165 }
1.166 } else if (abortIfWillNotOutput && quiet && packageGraphFile == null && classGraphFile == null) {
1.167 errors.add("-q or --quiet specified, but no output file specified - would not produce any output at all");
1.168 }
1.169 - // XXX check if any folders are children of each other?
1.170 + // Ensure no folders are nested inside each other
1.171 + Set<File> all = new HashSet<>(this.folders);
1.172 + int children = 0;
1.173 + for (File f1 : all) {
1.174 + for (File f2 : all) {
1.175 + if (f1 == f2) {
1.176 + continue;
1.177 + }
1.178 + if (f2.getAbsolutePath().startsWith(f1.getAbsolutePath())) {
1.179 + children++;
1.180 + this.folders.remove(f2);
1.181 + }
1.182 + }
1.183 + }
1.184 + if (verbose) {
1.185 + System.err.println("Pruned " + children + " due to being children of other folders");
1.186 + }
1.187 if (!errors.isEmpty()) {
1.188 throw new InvalidArgumentsException(help(errors), errors);
1.189 }
1.190 }
1.191
1.192 + @Override
1.193 + public boolean accept(File f) {
1.194 + if (ignoreFolders.contains(f)) {
1.195 + return false;
1.196 + }
1.197 + for (File ig : ignoreFolders) {
1.198 + if (f.getAbsolutePath().startsWith(ig.getAbsolutePath())) {
1.199 + return false;
1.200 + }
1.201 + }
1.202 + return true;
1.203 + }
1.204 +
1.205 + public boolean isIgnoreAbstract() {
1.206 + return omitAbstract;
1.207 + }
1.208 +
1.209 + public boolean isIgnoreAnonymous() {
1.210 + return ignoreAnonymousClasses;
1.211 + }
1.212 +
1.213 private void findMavenSubfolders(List<String> errors) {
1.214 Set<File> flds = new HashSet<>(this.folders);
1.215 this.folders.clear();
1.216 @@ -282,8 +368,15 @@
1.217 this.folders.add(sources);
1.218 }
1.219 }
1.220 - for (File child : file.listFiles()) {
1.221 - recurseSubfoldersLookingForAntProjects(child);
1.222 + List<File> kids = Arrays.asList(file.listFiles(File::isDirectory));
1.223 + if (kids.size() > 5) {
1.224 + for (File child : file.listFiles()) {
1.225 + recurseSubfoldersLookingForAntProjects(child);
1.226 + }
1.227 + } else {
1.228 + kids.parallelStream().forEach((File f) -> {
1.229 + recurseSubfoldersLookingForAntProjects(f);
1.230 + });
1.231 }
1.232 }
1.233 }
1.234 @@ -385,6 +478,7 @@
1.235 return xprop;
1.236 }
1.237
1.238 + @Override
1.239 public boolean isAggressive() {
1.240 return aggressive;
1.241 }
1.242 @@ -413,9 +507,29 @@
1.243 return omitAbstract;
1.244 }
1.245
1.246 - public boolean isExcluded(String qname) {
1.247 - for (String ex : exclude) {
1.248 - if (qname.startsWith(ex)) {
1.249 + private static final Pattern ANONYMOUS = Pattern.compile(".*?\\$\\.\\d+.*");
1.250 +
1.251 + public boolean isExcluded(CharSequence qname) {
1.252 + if (exclude.size() > 0) {
1.253 + if (qname instanceof ComparableCharSequence) {
1.254 + ComparableCharSequence ccs = (ComparableCharSequence) qname;
1.255 + for (String ex : exclude) {
1.256 + if (ccs.startsWith(ex)) {
1.257 + return true;
1.258 + }
1.259 + }
1.260 + } else {
1.261 + String qn = qname.toString();
1.262 + for (String ex : exclude) {
1.263 + if (qn.startsWith(ex)) {
1.264 + return true;
1.265 + }
1.266 + }
1.267 + }
1.268 + }
1.269 + if (isIgnoreAnonymous()) {
1.270 + Matcher m = ANONYMOUS.matcher(qname);
1.271 + if (m.find()) {
1.272 return true;
1.273 }
1.274 }
1.275 @@ -432,7 +546,7 @@
1.276 + "and can output graphs of what methods / classes / packages (or all of the above) call each other\nwithin that"
1.277 + "source tree."
1.278 + "\n\nUsage:\njava -jar callgraph.jar ");
1.279 - for (Command c : commands) {
1.280 + for (Command c : COMMANDS) {
1.281 if (c.optional) {
1.282 sb.append('[');
1.283 }
1.284 @@ -447,7 +561,7 @@
1.285 }
1.286 sb.append("dir1 [dir2 dir3 ...]");
1.287 sb.append('\n');
1.288 - for (Command c : commands) {
1.289 + for (Command c : COMMANDS) {
1.290 sb.append("\n\t");
1.291 sb.append("--").append(c.name).append(" / -").append(c.shortcut).append(" :\t").append(c.help());
1.292 }
1.293 @@ -460,6 +574,11 @@
1.294 return sb.toString();
1.295 }
1.296
1.297 + @Override
1.298 + public boolean isIgnoreSinglePackage() {
1.299 + return ignoreShallowPackages;
1.300 + }
1.301 +
1.302 private static abstract class Command {
1.303
1.304 protected final String name;
1.305 @@ -658,7 +777,7 @@
1.306 private static final class OmitAbstractCommand extends Command {
1.307
1.308 OmitAbstractCommand() {
1.309 - super(CMD_OMIT_ABSTRACT, "a", true, false);
1.310 + super(CMD_OMIT_ABSTRACT, "b", true, false);
1.311 }
1.312
1.313 @Override
1.314 @@ -691,6 +810,44 @@
1.315 }
1.316 }
1.317
1.318 + private static final class IgnoreShallowPackagesCommand extends Command {
1.319 +
1.320 + IgnoreShallowPackagesCommand() {
1.321 + super(CMD_IGNORE_SINGLE_PACKAGE, "h", true, false);
1.322 + }
1.323 +
1.324 + @Override
1.325 + protected int doParse(int i, String[] args, Arguments toSet) {
1.326 + toSet.ignoreShallowPackages = true;
1.327 + return 1;
1.328 + }
1.329 +
1.330 + @Override
1.331 + protected String help() {
1.332 + return "Ignore classes in packages just below the default package, "
1.333 + + "a pattern frequently used in demo code.";
1.334 + }
1.335 + }
1.336 +
1.337 + private static final class IgnoreAnonymousClassesCommand extends Command {
1.338 +
1.339 + IgnoreAnonymousClassesCommand() {
1.340 + super(CMD_IGNORE_ANONYMOUS, "y", true, false);
1.341 + }
1.342 +
1.343 + @Override
1.344 + protected int doParse(int i, String[] args, Arguments toSet) {
1.345 + toSet.ignoreAnonymousClasses = true;
1.346 + return 1;
1.347 + }
1.348 +
1.349 + @Override
1.350 + protected String help() {
1.351 + return "Ignore classes in packages just below the default package, "
1.352 + + "a pattern frequently used in demo code.";
1.353 + }
1.354 + }
1.355 +
1.356 private static final class QuietCommand extends Command {
1.357
1.358 QuietCommand() {
1.359 @@ -705,7 +862,7 @@
1.360
1.361 @Override
1.362 protected String help() {
1.363 - return "Supress writing the graph to the standard output";
1.364 + return "Ignore anonymous classes, e.g. com.foo.Bar.$4";
1.365 }
1.366 }
1.367
1.368 @@ -785,6 +942,7 @@
1.369 }
1.370 for (String s : args[i + 1].split(",")) {
1.371 toSet.exclude.add(s);
1.372 + System.out.println("EXCLUDE '" + s + "'");
1.373 }
1.374 return 2;
1.375 }
1.376 @@ -807,7 +965,7 @@
1.377 throw new IllegalArgumentException("--exclude or -e present but no exclusion list present");
1.378 }
1.379 for (String s : args[i + 1].split(",")) {
1.380 - toSet.ignore.add(s);
1.381 + toSet.ignore.add(s.trim());
1.382 }
1.383 return 2;
1.384 }
2.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/Callgraph.java Sat Sep 03 02:41:36 2016 -0400
2.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/Callgraph.java Wed Apr 12 05:37:43 2017 -0400
2.3 @@ -1,4 +1,4 @@
2.4 -/*
2.5 +/*
2.6 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2.7 *
2.8 * Copyright (C) 1997-2016 Oracle and/or its affiliates. All rights reserved.
2.9 @@ -44,17 +44,7 @@
2.10 package org.netbeans.lib.callgraph;
2.11
2.12 import org.netbeans.lib.callgraph.Arguments.InvalidArgumentsException;
2.13 -import static org.netbeans.lib.callgraph.CallgraphControl.CMD_CLASSGRAPH;
2.14 -import static org.netbeans.lib.callgraph.CallgraphControl.CMD_DISABLE_EIGHT_BIT_STRINGS;
2.15 -import static org.netbeans.lib.callgraph.CallgraphControl.CMD_EXCLUDE;
2.16 -import static org.netbeans.lib.callgraph.CallgraphControl.CMD_MAVEN;
2.17 -import static org.netbeans.lib.callgraph.CallgraphControl.CMD_METHODGRAPH;
2.18 -import static org.netbeans.lib.callgraph.CallgraphControl.CMD_NOSELF;
2.19 -import static org.netbeans.lib.callgraph.CallgraphControl.CMD_OMIT_ABSTRACT;
2.20 -import static org.netbeans.lib.callgraph.CallgraphControl.CMD_PACKAGEGRAPH;
2.21 -import static org.netbeans.lib.callgraph.CallgraphControl.CMD_QUIET;
2.22 -import static org.netbeans.lib.callgraph.CallgraphControl.CMD_REVERSE;
2.23 -import static org.netbeans.lib.callgraph.CallgraphControl.CMD_SIMPLE;
2.24 +import static org.netbeans.lib.callgraph.CallgraphControl.*;
2.25 import org.netbeans.lib.callgraph.io.JavaFilesIterator;
2.26 import org.netbeans.lib.callgraph.javac.JavacRunner;
2.27 import org.netbeans.lib.callgraph.javac.SourceElement;
2.28 @@ -64,12 +54,17 @@
2.29 import java.io.IOException;
2.30 import java.io.PrintStream;
2.31 import java.util.ArrayList;
2.32 +import java.util.Collection;
2.33 import java.util.Collections;
2.34 import java.util.HashSet;
2.35 import java.util.Iterator;
2.36 +import java.util.LinkedHashSet;
2.37 import java.util.LinkedList;
2.38 import java.util.List;
2.39 import java.util.Set;
2.40 +import java.util.TreeSet;
2.41 +import java.util.concurrent.ExecutionException;
2.42 +import java.util.concurrent.ForkJoinPool;
2.43 import java.util.concurrent.atomic.AtomicReference;
2.44 import java.util.function.Consumer;
2.45
2.46 @@ -100,6 +95,20 @@
2.47
2.48 private Callgraph() {
2.49 }
2.50 +
2.51 + static final class UH implements Thread.UncaughtExceptionHandler {
2.52 +
2.53 + @Override
2.54 + public void uncaughtException(Thread t, Throwable e) {
2.55 + if (e instanceof ExecutionException && e.getCause() != null) {
2.56 + e = e.getCause();
2.57 + }
2.58 + e.printStackTrace(System.err);
2.59 + System.err.flush();
2.60 + System.exit(1);
2.61 + }
2.62 +
2.63 + }
2.64
2.65 /**
2.66 * Configure a Callgraph to build.
2.67 @@ -110,18 +119,26 @@
2.68 return new Callgraph();
2.69 }
2.70
2.71 - public static void main(String[] args) throws IOException {
2.72 - CallgraphControl arguments = null;
2.73 - try {
2.74 - arguments = new Arguments(args);
2.75 - } catch (InvalidArgumentsException ex) {
2.76 - // this will be a help message describing usage and the invalid
2.77 - // arguments
2.78 - System.err.println(ex.getMessage());
2.79 - System.exit(1);
2.80 - }
2.81 - assert arguments != null;
2.82 - invoke(arguments, arguments.isVerbose() ? new LoggingListener() : null);
2.83 + public static void main(String[] args) throws Exception {
2.84 + int threads = Runtime.getRuntime().availableProcessors() * 4;
2.85 + // Threads will spend most of their time blocked waiting for the
2.86 + // I/O controller to shovel data from disk, so we want more threads
2.87 + // than we actually have CPUs
2.88 + new ForkJoinPool(threads, ForkJoinPool.defaultForkJoinWorkerThreadFactory, new UH(), false)
2.89 + .submit(() -> {
2.90 + CallgraphControl arguments = null;
2.91 + try {
2.92 + arguments = new Arguments(args);
2.93 + } catch (InvalidArgumentsException ex) {
2.94 + // this will be a help message describing usage and the invalid
2.95 + // arguments
2.96 + System.err.println(ex.getMessage());
2.97 + System.exit(1);
2.98 + }
2.99 + assert arguments != null;
2.100 + invoke(arguments, arguments.isVerbose() ? new LoggingListener() : null);
2.101 + return null;
2.102 + }).get();
2.103 }
2.104
2.105 /**
2.106 @@ -131,25 +148,38 @@
2.107 * @return The list of all methods found, sorted by qname
2.108 * @throws IOException If i/o fails
2.109 */
2.110 - static List<SourceElement> invoke(CallgraphControl arguments, Listener listener) throws IOException {
2.111 + static Collection<SourceElement> invoke(CallgraphControl arguments, Listener listener) throws IOException {
2.112 SourcesInfo info = new SourcesInfo(arguments.isDisableEightBitStrings(), arguments.isAggressive());
2.113 // Build an iterable of all Java sources (without collecting them all ahead of time)
2.114 List<Iterable<File>> iterables = new LinkedList<>();
2.115 for (File folder : arguments) {
2.116 - iterables.add(JavaFilesIterator.iterable(folder));
2.117 + iterables.add(JavaFilesIterator.iterable(folder, arguments));
2.118 }
2.119 // The thing that will run javac
2.120 - JavacRunner runner = new JavacRunner(info, MergeIterator.toIterable(iterables), listener);
2.121 + JavacRunner runner = new JavacRunner(info, MergeIterator.toIterable(iterables), listener, arguments.isIgnoreSinglePackage(), arguments.isIgnoreAbstract(), arguments.isIgnoreAnonymous());
2.122 AtomicReference<File> lastFile = new AtomicReference<>();
2.123 - Consumer<File> monitor = lastFile::set;
2.124 + int[] count = new int[1];
2.125 + Consumer<File> monitor = new Consumer<File>() { //lastFile::set;
2.126 +
2.127 + @Override
2.128 + public void accept(File t) {
2.129 + lastFile.set(t);
2.130 + if (listener != null && count[0] % 100 == 0) {
2.131 + listener.onStep("Scanned " + count[0] + " source files...");
2.132 + }
2.133 + count[0]++;
2.134 + }
2.135 + };
2.136 +
2.137 + if (listener != null) {
2.138 + listener.onStep("Scan " + arguments.folders().size() + " source roots.");
2.139 + }
2.140 // run javac
2.141 Set<SourceElement> allElements = runner.go(monitor, lastFile);
2.142 -
2.143 - List<SourceElement> all = new ArrayList<>(allElements);
2.144 - // Sort, so textual output is more human-friendly
2.145 - Collections.sort(all);
2.146 + allElements = new TreeSet<>(allElements);
2.147 +// Map<CharSequence, List<Object>> packageLineForCharSequence = new TreeMap<>();
2.148 // Now write files and print output
2.149 - if (!all.isEmpty()) {
2.150 + if (!allElements.isEmpty()) {
2.151 PrintStream outStream = createPrintStreamIfNotNull(arguments.methodGraphFile());
2.152 PrintStream packageStream = createPrintStreamIfNotNull(arguments.packageGraphFile());
2.153 PrintStream classStream = createPrintStreamIfNotNull(arguments.classGraphFile());
2.154 @@ -159,14 +189,14 @@
2.155 List<Object> clazz = new ArrayList<>(5);
2.156 CharSequence lastClass = null;
2.157
2.158 - List<Object> pkg = new ArrayList<>(5);
2.159 + Set<Object> pkg = new LinkedHashSet<>(5);
2.160 CharSequence lastPackage = null;
2.161 -
2.162 + SourceElement last = null;
2.163 try {
2.164 // Iterate every method
2.165 outer:
2.166 - for (SourceElement sce : all) {
2.167 - if (arguments.isExcluded(sce.qname().toString())) { // Ignore matches
2.168 + for (SourceElement sce : allElements) {
2.169 + if (arguments.isExcluded(sce.qname()) || arguments.isExcluded(sce.typeName())) { // Ignore matches
2.170 continue;
2.171 }
2.172 List<SourceElement> outbounds = new ArrayList<>(arguments.isReverse() ? sce.getInboundReferences() : sce.getOutboundReferences());
2.173 @@ -176,7 +206,9 @@
2.174 CharSequence currClazz = arguments.isShortNames() ? sce.typeName() : info.strings.concat(sce.packageName(), info.strings.DOT, sce.typeName());
2.175 if (!currClazz.equals(lastClass)) {
2.176 if (classStream != null) {
2.177 - writeLine(clazz, info, emittedClassLines, classStream);
2.178 + if (!arguments.isIgnoreAnonymous() || !arguments.isExcluded(currClazz)) {
2.179 + writeLine(clazz, info, emittedClassLines, classStream);
2.180 + }
2.181 }
2.182 clazz.clear();
2.183 lastClass = currClazz;
2.184 @@ -188,9 +220,6 @@
2.185 }
2.186 }
2.187 CharSequence currPkg = sce.packageName();
2.188 - if (pkg.isEmpty()) {
2.189 - pkg.add(currPkg);
2.190 - }
2.191 if (!currPkg.equals(lastPackage)) {
2.192 if (packageStream != null) {
2.193 writeLine(pkg, info, emittedPackageLines, packageStream);
2.194 @@ -198,8 +227,12 @@
2.195 lastPackage = currPkg;
2.196 pkg.clear();
2.197 }
2.198 + if (pkg.isEmpty()) {
2.199 + pkg.add(currPkg);
2.200 + }
2.201 + last = sce;
2.202 for (SourceElement outbound : outbounds) {
2.203 - if (arguments.isExcluded(outbound.qname().toString())) { // Ignore matches
2.204 + if (arguments.isExcluded(outbound.qname()) || arguments.isExcluded(outbound.typeName())) { // Ignore matches
2.205 continue;
2.206 }
2.207 // If we are ignoring abstract methods, do that - has no effect on classes
2.208 @@ -223,7 +256,7 @@
2.209 }
2.210 // Build the package graph output if necessary
2.211 if (packageStream != null) {
2.212 - if (!outbound.packageName().equals(currPkg) || arguments.isSelfReferences()) {
2.213 + if (!outbound.packageName().equals(currPkg) && !pkg.contains(outbound.packageName())) {
2.214 pkg.add(outbound.packageName());
2.215 }
2.216 }
2.217 @@ -232,7 +265,6 @@
2.218 CharSequence type1 = sce.typeName();
2.219 CharSequence type2 = outbound.typeName();
2.220 if (!arguments.isShortNames()) {
2.221 -// type1 = info.strings.concat(sce.packageName(), info.strings.DOT, type1);
2.222 type2 = info.strings.concat(outbound.packageName(), info.strings.DOT, type2);
2.223 }
2.224 if (!type1.equals(type2) && !clazz.contains(type2)) {
2.225 @@ -275,9 +307,10 @@
2.226 }
2.227 }
2.228 }
2.229 - return all;
2.230 + return allElements;
2.231 }
2.232 - private static void writeLine(List<Object> clazz, SourcesInfo info, Set<CharSequence> emittedClassLines, PrintStream classStream) {
2.233 +
2.234 + private static void writeLine(Collection<Object> clazz, SourcesInfo info, Set<CharSequence> emittedClassLines, PrintStream classStream) {
2.235 if (!clazz.isEmpty()) {
2.236 CharSequence cs = info.strings.concatQuoted(clazz);
2.237 if (!emittedClassLines.contains(cs)) {
2.238 @@ -346,7 +379,7 @@
2.239 * @return Sorted set of source elements
2.240 * @throws IOException If i/o fails
2.241 */
2.242 - public List<SourceElement> run() throws IOException {
2.243 + public Collection<SourceElement> run() throws IOException {
2.244 return Callgraph.invoke(build(), listener);
2.245 }
2.246
2.247 @@ -437,19 +470,20 @@
2.248
2.249 @Override
2.250 public void onFinish() {
2.251 - System.out.println("Done.");
2.252 + System.err.println("Done.");
2.253 }
2.254
2.255 @Override
2.256 - public void onStartActivity(String activity, int steps) {
2.257 - if (steps > 0) {
2.258 - System.out.println(activity + " (" + steps + " steps)");
2.259 - }
2.260 + public void onStartActivity(CharSequence activity, int steps) {
2.261 +// if (steps > 0) {
2.262 +// System.err.println(activity + " (" + steps + " steps)");
2.263 +// }
2.264 + System.err.println(activity);
2.265 }
2.266
2.267 @Override
2.268 - public void onStep(String step) {
2.269 - System.out.println("\t" + step);
2.270 + public void onStep(CharSequence step) {
2.271 + System.err.println("\t" + step);
2.272 }
2.273 }
2.274 }
3.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/CallgraphControl.java Sat Sep 03 02:41:36 2016 -0400
3.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/CallgraphControl.java Wed Apr 12 05:37:43 2017 -0400
3.3 @@ -45,7 +45,7 @@
3.4 package org.netbeans.lib.callgraph;
3.5
3.6 import java.io.File;
3.7 -import java.util.Iterator;
3.8 +import java.io.FileFilter;
3.9 import java.util.Set;
3.10
3.11 /**
3.12 @@ -53,7 +53,7 @@
3.13 *
3.14 * @author Tim Boudreau
3.15 */
3.16 -interface CallgraphControl extends Iterable<File> {
3.17 +interface CallgraphControl extends Iterable<File>, FileFilter {
3.18
3.19 static final String CMD_NOSELF = "noself";
3.20 static final String CMD_SIMPLE = "simple";
3.21 @@ -72,6 +72,8 @@
3.22 static final String CMD_OMIT_ABSTRACT = "omit_abstract";
3.23 static final String CMD_DISABLE_EIGHT_BIT_STRINGS = "use_java_strings";
3.24 static final String CMD_REVERSE = "reverse";
3.25 + static final String CMD_IGNORE_SINGLE_PACKAGE = "ignore_shallow_packages";
3.26 + static final String CMD_IGNORE_ANONYMOUS = "ignore_anonymous";
3.27
3.28 boolean isDisableEightBitStrings();
3.29
3.30 @@ -97,15 +99,21 @@
3.31
3.32 boolean isShortNames();
3.33
3.34 + boolean isIgnoreSinglePackage();
3.35 +
3.36 + boolean isIgnoreAbstract();
3.37 +
3.38 File methodGraphFile();
3.39
3.40 File packageGraphFile();
3.41
3.42 - boolean isExcluded(String qname);
3.43 + boolean isExcluded(CharSequence qname);
3.44
3.45 boolean isVerbose();
3.46
3.47 boolean isOmitAbstract();
3.48
3.49 boolean isReverse();
3.50 +
3.51 + public boolean isIgnoreAnonymous();
3.52 }
4.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/Listener.java Sat Sep 03 02:41:36 2016 -0400
4.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/Listener.java Wed Apr 12 05:37:43 2017 -0400
4.3 @@ -56,7 +56,7 @@
4.4
4.5 void onFinish();
4.6
4.7 - void onStartActivity(String activity, int steps);
4.8 + void onStartActivity(CharSequence activity, int steps);
4.9
4.10 - void onStep(String step);
4.11 + void onStep(CharSequence step);
4.12 }
5.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/io/JavaFilesIterator.java Sat Sep 03 02:41:36 2016 -0400
5.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/io/JavaFilesIterator.java Wed Apr 12 05:37:43 2017 -0400
5.3 @@ -62,18 +62,23 @@
5.4
5.5 private final LinkedList<Iterator<File>> stack = new LinkedList<>();
5.6 private File next;
5.7 + private final FileFilter ignoreFilter;
5.8
5.9 - public JavaFilesIterator(File root) throws IOException {
5.10 + public JavaFilesIterator(File root, FileFilter ignoreFilter) throws IOException {
5.11 + if (ignoreFilter == null) {
5.12 + throw new IllegalArgumentException("Null filter");
5.13 + }
5.14 + this.ignoreFilter = ignoreFilter;
5.15 Iterator<File> base = list(root);
5.16 stack.push(base);
5.17 }
5.18
5.19 - public static Iterable<File> iterable(final File root) {
5.20 + public static Iterable<File> iterable(final File root, FileFilter ignoreFilter) {
5.21 return () -> {
5.22 try {
5.23 - return new JavaFilesIterator(root);
5.24 + return new JavaFilesIterator(root, ignoreFilter);
5.25 } catch (IOException ex) {
5.26 - throw new IllegalStateException(root + "");
5.27 + throw new IllegalStateException(root + "", ex);
5.28 }
5.29 };
5.30 }
5.31 @@ -131,9 +136,20 @@
5.32
5.33 @Override
5.34 public boolean accept(File file) {
5.35 - return (file.isDirectory() && !file.getName().startsWith("."))
5.36 - || ((file.isFile() && file.canRead() && file.getName().endsWith(".java")
5.37 - && !file.getName().equals("package-info.java")));
5.38 + if (file == null) {
5.39 + return false;
5.40 + }
5.41 + if (file.getName() == null) {
5.42 + return false;
5.43 + }
5.44 + boolean nonHiddenFolder = file.isDirectory() && !file.getName().startsWith(".");
5.45 + boolean isJavaFile = file.isFile() && file.canRead() && file.getName().endsWith(".java") ;
5.46 + boolean isNotPackageInfo = !"package-info.java".equals(file.getName());
5.47 + return nonHiddenFolder || ((isJavaFile && isNotPackageInfo) && ignoreFilter.accept(file));
5.48 +// return (
5.49 +// file.isDirectory() && !file.getName().startsWith("."))
5.50 +// || ((file.isFile() && file.canRead() && file.getName().endsWith(".java")
5.51 +// && !file.getName().equals("package-info.java")));
5.52 }
5.53
5.54 @Override
6.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/ElementFinder.java Sat Sep 03 02:41:36 2016 -0400
6.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/ElementFinder.java Wed Apr 12 05:37:43 2017 -0400
6.3 @@ -50,7 +50,9 @@
6.4 import com.sun.source.util.TreePath;
6.5 import com.sun.source.util.TreeScanner;
6.6 import com.sun.source.util.Trees;
6.7 -import java.lang.reflect.Modifier;
6.8 +import java.util.regex.Matcher;
6.9 +import java.util.regex.Pattern;
6.10 +import javax.lang.model.element.Modifier;
6.11 import javax.lang.model.element.Element;
6.12 import javax.lang.model.element.ElementKind;
6.13 import javax.lang.model.type.TypeMirror;
6.14 @@ -60,21 +62,29 @@
6.15 *
6.16 * @author Tim Boudreau
6.17 */
6.18 -final class ElementFinder extends TreeScanner<Void, SourcesInfo> {
6.19 +final class ElementFinder extends TreeScanner<SourceElement, SourcesInfo> {
6.20
6.21 private final CompilationUnitTree cc;
6.22 private final Trees trees;
6.23 + private SourceElement last;
6.24 + private final boolean ignoreShallow;
6.25 + private final boolean ignoreAnonymous;
6.26 + private final boolean ignoreAbstract;
6.27
6.28 - public ElementFinder(CompilationUnitTree cc, Trees trees) {
6.29 + public ElementFinder(CompilationUnitTree cc, Trees trees, boolean ignoreShallow, boolean ignoreAbstract, boolean ignoreAnonymous) {
6.30 this.cc = cc;
6.31 this.trees = trees;
6.32 + this.ignoreShallow = ignoreShallow;
6.33 + this.ignoreAbstract = ignoreAbstract;
6.34 + this.ignoreAnonymous = ignoreAnonymous;
6.35 }
6.36
6.37 @Override
6.38 - public Void visitMethod(MethodTree tree, SourcesInfo info) {
6.39 + public SourceElement visitMethod(MethodTree tree, SourcesInfo info) {
6.40 String nm = tree.getName().toString();
6.41 - addItem(tree, info, SourceElementKind.METHOD, nm);
6.42 - return super.visitMethod(tree, info);
6.43 + SourceElement last = addItem(tree, info, SourceElementKind.METHOD, nm);
6.44 + super.visitMethod(tree, info);
6.45 + return this.last = last;
6.46 }
6.47
6.48 // uncomment to also deal in fields
6.49 @@ -85,16 +95,36 @@
6.50 // addItem(tree, set, SceneObjectKind.FIELD, nm);
6.51 // return super.visitVariable(tree, set);
6.52 // }
6.53 - private void addItem(Tree tree, SourcesInfo info, SourceElementKind kind, String nm) {
6.54 +// private static final Pattern ANONYMOUS = Pattern.compile(".*\\$\\d+$");
6.55 + private SourceElement addItem(Tree tree, SourcesInfo info, SourceElementKind kind, String nm) {
6.56 TreePath path = TreePath.getPath(cc, tree);
6.57 Element el = trees.getElement(path);
6.58 if (el != null && (el.getKind() == ElementKind.FIELD || el.getKind() == ElementKind.METHOD)) {
6.59 - boolean abstrakt = el.getKind() == ElementKind.METHOD && el.getModifiers().contains(Modifier.ABSTRACT);
6.60 + boolean abstrakt = el.getKind() == ElementKind.METHOD
6.61 + && el.getModifiers().contains(Modifier.ABSTRACT);
6.62 + if (ignoreAbstract && abstrakt) {
6.63 + return null;
6.64 + }
6.65 TypeMirror mirror = el.asType();
6.66 +
6.67 +// if (ignoreAnonymous && mirror != null) {
6.68 +// System.out.println(mirror.toString());
6.69 +// Matcher m = ANONYMOUS.matcher(mirror.toString());
6.70 +// if (m.find()) {
6.71 +// System.out.println("IGNORE " + mirror.toString());
6.72 +// return null;
6.73 +// }
6.74 +// }
6.75 +
6.76 String typeStr = mirror != null ? mirror.toString() : "void";
6.77 + if (ignoreShallow && typeStr.indexOf('.') == typeStr.lastIndexOf('.')) {
6.78 + return null;
6.79 + }
6.80 SourceElement nue = new SourceElement(kind, path, nm, typeStr, info, abstrakt);
6.81 info.allElements.add(nue);
6.82 info.elements.put(el, nue);
6.83 + return nue;
6.84 }
6.85 + return null;
6.86 }
6.87 }
7.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/JavacRunner.java Sat Sep 03 02:41:36 2016 -0400
7.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/JavacRunner.java Wed Apr 12 05:37:43 2017 -0400
7.3 @@ -56,7 +56,9 @@
7.4 import java.util.LinkedList;
7.5 import java.util.List;
7.6 import java.util.Locale;
7.7 +import java.util.Objects;
7.8 import java.util.Set;
7.9 +import java.util.concurrent.atomic.AtomicInteger;
7.10 import java.util.concurrent.atomic.AtomicReference;
7.11 import java.util.function.Consumer;
7.12 import javax.tools.Diagnostic;
7.13 @@ -79,15 +81,21 @@
7.14 private final Iterable<? extends File> outdir = Collections.singleton(new File(System.getProperty("java.io.tmpdir")));
7.15 private final Listener listener;
7.16 private final SourcesInfo info;
7.17 + private final boolean ignoreShallow;
7.18 + private final boolean ignoreAbstract;
7.19 + private boolean ignoreAnonymous;
7.20
7.21 - public JavacRunner(SourcesInfo info, Iterable<File> files) {
7.22 - this(info, files, new NullListener());
7.23 + public JavacRunner(SourcesInfo info, Iterable<File> files, boolean ignoreShallow, boolean ignoreAbstract, boolean ignoreAnonymous) {
7.24 + this(info, files, new NullListener(), ignoreShallow, ignoreAbstract, ignoreAnonymous);
7.25 }
7.26
7.27 - public JavacRunner(SourcesInfo info, Iterable<File> files, Listener listener) {
7.28 + public JavacRunner(SourcesInfo info, Iterable<File> files, Listener listener, boolean ignoreShallow, boolean ignoreAbstract, boolean ignoreAnonymous) {
7.29 this.files = files;
7.30 this.listener = listener == null ? new NullListener() : listener;
7.31 this.info = info;
7.32 + this.ignoreAnonymous = ignoreAnonymous;
7.33 + this.ignoreShallow = ignoreShallow;
7.34 + this.ignoreAbstract = ignoreAbstract;
7.35 }
7.36
7.37 private Iterable<? extends JavaFileObject> javaFileObjects(StandardJavaFileManager m, Consumer<File> monitor) {
7.38 @@ -168,15 +176,21 @@
7.39 }
7.40 }
7.41
7.42 + @SuppressWarnings("UseSpecificCatch")
7.43 private SourcesInfo parse(JavacTaskImpl task, AtomicReference<File> lastFile) throws IOException {
7.44 listener.onStartActivity("Finding and parsing Java sources", -1);
7.45 JavacTrees trees = JavacTrees.instance(task.getContext());
7.46 List<CompilationUnitTree> units = new LinkedList<>();
7.47 // We need to cache these because calling parse() twice is an error
7.48 + listener.onStartActivity("Parsing sources with javac", -1);
7.49 + int ct = 0;
7.50 for (CompilationUnitTree tree : task.parse()) {
7.51 units.add(tree);
7.52 + if (++ct % 1000 == 0) {
7.53 + listener.onStep("Parsed " + ct + " sources so far...");
7.54 + }
7.55 }
7.56 - listener.onStartActivity("Attributing Java sources", -1);
7.57 + listener.onStartActivity("Attributing " + units.size() + " Java sources", -1);
7.58 try {
7.59 task.analyze(); // Run attribution - the compiler flags we have set will have it not abort on unresolvable classes
7.60 } catch (Throwable err) {
7.61 @@ -186,18 +200,48 @@
7.62 try {
7.63 listener.onStartActivity("Cataloging methods", units.size());
7.64 // First pass, find all methods in all classes
7.65 - for (CompilationUnitTree tree : units) {
7.66 - listener.onStep(tree.getSourceFile().getName());
7.67 - ElementFinder elementFinder = new ElementFinder(tree, trees);
7.68 - elementFinder.scan(tree, info);
7.69 - }
7.70 + final AtomicInteger count = new AtomicInteger();
7.71 + units.parallelStream().forEach(new Consumer<CompilationUnitTree>() {
7.72 + private final ThreadLocal<CharSequence> pkg = new ThreadLocal<>();
7.73 +
7.74 + @Override
7.75 + public void accept(CompilationUnitTree tree) {
7.76 + ElementFinder elementFinder = new ElementFinder(tree, trees, ignoreShallow, ignoreAbstract, ignoreAnonymous);
7.77 + SourceElement last = elementFinder.scan(tree, info);
7.78 + if (listener != null && !(listener instanceof NullListener)) {
7.79 + if (last != null && !Objects.equals(last.packageName(), pkg.get())) {
7.80 + pkg.equals(last.packageName());
7.81 + listener.onStep(pkg.get() + " scanned");
7.82 + }
7.83 + if (count.getAndIncrement() % 1000 == 0) {
7.84 + listener.onStep(count.get() + " of " + units.size()
7.85 + + " classes scanned for methods, "
7.86 + + info.allElements.size() + " edges found so far...");
7.87 + }
7.88 + }
7.89 + }
7.90 + });
7.91 +
7.92 listener.onStartActivity("Finding usages", units.size());
7.93 + count.set(0);
7.94 // Second pass, run find usages on every method we found
7.95 - for (CompilationUnitTree tree : units) {
7.96 - listener.onStep(tree.getSourceFile().getName());
7.97 +// for (CompilationUnitTree tree : units) {
7.98 +//// listener.onStep(tree.getSourceFile().getName());
7.99 +// UsageFinder usageFinder = new UsageFinder(tree, trees);
7.100 +// usageFinder.scan(tree, info);
7.101 +// if (count++ % 1000 == 0) {
7.102 +// listener.onStep(count + " classes searched for usages, " + info.edgeCount() + " found so far...");
7.103 +// }
7.104 +// }
7.105 + units.parallelStream().forEach((tree) -> {
7.106 UsageFinder usageFinder = new UsageFinder(tree, trees);
7.107 usageFinder.scan(tree, info);
7.108 - }
7.109 + if (listener != null && !(listener instanceof NullListener)) {
7.110 + if (count.getAndIncrement() % 1000 == 0) {
7.111 + listener.onStep(count.get() + " classes searched for usages, " + info.edgeCount() + " found so far...");
7.112 + }
7.113 + }
7.114 + });
7.115 } finally {
7.116 task.finish();
7.117 }
7.118 @@ -230,12 +274,12 @@
7.119 }
7.120
7.121 @Override
7.122 - public void onStartActivity(String activity, int steps) {
7.123 + public void onStartActivity(CharSequence activity, int steps) {
7.124
7.125 }
7.126
7.127 @Override
7.128 - public void onStep(String step) {
7.129 + public void onStep(CharSequence step) {
7.130
7.131 }
7.132 }
8.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/SourceElement.java Sat Sep 03 02:41:36 2016 -0400
8.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/SourceElement.java Wed Apr 12 05:37:43 2017 -0400
8.3 @@ -41,13 +41,13 @@
8.4 * Version 2 license, then the option applies only if the new code is
8.5 * made subject to such option by the copyright holder.
8.6 */
8.7 -
8.8 package org.netbeans.lib.callgraph.javac;
8.9
8.10 import com.sun.source.util.TreePath;
8.11 import java.util.Collections;
8.12 import java.util.HashSet;
8.13 import java.util.Set;
8.14 +import java.util.concurrent.ConcurrentSkipListSet;
8.15 import org.netbeans.lib.callgraph.util.ComparableCharSequence;
8.16
8.17 /**
8.18 @@ -59,8 +59,8 @@
8.19 public final class SourceElement implements Comparable<SourceElement> {
8.20
8.21 private final ComparableCharSequence name;
8.22 - private final Set<SourceElement> inbound = new HashSet<>();
8.23 - private final Set<SourceElement> outbound = new HashSet<>();
8.24 + private volatile Set<SourceElement> inbound;
8.25 + private volatile Set<SourceElement> outbound = new HashSet<>();
8.26 private final SourceElementKind kind;
8.27 private final ComparableCharSequence type;
8.28 private final ComparableCharSequence typeName;
8.29 @@ -79,8 +79,8 @@
8.30 this.typeName = info.strings.create(info.nameOf(handle));
8.31 parameters = info.strings.create(info.parametersOf(handle));
8.32 packageName = info.strings.create(info.packageNameOf(handle));
8.33 - qname = info.strings.concat(packageName, info.strings.DOT, typeName(), info.strings.DOT, getName(), parameters());;
8.34 - shortName = info.strings.concat(typeName(), info.strings.DOT, getName(), parameters());
8.35 + qname = info.strings.concat(packageName, info.strings.DOT, typeName(), info.strings.DOT, name, parameters());;
8.36 + shortName = info.strings.concat(typeName(), info.strings.DOT, name, parameters());
8.37 this.abstrakt = abstrakt;
8.38 }
8.39
8.40 @@ -93,21 +93,41 @@
8.41 }
8.42
8.43 public synchronized Set<SourceElement> getOutboundReferences() {
8.44 - return Collections.unmodifiableSet(outbound);
8.45 + return outbound == null ? EMPTY : Collections.unmodifiableSet(outbound);
8.46 }
8.47
8.48 + private static final Set<SourceElement> EMPTY = Collections.emptySet();
8.49 +
8.50 public synchronized Set<SourceElement> getInboundReferences() {
8.51 - return Collections.unmodifiableSet(inbound);
8.52 + return inbound == null ? EMPTY : Collections.unmodifiableSet(inbound);
8.53 }
8.54
8.55 - synchronized void addOutboundReference(SourceElement item) {
8.56 + void addOutboundReference(SourceElement item) {
8.57 if (item != this) {
8.58 + Set<SourceElement> outbound = this.outbound;
8.59 + if (outbound == null) {
8.60 + synchronized (this) {
8.61 + outbound = this.outbound;
8.62 + if (outbound == null) {
8.63 + this.outbound = outbound = new ConcurrentSkipListSet<>();
8.64 + }
8.65 + }
8.66 + }
8.67 outbound.add(item);
8.68 }
8.69 }
8.70
8.71 synchronized void addInboundReference(SourceElement item) {
8.72 if (item != this) {
8.73 + Set<SourceElement> inbound = this.inbound;
8.74 + if (inbound == null) {
8.75 + synchronized (this) {
8.76 + inbound = this.inbound;
8.77 + if (inbound == null) {
8.78 + this.inbound = inbound = new ConcurrentSkipListSet<>();
8.79 + }
8.80 + }
8.81 + }
8.82 inbound.add(item);
8.83 }
8.84 }
8.85 @@ -127,7 +147,7 @@
8.86 public ComparableCharSequence parameters() {
8.87 return parameters;
8.88 }
8.89 -
8.90 +
8.91 public boolean isAbstract() {
8.92 return abstrakt;
8.93 }
8.94 @@ -160,10 +180,9 @@
8.95 return qname().equals(other.qname());
8.96 }
8.97
8.98 - int hash = 0;
8.99 @Override
8.100 public int hashCode() {
8.101 - return hash == 0 ? hash = 37 * qname().hashCode() : hash;
8.102 + return 37 * qname().hashCode();
8.103 }
8.104
8.105 @Override
9.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/SourcesInfo.java Sat Sep 03 02:41:36 2016 -0400
9.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/SourcesInfo.java Wed Apr 12 05:37:43 2017 -0400
9.3 @@ -52,13 +52,15 @@
9.4 import com.sun.source.tree.VariableTree;
9.5 import com.sun.source.util.TreePath;
9.6 import java.util.ArrayList;
9.7 -import java.util.HashMap;
9.8 -import java.util.HashSet;
9.9 import java.util.Iterator;
9.10 +import java.util.LinkedList;
9.11 import java.util.List;
9.12 import java.util.Map;
9.13 import java.util.Set;
9.14 +import java.util.concurrent.ConcurrentHashMap;
9.15 +import java.util.concurrent.ConcurrentSkipListSet;
9.16 import javax.lang.model.element.Element;
9.17 +import org.netbeans.lib.callgraph.util.ComparableCharSequence;
9.18
9.19 /**
9.20 * Container for shared state that is gathered during the parse and discarded
9.21 @@ -68,15 +70,27 @@
9.22 */
9.23 public final class SourcesInfo implements AutoCloseable {
9.24
9.25 - public final Set<SourceElement> allElements = new HashSet<>();
9.26 - public final Map<Element, SourceElement> elements = new HashMap<>();
9.27 + public final Set<SourceElement> allElements = new ConcurrentSkipListSet<>();
9.28 + public final Map<Element, SourceElement> elements = new ConcurrentHashMap<>();
9.29
9.30 - private final Map<ClassTree, List<ClassTree>> inners = new HashMap<>();
9.31 + private final Map<ClassTree, List<ClassTree>> inners = new ConcurrentHashMap<>();
9.32
9.33 public final EightBitStrings strings;
9.34 + private final ComparableCharSequence DOLLARS_DOT;
9.35 + private final ComparableCharSequence DEFAULT_PACKAGE;
9.36 + private final ComparableCharSequence OPEN_PARENS;
9.37 + private final ComparableCharSequence COMMA;
9.38 + private final ComparableCharSequence CLOSE_PARENS;
9.39 + private final ComparableCharSequence OPEN_CLOSE_PARENS;
9.40
9.41 public SourcesInfo(boolean eightBitStringsDisabled, boolean aggressive) {
9.42 this.strings = new EightBitStrings(eightBitStringsDisabled, aggressive);
9.43 + DOLLARS_DOT = strings.create("$.");
9.44 + DEFAULT_PACKAGE = strings.create("<defaultPackage>");
9.45 + OPEN_PARENS = strings.create("(");
9.46 + CLOSE_PARENS = strings.create("}");
9.47 + OPEN_CLOSE_PARENS = strings.create("()");
9.48 + COMMA = strings.create(",");
9.49 }
9.50
9.51 // XXX rewrite to not hold references to ClassTree, just names,
9.52 @@ -92,34 +106,48 @@
9.53 } while (path != null && !CLASS_TREE_KINDS.contains(path.getLeaf().getKind()));
9.54 return path;
9.55 }
9.56 +
9.57 + int edgeCount() {
9.58 + int ct = 0;
9.59 + for (SourceElement e : allElements) {
9.60 + ct += e.getInboundReferences().size() + e.getOutboundReferences().size();
9.61 + }
9.62 + return ct;
9.63 + }
9.64
9.65 - String parametersOf(TreePath path) {
9.66 + ComparableCharSequence parametersOf(TreePath path) {
9.67 if (path.getLeaf() instanceof MethodTree) {
9.68 MethodTree method = (MethodTree) path.getLeaf();
9.69 - StringBuilder paramsString = new StringBuilder("(");
9.70 - for (Iterator<? extends VariableTree> it = method.getParameters().iterator(); it.hasNext();) {
9.71 + List<? extends VariableTree> params = method.getParameters();
9.72 + if (params.isEmpty()) {
9.73 + return OPEN_CLOSE_PARENS;
9.74 + }
9.75 + List<CharSequence> l = new ArrayList<>(((params.size() -1) * 2) + 2);
9.76 + l.add(OPEN_PARENS);
9.77 + for (Iterator<? extends VariableTree> it = params.iterator(); it.hasNext();) {
9.78 VariableTree variable = it.next();
9.79 - paramsString.append(variable.getType().toString());
9.80 + l.add(strings.create(variable.getType().toString()));
9.81 if (it.hasNext()) {
9.82 - paramsString.append(',');
9.83 + l.add(COMMA);
9.84 }
9.85 }
9.86 - return paramsString.append(")").toString();
9.87 + l.add(CLOSE_PARENS);
9.88 + return strings.concat(l.toArray(new CharSequence[l.size()]));
9.89 } else {
9.90 - return "";
9.91 + return ComparableCharSequence.EMPTY;
9.92 }
9.93 }
9.94
9.95 - String packageNameOf(TreePath path) {
9.96 + ComparableCharSequence packageNameOf(TreePath path) {
9.97 ExpressionTree pkgName = path.getCompilationUnit().getPackageName();
9.98 - return pkgName == null ? "<defaultPackage>" : pkgName.toString();
9.99 + return pkgName == null ? DEFAULT_PACKAGE : strings.create(pkgName.toString());
9.100 }
9.101
9.102 - String nameOf(TreePath tree) {
9.103 + ComparableCharSequence nameOf(TreePath tree) {
9.104 TreePath containingClass = containingClassOf(tree);
9.105 ClassTree clazz = (ClassTree) containingClass.getLeaf();
9.106 - String name = clazz.getSimpleName().toString();
9.107 - if (name.isEmpty()) {
9.108 + ComparableCharSequence name = strings.create(clazz.getSimpleName());
9.109 + if (name.length() == 0) {
9.110 // Recursively generate $1, $2 names, handling the case of
9.111 // mutiple nesting
9.112 name = nameOf(containingClass);
9.113 @@ -127,11 +155,11 @@
9.114 ClassTree nestingParent = (ClassTree) nestedIn.getLeaf();
9.115 List<ClassTree> innerClasses = inners.get(nestingParent);
9.116 if (innerClasses == null) {
9.117 - innerClasses = new ArrayList<>(3);
9.118 + innerClasses = new LinkedList<>();
9.119 inners.put(nestingParent, innerClasses);
9.120 }
9.121 innerClasses.add(clazz);
9.122 - name = name + ".$" + innerClasses.size();
9.123 + return strings.concat(name, DOLLARS_DOT, Integer.toString(innerClasses.size()));
9.124 }
9.125 return name;
9.126 }
10.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/util/ComparableCharSequence.java Sat Sep 03 02:41:36 2016 -0400
10.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/util/ComparableCharSequence.java Wed Apr 12 05:37:43 2017 -0400
10.3 @@ -10,4 +10,92 @@
10.4 public default int compareTo(CharSequence o) {
10.5 return EightBitStrings.compareCharSequences(this, o);
10.6 }
10.7 +
10.8 + public default boolean startsWith(CharSequence seq) {
10.9 + int myLen = length();
10.10 + int seqLength = seq.length();
10.11 + if (seqLength > myLen) {
10.12 + return false;
10.13 + }
10.14 + for (int i = seqLength-1; i >= 0; i--) {
10.15 + if (charAt(i) != seq.charAt(i)) {
10.16 + return false;
10.17 + }
10.18 + }
10.19 + return true;
10.20 + }
10.21 +
10.22 + public default int indexOf(char c) {
10.23 + int len = length();
10.24 + if (len > 0) {
10.25 + for (int i = 0; i < len; i++) {
10.26 + if (c == charAt(i)) {
10.27 + return i;
10.28 + }
10.29 + }
10.30 + }
10.31 + return -1;
10.32 + }
10.33 +
10.34 + public default int lastIndexOf(char c) {
10.35 + int len = length();
10.36 + if (len > 0) {
10.37 + for (int i = len - 1; i >= 0; i--) {
10.38 + if (c == charAt(i)) {
10.39 + return i;
10.40 + }
10.41 + }
10.42 + }
10.43 + return -1;
10.44 + }
10.45 +
10.46 + static final ComparableCharSequence EMPTY = new ComparableCharSequence() {
10.47 + @Override
10.48 + public int compareTo(CharSequence o) {
10.49 + return o.length() == 0 ? 0 : -1;
10.50 + }
10.51 +
10.52 + @Override
10.53 + public int indexOf(char c) {
10.54 + return -1;
10.55 + }
10.56 +
10.57 + @Override
10.58 + public int lastIndexOf(char c) {
10.59 + return -1;
10.60 + }
10.61 +
10.62 + @Override
10.63 + public int length() {
10.64 + return 0;
10.65 + }
10.66 +
10.67 + @Override
10.68 + public char charAt(int index) {
10.69 + throw new ArrayIndexOutOfBoundsException("0 length string but"
10.70 + + " requested char " + index);
10.71 + }
10.72 +
10.73 + @Override
10.74 + public CharSequence subSequence(int start, int end) {
10.75 + if (start == 0 && end == 0) {
10.76 + return this;
10.77 + }
10.78 + throw new ArrayIndexOutOfBoundsException("0 length string but"
10.79 + + " requested substring " + start + " -> " + end);
10.80 +
10.81 + }
10.82 +
10.83 + public boolean equals(Object o) {
10.84 + return o instanceof CharSequence && ((CharSequence) o).length() == 0;
10.85 + }
10.86 +
10.87 + public int hashCode() {
10.88 + return 0;
10.89 + }
10.90 +
10.91 + public String toString() {
10.92 + return "";
10.93 + }
10.94 + };
10.95 }
11.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/util/EightBitStrings.java Sat Sep 03 02:41:36 2016 -0400
11.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/util/EightBitStrings.java Wed Apr 12 05:37:43 2017 -0400
11.3 @@ -49,6 +49,7 @@
11.4 import java.nio.ByteBuffer;
11.5 import java.nio.charset.CharacterCodingException;
11.6 import java.nio.charset.Charset;
11.7 +import java.nio.charset.CharsetDecoder;
11.8 import java.util.ArrayList;
11.9 import java.util.Arrays;
11.10 import java.util.Collection;
11.11 @@ -66,6 +67,7 @@
11.12 public class EightBitStrings {
11.13
11.14 private static final Charset UTF = Charset.forName("UTF-8");
11.15 + private static final Charset ASCII = Charset.forName("US-ASCII");
11.16
11.17 private final InternTable INTERN_TABLE = new InternTable();
11.18 public final CharSequence DOT = create(".");
11.19 @@ -74,18 +76,23 @@
11.20 public final CharSequence QUOTE_SPACE = create("\" ");
11.21 public final CharSequence CLOSE_OPEN_QUOTE = create("\" \"");
11.22
11.23 + private static final boolean ascii = !Boolean.getBoolean("utf");
11.24 private final boolean disabled;
11.25 private final boolean aggressive;
11.26
11.27 public EightBitStrings(boolean disabled) {
11.28 - this (disabled, false);
11.29 + this(disabled, false);
11.30 }
11.31
11.32 public EightBitStrings(boolean disabled, boolean aggressive) {
11.33 this.disabled = disabled;
11.34 this.aggressive = aggressive;
11.35 }
11.36 -
11.37 +
11.38 + public static Charset charset() {
11.39 + return ascii ? ASCII : UTF;
11.40 + }
11.41 +
11.42 public void clear() {
11.43 INTERN_TABLE.dispose();
11.44 }
11.45 @@ -114,16 +121,19 @@
11.46 } else if (aggressive) {
11.47 List<CharSequence> nue = new ArrayList<>(seqs.length + (seqs.length / 2));
11.48 for (CharSequence seq : seqs) {
11.49 + if (seq == ComparableCharSequence.EMPTY) {
11.50 + continue;
11.51 + }
11.52 int ln = seq.length();
11.53 StringBuilder sb = new StringBuilder();
11.54 - for(int i=0; i < ln; i++) {
11.55 + for (int i = 0; i < ln; i++) {
11.56 char c = seq.charAt(i);
11.57 if (Character.isLetter(c) || Character.isDigit(c)) {
11.58 sb.append(c);
11.59 } else {
11.60 nue.add(sb.toString());
11.61 - sb = new StringBuilder();
11.62 - nue.add(new String(new char[] { c }));
11.63 + sb.setLength(0);
11.64 + nue.add(new String(new char[]{c}));
11.65 }
11.66 }
11.67 if (sb.length() > 0) {
11.68 @@ -162,7 +172,7 @@
11.69 Object c = it.next();
11.70 if (c instanceof CharSequence) {
11.71 quoted.add(QUOTE);
11.72 - quoted.add((CharSequence)c);
11.73 + quoted.add((CharSequence) c);
11.74 if (it.hasNext()) {
11.75 quoted.add(QUOTE_SPACE);
11.76 } else {
11.77 @@ -173,14 +183,18 @@
11.78 quoted.add(SPACE);
11.79 }
11.80 }
11.81 - return new Concatenation(quoted.toArray(new CharSequence[quoted.size()]));
11.82 + Concatenation result = new Concatenation(quoted.toArray(new CharSequence[quoted.size()]));
11.83 + if (result.entries.length == 1) {
11.84 + return result.entries[0];
11.85 + }
11.86 + return result;
11.87 }
11.88 }
11.89
11.90 private static byte[] toBytes(CharSequence seq) {
11.91 try {
11.92 ByteArrayOutputStream out = new ByteArrayOutputStream();
11.93 - out.write(seq.toString().getBytes(UTF));
11.94 + out.write(seq.toString().getBytes(charset()));
11.95 return out.toByteArray();
11.96 } catch (IOException ex) {
11.97 throw new IllegalArgumentException(ex);
11.98 @@ -189,7 +203,7 @@
11.99
11.100 private static CharSequence toCharSequence(byte[] bytes) {
11.101 try {
11.102 - return UTF.newDecoder().decode(ByteBuffer.wrap(bytes));
11.103 + return charset().newDecoder().decode(ByteBuffer.wrap(bytes));
11.104 } catch (CharacterCodingException ex) {
11.105 throw new IllegalArgumentException(ex);
11.106 }
11.107 @@ -198,14 +212,14 @@
11.108 int internTableSize() {
11.109 return INTERN_TABLE.last + 1;
11.110 }
11.111 -
11.112 +
11.113 List<CharSequence> dumpInternTable() {
11.114 return INTERN_TABLE.dumpInternTable();
11.115 }
11.116
11.117 static class InternTable {
11.118
11.119 - private static final int SIZE_INCREMENT = 50;
11.120 + private static final int SIZE_INCREMENT = 150;
11.121
11.122 private int last = -1;
11.123 private Entry[] entries = new Entry[SIZE_INCREMENT];
11.124 @@ -223,20 +237,28 @@
11.125 // here. This is slower than a HashMap (we sort on insert so
11.126 // we can binary search later), but involves far fewer allocations
11.127 Entry entry = new Entry(toBytes(seq), (short) seq.length());
11.128 - int offset = last == -1 ? -1 : Arrays.binarySearch(entries, 0, last + 1, entry);
11.129 - if (offset > 0) {
11.130 - return entries[offset];
11.131 + synchronized (this) {
11.132 + int offset = last == -1 ? -1 : Arrays.binarySearch(entries, 0, last + 1, entry);
11.133 + if (offset > 0) {
11.134 + return entries[offset];
11.135 + }
11.136 + if (last == entries.length - 1) {
11.137 + Entry[] nue = new Entry[entries.length + SIZE_INCREMENT];
11.138 + System.arraycopy(entries, 0, nue, 0, entries.length);
11.139 + entries = nue;
11.140 + }
11.141 + entries[++last] = entry;
11.142 + try {
11.143 + Arrays.sort(entries, 0, last + 1);
11.144 + } catch (IllegalArgumentException e) {
11.145 + throw new AssertionError("Broken sorting '" + seq
11.146 + + "' into array for item " + last
11.147 + + ". Full table: " + dumpInternTable(), e);
11.148 + }
11.149 }
11.150 - if (last == entries.length - 1) {
11.151 - Entry[] nue = new Entry[entries.length + SIZE_INCREMENT];
11.152 - System.arraycopy(entries, 0, nue, 0, entries.length);
11.153 - entries = nue;
11.154 - }
11.155 - entries[++last] = entry;
11.156 - Arrays.sort(entries, 0, last + 1);
11.157 return entry;
11.158 }
11.159 -
11.160 +
11.161 List<CharSequence> dumpInternTable() {
11.162 return Arrays.asList(entries);
11.163 }
11.164 @@ -254,16 +276,28 @@
11.165 this.length = length;
11.166 }
11.167
11.168 + int hash = 0;
11.169 +
11.170 public int hashCode() {
11.171 + if (hash != 0) {
11.172 + return hash;
11.173 + }
11.174 int h = 0;
11.175 if (h == 0 && bytes.length > 0) {
11.176 - CharSequence val = toChars();
11.177 - int max = val.length();
11.178 - for (int i = 0; i < max; i++) {
11.179 - h = 31 * h + val.charAt(i);
11.180 + if (ascii) {
11.181 + int max = bytes.length;
11.182 + for (int i = 0; i < max; i++) {
11.183 + h = 31 * h + ((char) bytes[i]);
11.184 + }
11.185 + } else {
11.186 + CharSequence val = toChars();
11.187 + int max = val.length();
11.188 + for (int i = 0; i < max; i++) {
11.189 + h = 31 * h + val.charAt(i);
11.190 + }
11.191 }
11.192 }
11.193 - return h;
11.194 + return hash = h;
11.195 }
11.196
11.197 @Override
11.198 @@ -277,6 +311,9 @@
11.199 if (other.bytes.length < bytes.length) {
11.200 return false;
11.201 }
11.202 + // XXX if two strings with different unicode encodings,
11.203 + // such as numeric encoding of ascii chars in one,
11.204 + // will give the wrong answer
11.205 return Arrays.equals(bytes, other.bytes);
11.206 } else if (o instanceof CharSequence) {
11.207 return charSequencesEqual(this, (CharSequence) o);
11.208 @@ -285,10 +322,7 @@
11.209 }
11.210 }
11.211
11.212 - public int compare(Entry o) {
11.213 - if (o == this) {
11.214 - return 0;
11.215 - }
11.216 + public int compareChars(Entry o) {
11.217 int max = Math.min(bytes.length, o.bytes.length);
11.218 for (int i = 0; i < max; i++) {
11.219 if (bytes[i] > o.bytes[i]) {
11.220 @@ -297,6 +331,17 @@
11.221 return -1;
11.222 }
11.223 }
11.224 + return 0;
11.225 + }
11.226 +
11.227 + public int compare(Entry o) {
11.228 + if (o == this) {
11.229 + return 0;
11.230 + }
11.231 + int result = compareChars(o);
11.232 + if (result != 0) {
11.233 + return result;
11.234 + }
11.235 if (bytes.length == o.bytes.length) {
11.236 return 0;
11.237 } else if (bytes.length > o.bytes.length) {
11.238 @@ -308,7 +353,7 @@
11.239
11.240 @Override
11.241 public String toString() {
11.242 - return new String(bytes, UTF);
11.243 + return new String(bytes, charset());
11.244 }
11.245
11.246 @Override
11.247 @@ -322,11 +367,17 @@
11.248
11.249 @Override
11.250 public char charAt(int index) {
11.251 + if (ascii) {
11.252 + return (char) bytes[index];
11.253 + }
11.254 return toCharSequence(bytes).charAt(index);
11.255 }
11.256
11.257 @Override
11.258 public CharSequence subSequence(int start, int end) {
11.259 + if (ascii) {
11.260 + return new String(bytes, start, end - start);
11.261 + }
11.262 return toCharSequence(bytes).subSequence(start, end);
11.263 }
11.264
11.265 @@ -347,11 +398,17 @@
11.266 int aLength = a.length();
11.267 int bLength = b.length();
11.268 int max = Math.min(aLength, bLength);
11.269 - for (int i = 0; i < max; i++) {
11.270 - if (a.charAt(i) > b.charAt(i)) {
11.271 - return 1;
11.272 - } else if (a.charAt(i) < b.charAt(i)) {
11.273 - return -1;
11.274 + if (ascii && a instanceof InternTable.Entry && b instanceof InternTable.Entry) {
11.275 + InternTable.Entry ae = (InternTable.Entry) a;
11.276 + InternTable.Entry be = (InternTable.Entry) b;
11.277 + return ae.compare(be);
11.278 + } else {
11.279 + for (int i = 0; i < max; i++) {
11.280 + if (a.charAt(i) > b.charAt(i)) {
11.281 + return 1;
11.282 + } else if (a.charAt(i) < b.charAt(i)) {
11.283 + return -1;
11.284 + }
11.285 }
11.286 }
11.287 if (aLength == bLength) {
11.288 @@ -364,6 +421,7 @@
11.289 }
11.290
11.291 static boolean debug;
11.292 +
11.293 class Concatenation implements ComparableCharSequence, Comparable<CharSequence> {
11.294
11.295 private final InternTable.Entry[] entries;
11.296 @@ -403,7 +461,9 @@
11.297 return e.charAt(index);
11.298 }
11.299 }
11.300 - throw new IndexOutOfBoundsException(index + " of " + length() + " in " + this + " with entries " + Arrays.asList(entries));
11.301 + throw new IndexOutOfBoundsException(index + " of "
11.302 + + length() + " in " + this + " with entries "
11.303 + + Arrays.asList(entries));
11.304 }
11.305
11.306 @Override
11.307 @@ -417,13 +477,14 @@
11.308 sb.append(e);
11.309 }
11.310 if (debug) {
11.311 - sb.append(" - ").append (Arrays.asList(entries));
11.312 + sb.append(" - ").append(Arrays.asList(entries));
11.313 }
11.314 return sb.toString();
11.315 }
11.316
11.317 private int hash = 0;
11.318
11.319 + @Override
11.320 public int hashCode() {
11.321 int h = hash;
11.322 if (h == 0 && length() > 0) {
11.323 @@ -472,11 +533,33 @@
11.324
11.325 @Override
11.326 public int compareTo(CharSequence o) {
11.327 + if (o instanceof Concatenation) {
11.328 + Concatenation other = (Concatenation) o;
11.329 + int ec = Math.min(entries.length, other.entries.length);
11.330 + if (ec > 0) {
11.331 + int res = entries[0].compareChars(other.entries[0]);
11.332 + if (res != 0) {
11.333 + return res;
11.334 + }
11.335 + }
11.336 + }
11.337 return compareCharSequences(this, o);
11.338 }
11.339 }
11.340
11.341 private static boolean charSequencesEqual(CharSequence a, CharSequence b) {
11.342 + if (ascii && a instanceof InternTable.Entry && b instanceof InternTable.Entry) {
11.343 + return Arrays.equals(((InternTable.Entry) a).bytes, ((InternTable.Entry) b).bytes);
11.344 + }
11.345 + if (a instanceof Concatenation && b instanceof Concatenation) {
11.346 + Concatenation ca = (Concatenation) a;
11.347 + Concatenation cb = (Concatenation) b;
11.348 + if (ca.entries.length > 0 && cb.entries.length > 0) {
11.349 + if (ca.entries[0].compareChars(cb.entries[0]) != 0) {
11.350 + return false;
11.351 + }
11.352 + }
11.353 + }
11.354 int maxA = a.length();
11.355 int maxB = b.length();
11.356 if (maxA != maxB) {
11.357 @@ -497,7 +580,7 @@
11.358 private final String s;
11.359
11.360 public StringWrapper(String s) {
11.361 - this.s = s;
11.362 + this.s = s.intern();
11.363 }
11.364
11.365 public int length() {
11.366 @@ -558,7 +641,10 @@
11.367 if (!(anObject instanceof CharSequence)) {
11.368 return false;
11.369 }
11.370 - return charSequencesEqual(this, (CharSequence) anObject);
11.371 + if (anObject instanceof String) {
11.372 + return s.equals(anObject);
11.373 + }
11.374 + return s.contentEquals((CharSequence) anObject);
11.375 }
11.376
11.377 public boolean contentEquals(StringBuffer sb) {
11.378 @@ -777,6 +863,5 @@
11.379 public String intern() {
11.380 return s.intern();
11.381 }
11.382 -
11.383 }
11.384 }
12.1 --- a/callgraph/src/test/java/org/netbeans/lib/callgraph/util/SmallStringTest.java Sat Sep 03 02:41:36 2016 -0400
12.2 +++ b/callgraph/src/test/java/org/netbeans/lib/callgraph/util/SmallStringTest.java Wed Apr 12 05:37:43 2017 -0400
12.3 @@ -9,7 +9,6 @@
12.4 import java.util.Set;
12.5 import org.junit.Test;
12.6 import static org.junit.Assert.*;
12.7 -import org.netbeans.lib.callgraph.util.EightBitStrings.Concatenation;
12.8
12.9 /**
12.10 *
12.11 @@ -86,7 +85,8 @@
12.12 for (ComparableCharSequence c1 : new ComparableCharSequence[]{a, b, c, d, e}) {
12.13 for (ComparableCharSequence c2 : new ComparableCharSequence[]{a, b, c, d, e}) {
12.14 assertEquals(c1, c2);
12.15 - assertEquals(0, c1.compareTo(c2));
12.16 + int comp = c1.compareTo(c2);
12.17 + assertEquals("'" + c1 + "/ sort with '" + c2 + "' wrongly sorts to " + comp, 0, comp);
12.18 }
12.19 }
12.20
12.21 @@ -94,7 +94,7 @@
12.22 assertEquals("\"a\" \"b\" \"c\" \"d\"", cs.toString());
12.23
12.24 strings = new EightBitStrings(true);
12.25 - a = strings.concat("a", "b", "c", "d");
12.26 + a = strings.concat("a", "b", "c", "d", "");
12.27 b = strings.concat("a", "b", "cd");
12.28 c = strings.concat("ab", "cd");
12.29 d = strings.concat("a", "bcd");
12.30 @@ -112,7 +112,7 @@
12.31 }
12.32
12.33 strings = new EightBitStrings(false);
12.34 - a = strings.concat(strings.create("abc"), strings.create("def"));
12.35 + a = strings.concat(strings.create("abc"), strings.create("def"), strings.create(""));
12.36 b = strings.concat("bcd", strings.create("efg"));
12.37 c = strings.concat("cde", strings.create("fgh"));
12.38 d = strings.create("defghi");
12.39 @@ -121,7 +121,7 @@
12.40 Collections.sort(l);
12.41 assertEquals(l, Arrays.asList(a, b, c, d, e));
12.42 }
12.43 -
12.44 +
12.45 List<String> stringsOf(List<CharSequence> cs) {
12.46 List<String> result = new ArrayList<>();
12.47 for (CharSequence c : cs) {
12.48 @@ -132,7 +132,7 @@
12.49 }
12.50 return result;
12.51 }
12.52 -
12.53 +
12.54 @Test
12.55 public void testAggressive() {
12.56 EightBitStrings strings = new EightBitStrings(false, true);
12.57 @@ -159,13 +159,48 @@
12.58 }
12.59
12.60 @Test
12.61 + public void testMoreSorting() {
12.62 + EightBitStrings strings = new EightBitStrings(false, true);
12.63 + char[] chars = "QWERTYUIOPASDFGHJKLZXCVBNM<>?:}\"!@#$%^&*()_+1234567890qwertyuiopasdfghjklzxcvbnm,./;'[]=-\\|`~ \t".toCharArray();
12.64 + Arrays.sort(chars);
12.65 + Random r = new Random(5);
12.66 + List<String> l = new ArrayList<>();
12.67 + for (int i = 1; i < 5; i++) {
12.68 + for (int j = 0; j < chars.length - (i + 1); j++) {
12.69 + StringBuilder sb = new StringBuilder();
12.70 + for (int k = 0; k < i; k++) {
12.71 + if (k == 0) {
12.72 + sb.append(chars[k]);
12.73 + } else {
12.74 + sb.append(chars[r.nextInt(chars.length)]);
12.75 + }
12.76 + }
12.77 + l.add(sb.toString());
12.78 + }
12.79 + if (i == 4) {
12.80 + l.add("");
12.81 + }
12.82 + }
12.83 +
12.84 + List<ComparableCharSequence> ll = new ArrayList<>();
12.85 + for (String s : l) {
12.86 + ll.add(strings.create(s));
12.87 + }
12.88 + Collections.sort(l);
12.89 + Collections.sort(ll);
12.90 + for (int i=0; i < l.size(); i++) {
12.91 + assertEquals("At " + i + " bad order:\n" + l + "\n" + ll, l.get(i), ll.get(i).toString());
12.92 + }
12.93 + }
12.94 +
12.95 + @Test
12.96 public void testConcatQuoted() {
12.97 EightBitStrings strings = new EightBitStrings(false);
12.98 List<Object> l = Arrays.asList(strings.create("hey"), false, 23, strings.create("bar"), "baz");
12.99 CharSequence cc = strings.concatQuoted(l);
12.100 assertEquals("\"hey\" false 23 \"bar\" \"baz\"", cc.toString());
12.101 }
12.102 -
12.103 +
12.104 private static String randomString(int len) {
12.105 char[] c = new char[len];
12.106 for (int i = 0; i < c.length; i++) {