Eliminate duplicates in graph files release82_base
authorTim Boudreau <tboudreau@netbeans.org>
Sat, 03 Sep 2016 02:41:36 -0400
changeset 1837404a79821e760
parent 18373 3527a32a19f0
child 18375 a32a4e528a9f
child 18376 ef4193034651
child 18400 c87c223efe6a
Eliminate duplicates in graph files
callgraph/pom.xml
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/SourcesInfo.java
callgraph/src/main/java/org/netbeans/lib/callgraph/util/EightBitStrings.java
callgraph/src/test/java/org/netbeans/lib/callgraph/ArgumentsTest.java
callgraph/src/test/java/org/netbeans/lib/callgraph/util/SmallStringTest.java
     1.1 --- a/callgraph/pom.xml	Mon Aug 29 13:00:53 2016 -0400
     1.2 +++ b/callgraph/pom.xml	Sat Sep 03 02:41:36 2016 -0400
     1.3 @@ -4,7 +4,7 @@
     1.4      <groupId>org.netbeans</groupId>
     1.5      <artifactId>callgraph</artifactId>
     1.6      <name>Callgraph Utility</name>
     1.7 -    <version>1.1</version>
     1.8 +    <version>1.3</version>
     1.9      <packaging>jar</packaging>
    1.10      <properties>
    1.11          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     2.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/Arguments.java	Mon Aug 29 13:00:53 2016 -0400
     2.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/Arguments.java	Sat Sep 03 02:41:36 2016 -0400
     2.3 @@ -63,6 +63,8 @@
     2.4      private static final Command[] commands = new Command[]{
     2.5          new NoSelfReferencesCommand(),
     2.6          new ShortNamesCommand(),
     2.7 +        new ExtendedPropertiesCommand(),
     2.8 +        new AntCommand(),
     2.9          new MavenCommand(),
    2.10          new GradleCommand(),
    2.11          new IgnoreCommand(),
    2.12 @@ -75,12 +77,16 @@
    2.13          new OmitAbstractCommand(),
    2.14          new QuietCommand(),
    2.15          new ReverseCommand(),
    2.16 +        new AggressiveCommand(),
    2.17          new VerboseCommand()
    2.18      };
    2.19      private boolean noSelfReferences = false;
    2.20      private boolean shortNames = false;
    2.21      private boolean maven = false;
    2.22      private boolean gradle = false;
    2.23 +    private boolean ant = false;
    2.24 +    private boolean xprop = false;
    2.25 +    private boolean aggressive = false;
    2.26      private File outfile;
    2.27      private Set<String> exclude = new HashSet<>();
    2.28      private Set<String> ignore = new HashSet<>();
    2.29 @@ -141,13 +147,14 @@
    2.30                  }
    2.31              }
    2.32          }
    2.33 -        Set<String> origFolders = folders;
    2.34 -        if (maven && gradle) {
    2.35 -            errors.add("--maven and --gradle are mutually exclusive");
    2.36 +        if ((maven && gradle) || (ant && gradle) || (ant && maven)) {
    2.37 +            errors.add("--maven, --ant and --gradle are mutually exclusive");
    2.38          } else if (maven) {
    2.39              findMavenSubfolders(errors);
    2.40          } else if (gradle) {
    2.41              findGradleSubfolders(errors);
    2.42 +        } else if (ant) {
    2.43 +            findAntSubfolders(errors);
    2.44          }
    2.45          Set<File> toIgnore = new HashSet<>();
    2.46          for (String ig : ignore) {
    2.47 @@ -193,6 +200,12 @@
    2.48              }
    2.49          }
    2.50          this.folders.removeAll(toIgnore);
    2.51 +        if (verbose && !this.folders.isEmpty()) {
    2.52 +            System.err.println("Will scan the following source roots:");
    2.53 +            for (File f : folders()) {
    2.54 +                System.err.println("  " + f.getAbsolutePath());
    2.55 +            }
    2.56 +        }
    2.57  
    2.58          if (packageGraphFile != null) {
    2.59              File parent = packageGraphFile.getParentFile();
    2.60 @@ -250,6 +263,36 @@
    2.61          return pom.exists() && pom.isFile() && pom.canRead();
    2.62      }
    2.63  
    2.64 +    void findAntSubfolders(List<String> errors) {
    2.65 +        Set<File> flds = new HashSet<>(this.folders);
    2.66 +        this.folders.clear();
    2.67 +        for (File f : flds) {
    2.68 +            recurseSubfoldersLookingForAntProjects(f);
    2.69 +        }
    2.70 +        if (this.folders.isEmpty()) {
    2.71 +            errors.add("Did not find any ant projects (looked for build.xml and src/ in all subfolders of folder list)");
    2.72 +        }
    2.73 +    }
    2.74 +
    2.75 +    private void recurseSubfoldersLookingForAntProjects(File file) {
    2.76 +        if (file.isDirectory()) {
    2.77 +            if (hasBuildXml(file)) {
    2.78 +                File sources = new File(file, "src");
    2.79 +                if (sources.exists() && sources.isDirectory()) {
    2.80 +                    this.folders.add(sources);
    2.81 +                }
    2.82 +            }
    2.83 +            for (File child : file.listFiles()) {
    2.84 +                recurseSubfoldersLookingForAntProjects(child);
    2.85 +            }
    2.86 +        }
    2.87 +    }
    2.88 +
    2.89 +    private boolean hasBuildXml(File fld) {
    2.90 +        File pom = new File(fld, "build.xml");
    2.91 +        return pom.exists() && pom.isFile() && pom.canRead();
    2.92 +    }
    2.93 +
    2.94      void findGradleSubfolders(List<String> errors) {
    2.95          Set<File> flds = new HashSet<>(this.folders);
    2.96          this.folders.clear();
    2.97 @@ -333,6 +376,20 @@
    2.98      }
    2.99  
   2.100      @Override
   2.101 +    public boolean isAnt() {
   2.102 +        return ant;
   2.103 +    }
   2.104 +
   2.105 +    @Override
   2.106 +    public boolean isExtendedProperties() {
   2.107 +        return xprop;
   2.108 +    }
   2.109 +
   2.110 +    public boolean isAggressive() {
   2.111 +        return aggressive;
   2.112 +    }
   2.113 +
   2.114 +    @Override
   2.115      public File classGraphFile() {
   2.116          return classGraphFile;
   2.117      }
   2.118 @@ -467,7 +524,7 @@
   2.119  
   2.120          @Override
   2.121          protected String help() {
   2.122 -            return "Disable string memory optimizations - runs faster but may run out of memory";
   2.123 +            return "Disable string memory optimizations - runs faster and supports unicode class names, but may run out of memory";
   2.124          }
   2.125      }
   2.126  
   2.127 @@ -489,6 +546,25 @@
   2.128          }
   2.129      }
   2.130  
   2.131 +    private static final class AggressiveCommand extends Command {
   2.132 +
   2.133 +        AggressiveCommand() {
   2.134 +            super(CMD_AGGRESSIVE_MEMORY, "z", true, false);
   2.135 +        }
   2.136 +
   2.137 +        @Override
   2.138 +        protected int doParse(int i, String[] args, Arguments toSet) {
   2.139 +            toSet.aggressive = true;
   2.140 +            return 1;
   2.141 +        }
   2.142 +
   2.143 +        @Override
   2.144 +        protected String help() {
   2.145 +            return "Aggressively optimize the 8-bit string intern table "
   2.146 +                    + "for large graphs, sacrificing performace for space";
   2.147 +        }
   2.148 +    }
   2.149 +
   2.150      private static final class ShortNamesCommand extends Command {
   2.151  
   2.152          ShortNamesCommand() {
   2.153 @@ -507,6 +583,42 @@
   2.154          }
   2.155      }
   2.156  
   2.157 +    private static final class ExtendedPropertiesCommand extends Command {
   2.158 +
   2.159 +        ExtendedPropertiesCommand() {
   2.160 +            super(CMD_EXTENDED_PROPERTIES, "x", true, false);
   2.161 +        }
   2.162 +
   2.163 +        @Override
   2.164 +        protected int doParse(int i, String[] args, Arguments toSet) {
   2.165 +            toSet.xprop = true;
   2.166 +            return 1;
   2.167 +        }
   2.168 +
   2.169 +        @Override
   2.170 +        protected String help() {
   2.171 +            return "Find all maven projects that are children of the passed folders, and scan their src/main/java subfolders";
   2.172 +        }
   2.173 +    }
   2.174 +
   2.175 +    private static final class AntCommand extends Command {
   2.176 +
   2.177 +        AntCommand() {
   2.178 +            super(CMD_ANT, "a", true, false);
   2.179 +        }
   2.180 +
   2.181 +        @Override
   2.182 +        protected int doParse(int i, String[] args, Arguments toSet) {
   2.183 +            toSet.ant = true;
   2.184 +            return 1;
   2.185 +        }
   2.186 +
   2.187 +        @Override
   2.188 +        protected String help() {
   2.189 +            return "Find all ant projects that are children of the passed folders, and scan their src/ subfolders";
   2.190 +        }
   2.191 +    }
   2.192 +
   2.193      private static final class MavenCommand extends Command {
   2.194  
   2.195          MavenCommand() {
     3.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/Callgraph.java	Mon Aug 29 13:00:53 2016 -0400
     3.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/Callgraph.java	Sat Sep 03 02:41:36 2016 -0400
     3.3 @@ -1,7 +1,7 @@
     3.4  /* 
     3.5   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6   *
     3.7 - * Copyright (C) 1997-2015 Oracle and/or its affiliates. All rights reserved.
     3.8 + * Copyright (C) 1997-2016 Oracle and/or its affiliates. All rights reserved.
     3.9   *
    3.10   * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    3.11   * Other names may be trademarks of their respective owners.
    3.12 @@ -70,10 +70,8 @@
    3.13  import java.util.LinkedList;
    3.14  import java.util.List;
    3.15  import java.util.Set;
    3.16 -import java.util.TreeSet;
    3.17  import java.util.concurrent.atomic.AtomicReference;
    3.18  import java.util.function.Consumer;
    3.19 -import org.netbeans.lib.callgraph.util.ComparableCharSequence;
    3.20  
    3.21  /**
    3.22   * Scans a source folder and runs javac against any Java sources present, and
    3.23 @@ -134,7 +132,7 @@
    3.24       * @throws IOException If i/o fails
    3.25       */
    3.26      static List<SourceElement> invoke(CallgraphControl arguments, Listener listener) throws IOException {
    3.27 -        SourcesInfo info = new SourcesInfo(arguments.isDisableEightBitStrings());
    3.28 +        SourcesInfo info = new SourcesInfo(arguments.isDisableEightBitStrings(), arguments.isAggressive());
    3.29          // Build an iterable of all Java sources (without collecting them all ahead of time)
    3.30          List<Iterable<File>> iterables = new LinkedList<>();
    3.31          for (File folder : arguments) {
    3.32 @@ -158,8 +156,15 @@
    3.33              // duplicate avoidance
    3.34              Set<CharSequence> emittedPackageLines = new HashSet<>();
    3.35              Set<CharSequence> emittedClassLines = new HashSet<>();
    3.36 +            List<Object> clazz = new ArrayList<>(5);
    3.37 +            CharSequence lastClass = null;
    3.38 +
    3.39 +            List<Object> pkg = new ArrayList<>(5);
    3.40 +            CharSequence lastPackage = null;
    3.41 +            
    3.42              try {
    3.43                  // Iterate every method
    3.44 +                outer:
    3.45                  for (SourceElement sce : all) {
    3.46                      if (arguments.isExcluded(sce.qname().toString())) { // Ignore matches
    3.47                          continue;
    3.48 @@ -167,7 +172,32 @@
    3.49                      List<SourceElement> outbounds = new ArrayList<>(arguments.isReverse() ? sce.getInboundReferences() : sce.getOutboundReferences());
    3.50                      Collections.sort(outbounds); // also sort connections
    3.51                      // Iterate the current method's connections
    3.52 -                    Set<ComparableCharSequence> mth = new TreeSet<>();
    3.53 +                    List<Object> mth = new ArrayList<>(5);
    3.54 +                    CharSequence currClazz = arguments.isShortNames() ? sce.typeName() : info.strings.concat(sce.packageName(), info.strings.DOT, sce.typeName());
    3.55 +                    if (!currClazz.equals(lastClass)) {
    3.56 +                        if (classStream != null) {
    3.57 +                            writeLine(clazz, info, emittedClassLines, classStream);
    3.58 +                        }
    3.59 +                        clazz.clear();
    3.60 +                        lastClass = currClazz;
    3.61 +                    }
    3.62 +                    if (clazz.isEmpty()) {
    3.63 +                        clazz.add(currClazz);
    3.64 +                        if (arguments.isExtendedProperties()) {
    3.65 +                            clazz.add(sce.isAbstract());
    3.66 +                        }
    3.67 +                    }
    3.68 +                    CharSequence currPkg = sce.packageName();
    3.69 +                    if (pkg.isEmpty()) {
    3.70 +                        pkg.add(currPkg);
    3.71 +                    }
    3.72 +                    if (!currPkg.equals(lastPackage)) {
    3.73 +                        if (packageStream != null) {
    3.74 +                            writeLine(pkg, info, emittedPackageLines, packageStream);
    3.75 +                        }
    3.76 +                        lastPackage = currPkg;
    3.77 +                        pkg.clear();
    3.78 +                    }
    3.79                      for (SourceElement outbound : outbounds) {
    3.80                          if (arguments.isExcluded(outbound.qname().toString())) { // Ignore matches
    3.81                              continue;
    3.82 @@ -181,24 +211,20 @@
    3.83                          if (!arguments.isSelfReferences() && sce.typeName().equals(outbound.typeName())) {
    3.84                              continue;
    3.85                          }
    3.86 -                        if (outStream != null) {
    3.87 +                        if (outStream != null || !arguments.isQuiet()) {
    3.88                              if (arguments.isShortNames()) {
    3.89                                  mth.add(outbound.shortName());
    3.90                              } else {
    3.91                                  mth.add(outbound.qname());
    3.92                              }
    3.93 +                            if (arguments.isExtendedProperties()) {
    3.94 +                                mth.add(outbound.isAbstract());
    3.95 +                            }
    3.96                          }
    3.97                          // Build the package graph output if necessary
    3.98                          if (packageStream != null) {
    3.99 -                            CharSequence pkg1 = sce.packageName();
   3.100 -                            CharSequence pkg2 = outbound.packageName();
   3.101 -                            if (!pkg1.equals(pkg2)) {
   3.102 -//                                CharSequence pkgLine = '"' + pkg1.toString() + "\" \"" + pkg2.toString() + '"';
   3.103 -                                CharSequence pkgLine = info.strings.concat(info.strings.QUOTE, pkg1, info.strings.CLOSE_OPEN_QUOTE, pkg2, info.strings.QUOTE);
   3.104 -                                if (!emittedPackageLines.contains(pkgLine)) {
   3.105 -                                    emittedPackageLines.add(pkgLine);
   3.106 -                                    packageStream.println(pkgLine);
   3.107 -                                }
   3.108 +                            if (!outbound.packageName().equals(currPkg) || arguments.isSelfReferences()) {
   3.109 +                                pkg.add(outbound.packageName());
   3.110                              }
   3.111                          }
   3.112                          // Build the class graph output if necessary
   3.113 @@ -206,22 +232,24 @@
   3.114                              CharSequence type1 = sce.typeName();
   3.115                              CharSequence type2 = outbound.typeName();
   3.116                              if (!arguments.isShortNames()) {
   3.117 -                                type1 = info.strings.concat(sce.packageName(), info.strings.DOT, type1);
   3.118 +//                                type1 = info.strings.concat(sce.packageName(), info.strings.DOT, type1);
   3.119                                  type2 = info.strings.concat(outbound.packageName(), info.strings.DOT, type2);
   3.120                              }
   3.121 -                            if (!type1.equals(type2)) {
   3.122 -                                CharSequence classLine = info.strings.concat(info.strings.QUOTE, type1, info.strings.CLOSE_OPEN_QUOTE, type2, info.strings.QUOTE);
   3.123 -                                if (!emittedClassLines.contains(classLine)) {
   3.124 -                                    emittedClassLines.add(classLine);
   3.125 -                                    classStream.println(classLine);
   3.126 +                            if (!type1.equals(type2) && !clazz.contains(type2)) {
   3.127 +                                clazz.add(type2);
   3.128 +                                if (arguments.isExtendedProperties()) {
   3.129 +                                    clazz.add(outbound.isAbstract());
   3.130                                  }
   3.131                              }
   3.132                          }
   3.133                      }
   3.134 -                    if ((!arguments.isQuiet() || outStream != null)) {
   3.135 +                    if (!arguments.isQuiet() || outStream != null) {
   3.136                          if (!mth.isEmpty()) {
   3.137                              CharSequence nm = arguments.isShortNames() ? sce.shortName() : sce.qname();
   3.138 -                            List<CharSequence> l = new ArrayList<>(mth);
   3.139 +                            List<Object> l = mth;
   3.140 +                            if (arguments.isExtendedProperties()) {
   3.141 +                                mth.add(0, sce.isAbstract());
   3.142 +                            }
   3.143                              l.add(0, nm);
   3.144                              CharSequence line = info.strings.concatQuoted(l);
   3.145                              if (!arguments.isQuiet()) {
   3.146 @@ -233,6 +261,12 @@
   3.147                          }
   3.148                      }
   3.149                  }
   3.150 +                if (classStream != null && !clazz.isEmpty()) {
   3.151 +                    writeLine(clazz, info, emittedClassLines, classStream);
   3.152 +                }
   3.153 +                if (packageStream != null && !pkg.isEmpty()) {
   3.154 +                    writeLine(pkg, info, emittedPackageLines, packageStream);
   3.155 +                }
   3.156              } finally {
   3.157                  for (PrintStream ps : new PrintStream[]{outStream, packageStream, classStream}) {
   3.158                      if (ps != null) {
   3.159 @@ -243,6 +277,15 @@
   3.160          }
   3.161          return all;
   3.162      }
   3.163 +    private static void writeLine(List<Object> clazz, SourcesInfo info, Set<CharSequence> emittedClassLines, PrintStream classStream) {
   3.164 +        if (!clazz.isEmpty()) {
   3.165 +            CharSequence cs = info.strings.concatQuoted(clazz);
   3.166 +            if (!emittedClassLines.contains(cs)) {
   3.167 +                classStream.println(cs);
   3.168 +                emittedClassLines.add(cs);
   3.169 +            }
   3.170 +        }
   3.171 +    }
   3.172  
   3.173      private static PrintStream createPrintStreamIfNotNull(File outputFile) throws IOException {
   3.174          PrintStream outStream = null;
     4.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/CallgraphControl.java	Mon Aug 29 13:00:53 2016 -0400
     4.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/CallgraphControl.java	Sat Sep 03 02:41:36 2016 -0400
     4.3 @@ -1,4 +1,4 @@
     4.4 -/* 
     4.5 +/*
     4.6   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     4.7   *
     4.8   * Copyright (C) 1997-2015 Oracle and/or its affiliates. All rights reserved.
     4.9 @@ -57,8 +57,11 @@
    4.10  
    4.11      static final String CMD_NOSELF = "noself";
    4.12      static final String CMD_SIMPLE = "simple";
    4.13 +    static final String CMD_ANT = "ant";
    4.14      static final String CMD_MAVEN = "maven";
    4.15      static final String CMD_GRADLE = "gradle";
    4.16 +    static final String CMD_EXTENDED_PROPERTIES = "extensions";
    4.17 +    static final String CMD_AGGRESSIVE_MEMORY = "aggressive";
    4.18      static final String CMD_IGNORE = "ignore";
    4.19      static final String CMD_PACKAGEGRAPH = "packagegraph";
    4.20      static final String CMD_METHODGRAPH = "methodgraph";
    4.21 @@ -80,6 +83,14 @@
    4.22  
    4.23      boolean isMaven();
    4.24  
    4.25 +    boolean isGradle();
    4.26 +
    4.27 +    boolean isAnt();
    4.28 +
    4.29 +    boolean isExtendedProperties();
    4.30 +
    4.31 +    boolean isAggressive();
    4.32 +
    4.33      boolean isQuiet();
    4.34  
    4.35      boolean isSelfReferences();
     5.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/SourcesInfo.java	Mon Aug 29 13:00:53 2016 -0400
     5.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/javac/SourcesInfo.java	Sat Sep 03 02:41:36 2016 -0400
     5.3 @@ -75,8 +75,8 @@
     5.4  
     5.5      public final EightBitStrings strings;
     5.6  
     5.7 -    public SourcesInfo(boolean eightBitStringsDisabled) {
     5.8 -        this.strings = new EightBitStrings(eightBitStringsDisabled);
     5.9 +    public SourcesInfo(boolean eightBitStringsDisabled, boolean aggressive) {
    5.10 +        this.strings = new EightBitStrings(eightBitStringsDisabled, aggressive);
    5.11      }
    5.12  
    5.13      // XXX rewrite to not hold references to ClassTree, just names,
     6.1 --- a/callgraph/src/main/java/org/netbeans/lib/callgraph/util/EightBitStrings.java	Mon Aug 29 13:00:53 2016 -0400
     6.2 +++ b/callgraph/src/main/java/org/netbeans/lib/callgraph/util/EightBitStrings.java	Sat Sep 03 02:41:36 2016 -0400
     6.3 @@ -70,14 +70,22 @@
     6.4      private final InternTable INTERN_TABLE = new InternTable();
     6.5      public final CharSequence DOT = create(".");
     6.6      public final CharSequence QUOTE = create("\"");
     6.7 +    public final CharSequence SPACE = create(" ");
     6.8 +    public final CharSequence QUOTE_SPACE = create("\" ");
     6.9      public final CharSequence CLOSE_OPEN_QUOTE = create("\" \"");
    6.10  
    6.11 -    private boolean disabled;
    6.12 +    private final boolean disabled;
    6.13 +    private final boolean aggressive;
    6.14  
    6.15      public EightBitStrings(boolean disabled) {
    6.16 -        this.disabled = disabled;
    6.17 +        this (disabled, false);
    6.18      }
    6.19  
    6.20 +    public EightBitStrings(boolean disabled, boolean aggressive) {
    6.21 +        this.disabled = disabled;
    6.22 +        this.aggressive = aggressive;
    6.23 +    }
    6.24 +    
    6.25      public void clear() {
    6.26          INTERN_TABLE.dispose();
    6.27      }
    6.28 @@ -87,6 +95,9 @@
    6.29              return string instanceof ComparableCharSequence ? (ComparableCharSequence) string
    6.30                      : new StringWrapper(string.toString());
    6.31          }
    6.32 +        if (aggressive) {
    6.33 +            return concat(string);
    6.34 +        }
    6.35          return INTERN_TABLE.intern(string);
    6.36      }
    6.37  
    6.38 @@ -100,32 +111,66 @@
    6.39                  sb.append(c);
    6.40              }
    6.41              return new StringWrapper(sb.toString());
    6.42 +        } else if (aggressive) {
    6.43 +            List<CharSequence> nue = new ArrayList<>(seqs.length + (seqs.length / 2));
    6.44 +            for (CharSequence seq : seqs) {
    6.45 +                int ln = seq.length();
    6.46 +                StringBuilder sb = new StringBuilder();
    6.47 +                for(int i=0; i < ln; i++) {
    6.48 +                    char c = seq.charAt(i);
    6.49 +                    if (Character.isLetter(c) || Character.isDigit(c)) {
    6.50 +                        sb.append(c);
    6.51 +                    } else {
    6.52 +                        nue.add(sb.toString());
    6.53 +                        sb = new StringBuilder();
    6.54 +                        nue.add(new String(new char[] { c }));
    6.55 +                    }
    6.56 +                }
    6.57 +                if (sb.length() > 0) {
    6.58 +                    nue.add(sb.toString());
    6.59 +                }
    6.60 +            }
    6.61 +            if (nue.size() != seqs.length) {
    6.62 +                seqs = nue.toArray(new CharSequence[nue.size()]);
    6.63 +            }
    6.64          }
    6.65          return new Concatenation(seqs);
    6.66      }
    6.67  
    6.68 -    public ComparableCharSequence concatQuoted(Collection<CharSequence> seqs) {
    6.69 +    public ComparableCharSequence concatQuoted(Collection<Object> seqs) {
    6.70          if (disabled) {
    6.71              StringBuilder sb = new StringBuilder("\"");
    6.72 -            for (Iterator<CharSequence> it = seqs.iterator(); it.hasNext();) {
    6.73 -                CharSequence c = it.next();
    6.74 +            boolean first = true;
    6.75 +            for (Iterator<Object> it = seqs.iterator(); it.hasNext();) {
    6.76 +                Object c = it.next();
    6.77 +                if (!first) {
    6.78 +                    sb.append(SPACE);
    6.79 +                }
    6.80 +                if (c instanceof CharSequence) {
    6.81 +                    sb.append(QUOTE);
    6.82 +                }
    6.83                  sb.append(c);
    6.84 -                if (it.hasNext()) {
    6.85 -                    sb.append("\" \"");
    6.86 -                } else {
    6.87 -                    sb.append("\"");
    6.88 +                if (c instanceof CharSequence) {
    6.89 +                    sb.append(QUOTE);
    6.90                  }
    6.91 +                first = false;
    6.92              }
    6.93              return new StringWrapper(sb.toString());
    6.94          } else {
    6.95 -            List<CharSequence> quoted = new ArrayList<>((seqs.size() * 2) + 1);
    6.96 -            quoted.add(this.QUOTE);
    6.97 -            for (Iterator<CharSequence> it = seqs.iterator(); it.hasNext();) {
    6.98 -                quoted.add(it.next());
    6.99 -                if (it.hasNext()) {
   6.100 -                    quoted.add(CLOSE_OPEN_QUOTE);
   6.101 +            List<CharSequence> quoted = new ArrayList<>((seqs.size() * 3) + 1);
   6.102 +            for (Iterator<Object> it = seqs.iterator(); it.hasNext();) {
   6.103 +                Object c = it.next();
   6.104 +                if (c instanceof CharSequence) {
   6.105 +                    quoted.add(QUOTE);
   6.106 +                    quoted.add((CharSequence)c);
   6.107 +                    if (it.hasNext()) {
   6.108 +                        quoted.add(QUOTE_SPACE);
   6.109 +                    } else {
   6.110 +                        quoted.add(QUOTE);
   6.111 +                    }
   6.112                  } else {
   6.113 -                    quoted.add(QUOTE);
   6.114 +                    quoted.add(create(c.toString()));
   6.115 +                    quoted.add(SPACE);
   6.116                  }
   6.117              }
   6.118              return new Concatenation(quoted.toArray(new CharSequence[quoted.size()]));
   6.119 @@ -153,6 +198,10 @@
   6.120      int internTableSize() {
   6.121          return INTERN_TABLE.last + 1;
   6.122      }
   6.123 +    
   6.124 +    List<CharSequence> dumpInternTable() {
   6.125 +        return INTERN_TABLE.dumpInternTable();
   6.126 +    }
   6.127  
   6.128      static class InternTable {
   6.129  
   6.130 @@ -187,6 +236,10 @@
   6.131              Arrays.sort(entries, 0, last + 1);
   6.132              return entry;
   6.133          }
   6.134 +        
   6.135 +        List<CharSequence> dumpInternTable() {
   6.136 +            return Arrays.asList(entries);
   6.137 +        }
   6.138  
   6.139          private static final class Entry implements ComparableCharSequence {
   6.140  
   6.141 @@ -316,10 +369,16 @@
   6.142          private final InternTable.Entry[] entries;
   6.143  
   6.144          Concatenation(CharSequence... entries) {
   6.145 -            this.entries = new InternTable.Entry[entries.length];
   6.146 -            for (int i = 0; i < entries.length; i++) {
   6.147 -                this.entries[i] = INTERN_TABLE.intern(entries[i]);
   6.148 +            List<InternTable.Entry> l = new ArrayList<>(entries.length);
   6.149 +            for (CharSequence cs : entries) {
   6.150 +                if (cs instanceof Concatenation) {
   6.151 +                    Concatenation c1 = (Concatenation) cs;
   6.152 +                    l.addAll(Arrays.asList(c1.entries));
   6.153 +                } else {
   6.154 +                    l.add(INTERN_TABLE.intern(cs));
   6.155 +                }
   6.156              }
   6.157 +            this.entries = l.toArray(new InternTable.Entry[l.size()]);
   6.158          }
   6.159  
   6.160          @Override
   6.161 @@ -358,7 +417,7 @@
   6.162                  sb.append(e);
   6.163              }
   6.164              if (debug) {
   6.165 -                sb.append (" - " + Arrays.asList(entries));
   6.166 +                sb.append(" - ").append (Arrays.asList(entries));
   6.167              }
   6.168              return sb.toString();
   6.169          }
     7.1 --- a/callgraph/src/test/java/org/netbeans/lib/callgraph/ArgumentsTest.java	Mon Aug 29 13:00:53 2016 -0400
     7.2 +++ b/callgraph/src/test/java/org/netbeans/lib/callgraph/ArgumentsTest.java	Sat Sep 03 02:41:36 2016 -0400
     7.3 @@ -175,4 +175,59 @@
     7.4          }
     7.5      }
     7.6  
     7.7 +    
     7.8 +    @Test
     7.9 +    public void testAntScan() throws IOException {
    7.10 +        File tmp = new File(System.getProperty("java.io.tmpdir"));
    7.11 +        File dir = new File(tmp, ArgumentsTest.class.getSimpleName() + "_" + System.currentTimeMillis());
    7.12 +        File project1 = new File(dir, "prj");
    7.13 +        File project2 = new File(dir, "prj2");
    7.14 +        File pom1 = new File(project1, "build.xml");
    7.15 +        File pom2 = new File(project2, "build.xml");
    7.16 +
    7.17 +        File java1 = new File(project1, "src");
    7.18 +        File java2 = new File(project2, "src");
    7.19 +
    7.20 +        assertTrue(java1.mkdirs());
    7.21 +        assertTrue(java2.mkdirs());
    7.22 +        pom1.createNewFile();
    7.23 +        pom2.createNewFile();
    7.24 +        dir = dir.getCanonicalFile();
    7.25 +        java1 = java1.getCanonicalFile();
    7.26 +        java2 = java2.getCanonicalFile();
    7.27 +        
    7.28 +        Arguments a = new Arguments("-v", dir.getAbsolutePath());
    7.29 +        assertEquals(1, a.folders().size());
    7.30 +        assertEquals(dir, a.folders().iterator().next());
    7.31 +        List<String> errors = new ArrayList<>();
    7.32 +        a.findAntSubfolders(errors);
    7.33 +        assertTrue(errors.isEmpty());
    7.34 +        System.out.println("FLDS: ");
    7.35 +        for (File f : a.folders()) {
    7.36 +            System.out.println("  " + f.getAbsolutePath());
    7.37 +        }
    7.38 +        assertEquals(2, a.folders().size());
    7.39 +        assertFalse(a.folders().isEmpty());
    7.40 +        assertTrue(a.folders().contains(java1));
    7.41 +        assertTrue(a.folders().contains(java2));
    7.42 +        try {
    7.43 +
    7.44 +            Arguments args = new Arguments("--ant", "-s", dir.getAbsolutePath());
    7.45 +            assertTrue(args.isAnt());
    7.46 +            assertTrue(args.isShortNames());
    7.47 +            assertTrue(args.isSelfReferences());
    7.48 +            assertTrue(args.folders().contains(java1));
    7.49 +            assertTrue(args.folders().contains(java2));
    7.50 +        } finally {
    7.51 +            // Clean up
    7.52 +            assertTrue(pom1.delete());
    7.53 +            assertTrue(pom2.delete());
    7.54 +            assertTrue(java1.delete());
    7.55 +            assertTrue(java2.delete());
    7.56 +            assertTrue(project1.delete());
    7.57 +            assertTrue(project2.delete());
    7.58 +            assertTrue(dir.delete());
    7.59 +        }
    7.60 +    }
    7.61 +    
    7.62  }
     8.1 --- a/callgraph/src/test/java/org/netbeans/lib/callgraph/util/SmallStringTest.java	Mon Aug 29 13:00:53 2016 -0400
     8.2 +++ b/callgraph/src/test/java/org/netbeans/lib/callgraph/util/SmallStringTest.java	Sat Sep 03 02:41:36 2016 -0400
     8.3 @@ -9,6 +9,7 @@
     8.4  import java.util.Set;
     8.5  import org.junit.Test;
     8.6  import static org.junit.Assert.*;
     8.7 +import org.netbeans.lib.callgraph.util.EightBitStrings.Concatenation;
     8.8  
     8.9  /**
    8.10   *
    8.11 @@ -120,7 +121,51 @@
    8.12          Collections.sort(l);
    8.13          assertEquals(l, Arrays.asList(a, b, c, d, e));
    8.14      }
    8.15 +    
    8.16 +    List<String> stringsOf(List<CharSequence> cs) {
    8.17 +        List<String> result = new ArrayList<>();
    8.18 +        for (CharSequence c : cs) {
    8.19 +            if (c == null) {
    8.20 +                break;
    8.21 +            }
    8.22 +            result.add(c.toString());
    8.23 +        }
    8.24 +        return result;
    8.25 +    }
    8.26 +    
    8.27 +    @Test
    8.28 +    public void testAggressive() {
    8.29 +        EightBitStrings strings = new EightBitStrings(false, true);
    8.30 +        ComparableCharSequence c = strings.concat("com.foo.", "bar.baz.", "Hey$You");
    8.31 +        assertEquals("com.foo.bar.baz.Hey$You", c.toString());
    8.32 +        ComparableCharSequence c2 = strings.concat("com.foo.", "bar.whoodle.", "Hey$You");
    8.33 +        List<String> interned = stringsOf(strings.dumpInternTable());
    8.34 +        for (String cs : interned) {
    8.35 +            if (cs == null) {
    8.36 +                break;
    8.37 +            }
    8.38 +            System.out.println("  " + cs);
    8.39 +        }
    8.40 +        assertTrue(interned.contains("com"));
    8.41 +        assertTrue(interned.contains("foo"));
    8.42 +        assertTrue(interned.contains("bar"));
    8.43 +        assertTrue(interned.contains("baz"));
    8.44 +        assertTrue(interned.contains("Hey"));
    8.45 +        assertTrue(interned.contains("You"));
    8.46 +        assertTrue(interned.contains("whoodle"));
    8.47 +        assertTrue(interned.contains("."));
    8.48 +        assertTrue(interned.contains("$"));
    8.49 +        assertTrue(interned.contains("\""));
    8.50 +    }
    8.51  
    8.52 +    @Test
    8.53 +    public void testConcatQuoted() {
    8.54 +        EightBitStrings strings = new EightBitStrings(false);
    8.55 +        List<Object> l = Arrays.asList(strings.create("hey"), false, 23, strings.create("bar"), "baz");
    8.56 +        CharSequence cc = strings.concatQuoted(l);
    8.57 +        assertEquals("\"hey\" false 23 \"bar\" \"baz\"", cc.toString());
    8.58 +    }
    8.59 +    
    8.60      private static String randomString(int len) {
    8.61          char[] c = new char[len];
    8.62          for (int i = 0; i < c.length; i++) {