]> source.dussan.org Git - aspectj.git/commitdiff
mavenized util module
authorAndy Clement <aclement@pivotal.io>
Wed, 23 Jan 2019 20:28:35 +0000 (12:28 -0800)
committerAndy Clement <aclement@pivotal.io>
Wed, 23 Jan 2019 20:28:35 +0000 (12:28 -0800)
41 files changed:
pom.xml [new file with mode: 0644]
util/.classpath
util/.cvsignore [deleted file]
util/.isJava5 [deleted file]
util/.project [deleted file]
util/.settings/org.eclipse.core.resources.prefs [new file with mode: 0644]
util/.settings/org.eclipse.jdt.apt.core.prefs [new file with mode: 0644]
util/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
util/build.xml [deleted file]
util/pom.xml [new file with mode: 0644]
util/pr107313-SourceLocation.patch [deleted file]
util/src/main/java/org/aspectj/util/FileUtil.java [new file with mode: 0644]
util/src/main/java/org/aspectj/util/FuzzyBoolean.java [new file with mode: 0644]
util/src/main/java/org/aspectj/util/GenericSignature.java [new file with mode: 0644]
util/src/main/java/org/aspectj/util/GenericSignatureParser.java [new file with mode: 0644]
util/src/main/java/org/aspectj/util/IStructureModel.java [new file with mode: 0644]
util/src/main/java/org/aspectj/util/LangUtil.java [new file with mode: 0644]
util/src/main/java/org/aspectj/util/PartialOrder.java [new file with mode: 0644]
util/src/main/java/org/aspectj/util/Reflection.java [new file with mode: 0644]
util/src/main/java/org/aspectj/util/SoftHashMap.java [new file with mode: 0644]
util/src/main/java/org/aspectj/util/TypeSafeEnum.java [new file with mode: 0644]
util/src/main/java/org/aspectj/util/UtilClassLoader.java [new file with mode: 0644]
util/src/org/aspectj/util/FileUtil.java [deleted file]
util/src/org/aspectj/util/FuzzyBoolean.java [deleted file]
util/src/org/aspectj/util/GenericSignature.java [deleted file]
util/src/org/aspectj/util/GenericSignatureParser.java [deleted file]
util/src/org/aspectj/util/IStructureModel.java [deleted file]
util/src/org/aspectj/util/LangUtil.java [deleted file]
util/src/org/aspectj/util/PartialOrder.java [deleted file]
util/src/org/aspectj/util/Reflection.java [deleted file]
util/src/org/aspectj/util/SoftHashMap.java [deleted file]
util/src/org/aspectj/util/TypeSafeEnum.java [deleted file]
util/src/org/aspectj/util/UtilClassLoader.java [deleted file]
util/src/test/java/org/aspectj/util/FileUtilTest.java [new file with mode: 0644]
util/src/test/java/org/aspectj/util/GenericSignatureParserTest.java [new file with mode: 0644]
util/src/test/java/org/aspectj/util/LangUtilTest.java [new file with mode: 0644]
util/testsrc/org/aspectj/util/FileUtilTest.java [deleted file]
util/testsrc/org/aspectj/util/GenericSignatureParserTest.java [deleted file]
util/testsrc/org/aspectj/util/LangUtilTest.java [deleted file]
util/testsrc/org/aspectj/util/UtilModuleTests.java [deleted file]
util/testsrc/org/aspectj/util/UtilTests.java [deleted file]

diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..35ca85e
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.aspectj</groupId>
+    <artifactId>aspectj-parent</artifactId>
+    <packaging>pom</packaging>
+    <version>${revision}</version>
+    <name>AspectJ Parent Project</name>
+    
+    <properties>
+        <revision>1.9.3.BUILD-SNAPSHOT</revision>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <modules>
+       <module>util</module>
+        <module>bcel</module>
+    </modules>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <configuration>
+                        <source>1.8</source>
+                        <target>1.8</target>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <configuration>
+                        <testFailureIgnore>true</testFailureIgnore>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.1</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
index f33c5028466ef25d4c1aa602358028ad5eba291e..f0257c5a54142160c682eb3de6435e2e5ec2d03a 100644 (file)
@@ -1,8 +1,44 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="src" path="testsrc"/>
-       <classpathentry kind="lib" path="/lib/junit/junit.jar" sourcepath="/lib/junit/junit-src.jar"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-       <classpathentry kind="output" path="bin"/>
+       <classpathentry kind="src" output="target/classes" path="src/main/java">
+               <attributes>
+                       <attribute name="optional" value="true"/>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="src" output="target/test-classes" path="src/test/java">
+               <attributes>
+                       <attribute name="optional" value="true"/>
+                       <attribute name="maven.pomderived" value="true"/>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+               <attributes>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+               <attributes>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="src" path="target/generated-sources/annotations">
+               <attributes>
+                       <attribute name="optional" value="true"/>
+                       <attribute name="maven.pomderived" value="true"/>
+                       <attribute name="ignore_optional_problems" value="true"/>
+                       <attribute name="m2e-apt" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
+               <attributes>
+                       <attribute name="optional" value="true"/>
+                       <attribute name="maven.pomderived" value="true"/>
+                       <attribute name="ignore_optional_problems" value="true"/>
+                       <attribute name="m2e-apt" value="true"/>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="output" path="target/classes"/>
 </classpath>
diff --git a/util/.cvsignore b/util/.cvsignore
deleted file mode 100644 (file)
index dbb8143..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-bin
-
-bintest
diff --git a/util/.isJava5 b/util/.isJava5
deleted file mode 100644 (file)
index 136d063..0000000
+++ /dev/null
@@ -1 +0,0 @@
-  
\ No newline at end of file
diff --git a/util/.project b/util/.project
deleted file mode 100644 (file)
index ee97704..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-       <name>util</name>
-       <comment></comment>
-       <projects>
-       </projects>
-       <buildSpec>
-               <buildCommand>
-                       <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-       </buildSpec>
-       <natures>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-       </natures>
-</projectDescription>
diff --git a/util/.settings/org.eclipse.core.resources.prefs b/util/.settings/org.eclipse.core.resources.prefs
new file mode 100644 (file)
index 0000000..f9fe345
--- /dev/null
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding/<project>=UTF-8
diff --git a/util/.settings/org.eclipse.jdt.apt.core.prefs b/util/.settings/org.eclipse.jdt.apt.core.prefs
new file mode 100644 (file)
index 0000000..d4313d4
--- /dev/null
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.apt.aptEnabled=false
diff --git a/util/.settings/org.eclipse.jdt.core.prefs b/util/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..5592a0a
--- /dev/null
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.processAnnotations=disabled
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/util/build.xml b/util/build.xml
deleted file mode 100644 (file)
index 05bdade..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0"?>
-<!-- see ../build/*.html for explanation -->
-<project name="util" default="test" basedir=".">
-    <import file="${basedir}/../build/build.xml"/>  
-</project>
-
diff --git a/util/pom.xml b/util/pom.xml
new file mode 100644 (file)
index 0000000..d53319f
--- /dev/null
@@ -0,0 +1,17 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>util</artifactId>
+  <packaging>jar</packaging>
+  <name>util</name>
+
+  <parent>
+    <groupId>org.aspectj</groupId>
+    <artifactId>aspectj-parent</artifactId>
+    <version>${revision}</version>
+    <relativePath>..</relativePath>
+  </parent>
+
+</project>
diff --git a/util/pr107313-SourceLocation.patch b/util/pr107313-SourceLocation.patch
deleted file mode 100644 (file)
index 63fd61f..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-Index: src/org/aspectj/util/ConfigParser.java
-===================================================================
-RCS file: /home/technology/org.aspectj/modules/util/src/org/aspectj/util/ConfigParser.java,v
-retrieving revision 1.6
-diff -u -r1.6 ConfigParser.java
---- src/org/aspectj/util/ConfigParser.java     5 Aug 2004 17:31:56 -0000       1.6
-+++ src/org/aspectj/util/ConfigParser.java     2 Sep 2005 07:40:04 -0000
-@@ -21,11 +21,14 @@
- public class ConfigParser {
-     Location location;
-     protected File relativeDirectory = null;
--    protected List files = new LinkedList();
-+    protected List files = new ArrayList();
-+    protected Map userFileMap = new HashMap(); // from File -> File, keys are canonical files, values are the 'user' path
-     private boolean fileParsed = false;
-     protected static String CONFIG_MSG = "build config error: ";  
-     
-     public List getFiles() { return files; }
-+    
-+    public Map getUserFileNameMap() { return userFileMap; }
-     public void parseCommandLine(String[] argsArray) throws ParseException {
-         location = new CommandLineLocation();
-@@ -221,7 +224,9 @@
-           ret = new File(dir, name);
-         }
-         try {
--              ret = ret.getCanonicalFile();
-+              File canonicalFile = ret.getCanonicalFile();
-+              userFileMap.put(canonicalFile,new File(name));
-+              ret = canonicalFile;
-         } catch (IOException ioEx) {
-               // proceed without canonicalization
-               // so nothing to do here
diff --git a/util/src/main/java/org/aspectj/util/FileUtil.java b/util/src/main/java/org/aspectj/util/FileUtil.java
new file mode 100644 (file)
index 0000000..f3db2bd
--- /dev/null
@@ -0,0 +1,1577 @@
+/* *******************************************************************
+ * Copyright (c) 1999-2001 Xerox Corporation, 
+ *               2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/epl-v10.html 
+ *  
+ * Contributors: 
+ *     Xerox/PARC     initial implementation 
+ * ******************************************************************/
+
+package org.aspectj.util;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * @author Andy Clement
+ * @author Kris De Volder
+ */
+public class FileUtil {
+       /** default parent directory File when a file has a null parent */
+       public static final File DEFAULT_PARENT = new File("."); // XXX user.dir?
+
+       /** unmodifiable List of String source file suffixes (including leading ".") */
+       public static final List<String> SOURCE_SUFFIXES = Collections.unmodifiableList(Arrays.asList(new String[] { ".java", ".aj" }));
+
+       public static final FileFilter ZIP_FILTER = new FileFilter() {
+               public boolean accept(File file) {
+                       return isZipFile(file);
+               }
+
+               public String toString() {
+                       return "ZIP_FILTER";
+               }
+       };
+
+       // public static final FileFilter SOURCE_FILTER = new FileFilter() {
+       // public boolean accept(File file) {
+       // return hasSourceSuffix(file);
+       // }
+       //
+       // public String toString() {
+       // return "SOURCE_FILTER";
+       // }
+       // };
+
+       final static int[] INT_RA = new int[0];
+
+       /** accept all files */
+       public static final FileFilter ALL = new FileFilter() {
+               public boolean accept(File f) {
+                       return true;
+               }
+       };
+       public static final FileFilter DIRS_AND_WRITABLE_CLASSES = new FileFilter() {
+               public boolean accept(File file) {
+                       return ((null != file) && (file.isDirectory() || (file.canWrite() && file.getName().toLowerCase().endsWith(".class"))));
+               }
+       };
+       private static final boolean PERMIT_CVS;
+       static {
+               String name = FileUtil.class.getName() + ".PERMIT_CVS";
+               PERMIT_CVS = LangUtil.getBoolean(name, false);
+       }
+
+       /** @return true if file exists and is a zip file */
+       public static boolean isZipFile(File file) {
+               try {
+                       return (null != file) && new ZipFile(file) != null;
+               } catch (IOException e) {
+                       return false;
+               }
+       }
+
+       /** @return true if path ends with .zip or .jar */
+       // public static boolean hasZipSuffix(String path) {
+       // return ((null != path) && (0 != zipSuffixLength(path)));
+       // }
+       /** @return 0 if file has no zip/jar suffix or 4 otherwise */
+       public static int zipSuffixLength(File file) {
+               return (null == file ? 0 : zipSuffixLength(file.getPath()));
+       }
+
+       /** @return 0 if no zip/jar suffix or 4 otherwise */
+       public static int zipSuffixLength(String path) {
+               if ((null != path) && (4 < path.length())) {
+                       String test = path.substring(path.length() - 4).toLowerCase();
+                       if (".zip".equals(test) || ".jar".equals(test)) {
+                               return 4;
+                       }
+               }
+               return 0;
+       }
+
+       /** @return true if file path has a source suffix */
+       public static boolean hasSourceSuffix(File file) {
+               return ((null != file) && hasSourceSuffix(file.getPath()));
+       }
+
+       /** @return true if path ends with .java or .aj */
+       public static boolean hasSourceSuffix(String path) {
+               return ((null != path) && (0 != sourceSuffixLength(path)));
+       }
+
+       /**
+        * @return 0 if file has no source suffix or the length of the suffix otherwise
+        */
+       public static int sourceSuffixLength(File file) {
+               return (null == file ? 0 : sourceSuffixLength(file.getPath()));
+       }
+
+       /** @return 0 if no source suffix or the length of the suffix otherwise */
+       public static int sourceSuffixLength(String path) {
+               if (LangUtil.isEmpty(path)) {
+                       return 0;
+               }
+
+               for (Iterator<String> iter = SOURCE_SUFFIXES.iterator(); iter.hasNext();) {
+                       String suffix = iter.next();
+                       if (path.endsWith(suffix) || path.toLowerCase().endsWith(suffix)) {
+                               return suffix.length();
+                       }
+               }
+               return 0;
+       }
+
+       /** @return true if this is a readable directory */
+       public static boolean canReadDir(File dir) {
+               return ((null != dir) && dir.canRead() && dir.isDirectory());
+       }
+
+       /** @return true if this is a readable file */
+       public static boolean canReadFile(File file) {
+               return ((null != file) && file.canRead() && file.isFile());
+       }
+
+       /** @return true if dir is a writable directory */
+       public static boolean canWriteDir(File dir) {
+               return ((null != dir) && dir.canWrite() && dir.isDirectory());
+       }
+
+       /** @return true if this is a writable file */
+       public static boolean canWriteFile(File file) {
+               return ((null != file) && file.canWrite() && file.isFile());
+       }
+
+       // /**
+       // * @throws IllegalArgumentException unless file is readable and not a
+       // * directory
+       // */
+       // public static void throwIaxUnlessCanReadFile(File file, String label) {
+       // if (!canReadFile(file)) {
+       // throw new IllegalArgumentException(label + " not readable file: " +
+       // file);
+       // }
+       // }
+
+       /**
+        * @throws IllegalArgumentException unless dir is a readable directory
+        */
+       public static void throwIaxUnlessCanReadDir(File dir, String label) {
+               if (!canReadDir(dir)) {
+                       throw new IllegalArgumentException(label + " not readable dir: " + dir);
+               }
+       }
+
+       /**
+        * @throws IllegalArgumentException unless file is readable and not a directory
+        */
+       public static void throwIaxUnlessCanWriteFile(File file, String label) {
+               if (!canWriteFile(file)) {
+                       throw new IllegalArgumentException(label + " not writable file: " + file);
+               }
+       }
+
+       /** @throws IllegalArgumentException unless dir is a readable directory */
+       public static void throwIaxUnlessCanWriteDir(File dir, String label) {
+               if (!canWriteDir(dir)) {
+                       throw new IllegalArgumentException(label + " not writable dir: " + dir);
+               }
+       }
+
+       /** @return array same length as input, with String paths */
+       public static String[] getPaths(File[] files) {
+               if ((null == files) || (0 == files.length)) {
+                       return new String[0];
+               }
+               String[] result = new String[files.length];
+               for (int i = 0; i < result.length; i++) {
+                       if (null != files[i]) {
+                               result[i] = files[i].getPath();
+                       }
+               }
+               return result;
+       }
+
+       /** @return array same length as input, with String paths */
+       public static String[] getPaths(List<File> files) {
+               final int size = (null == files ? 0 : files.size());
+               if (0 == size) {
+                       return new String[0];
+               }
+               String[] result = new String[size];
+               for (int i = 0; i < size; i++) {
+                       File file = files.get(i);
+                       if (null != file) {
+                               result[i] = file.getPath();
+                       }
+               }
+               return result;
+       }
+
+       /**
+        * Extract the name of a class from the path to its file. If the basedir is null, then the class is assumed to be in the default
+        * package unless the classFile has one of the top-level suffixes { com, org, java, javax } as a parent directory.
+        *
+        * @param basedir the File of the base directory (prefix of classFile)
+        * @param classFile the File of the class to extract the name for
+        * @throws IllegalArgumentException if classFile is null or does not end with ".class" or a non-null basedir is not a prefix of
+        *         classFile
+        */
+       public static String fileToClassName(File basedir, File classFile) {
+               LangUtil.throwIaxIfNull(classFile, "classFile");
+               String classFilePath = normalizedPath(classFile);
+               if (!classFilePath.endsWith(".class")) {
+                       String m = classFile + " does not end with .class";
+                       throw new IllegalArgumentException(m);
+               }
+               classFilePath = classFilePath.substring(0, classFilePath.length() - 6);
+               if (null != basedir) {
+                       String basePath = normalizedPath(basedir);
+                       if (!classFilePath.startsWith(basePath)) {
+                               String m = classFile + " does not start with " + basedir;
+                               throw new IllegalArgumentException(m);
+                       }
+                       classFilePath = classFilePath.substring(basePath.length() + 1);
+               } else {
+                       final String[] suffixes = new String[] { "com", "org", "java", "javax" };
+                       boolean found = false;
+                       for (int i = 0; !found && (i < suffixes.length); i++) {
+                               int loc = classFilePath.indexOf(suffixes[i] + "/");
+                               if ((0 == loc) || ((-1 != loc) && ('/' == classFilePath.charAt(loc - 1)))) {
+                                       classFilePath = classFilePath.substring(loc);
+                                       found = true;
+                               }
+                       }
+                       if (!found) {
+                               int loc = classFilePath.lastIndexOf("/");
+                               if (-1 != loc) { // treat as default package
+                                       classFilePath = classFilePath.substring(loc + 1);
+                               }
+                       }
+               }
+               return classFilePath.replace('/', '.');
+       }
+
+       /**
+        * Normalize path for comparisons by rendering absolute, clipping basedir prefix, trimming and changing '\\' to '/'
+        *
+        * @param file the File with the path to normalize
+        * @param basedir the File for the prefix of the file to normalize - ignored if null
+        * @return "" if null or normalized path otherwise
+        * @throws IllegalArgumentException if basedir is not a prefix of file
+        */
+       public static String normalizedPath(File file, File basedir) {
+               String filePath = normalizedPath(file);
+               if (null != basedir) {
+                       String basePath = normalizedPath(basedir);
+                       if (filePath.startsWith(basePath)) {
+                               filePath = filePath.substring(basePath.length());
+                               if (filePath.startsWith("/")) {
+                                       filePath = filePath.substring(1);
+                               }
+                       }
+               }
+               return filePath;
+       }
+
+       /**
+        * Render a set of files to String as a path by getting absolute paths of each and delimiting with infix.
+        *
+        * @param files the File[] to flatten - may be null or empty
+        * @param infix the String delimiter internally between entries (if null, then use File.pathSeparator). (alias to
+        *        <code>flatten(getAbsolutePaths(files), infix)</code>
+        * @return String with absolute paths to entries in order, delimited with infix
+        */
+       public static String flatten(File[] files, String infix) {
+               if (LangUtil.isEmpty(files)) {
+                       return "";
+               }
+               return flatten(getPaths(files), infix);
+       }
+
+       /**
+        * Flatten File[] to String.
+        *
+        * @param files the File[] of paths to flatten - null ignored
+        * @param infix the String infix to use - null treated as File.pathSeparator
+        */
+       public static String flatten(String[] paths, String infix) {
+               if (null == infix) {
+                       infix = File.pathSeparator;
+               }
+               StringBuffer result = new StringBuffer();
+               boolean first = true;
+               for (int i = 0; i < paths.length; i++) {
+                       String path = paths[i];
+                       if (null == path) {
+                               continue;
+                       }
+                       if (first) {
+                               first = false;
+                       } else {
+                               result.append(infix);
+                       }
+                       result.append(path);
+               }
+               return result.toString();
+       }
+
+       /**
+        * Normalize path for comparisons by rendering absolute trimming and changing '\\' to '/'
+        *
+        * @return "" if null or normalized path otherwise
+        */
+       public static String normalizedPath(File file) {
+               return (null == file ? "" : weakNormalize(file.getAbsolutePath()));
+       }
+
+       /**
+        * Weakly normalize path for comparisons by trimming and changing '\\' to '/'
+        */
+       public static String weakNormalize(String path) {
+               if (null != path) {
+                       path = path.replace('\\', '/').trim();
+               }
+               return path;
+       }
+
+       /**
+        * Get best File for the first-readable path in input paths, treating entries prefixed "sp:" as system property keys. Safe to
+        * call in static initializers.
+        *
+        * @param paths the String[] of paths to check.
+        * @return null if not found, or valid File otherwise
+        */
+       public static File getBestFile(String[] paths) {
+               if (null == paths) {
+                       return null;
+               }
+               File result = null;
+               for (int i = 0; (null == result) && (i < paths.length); i++) {
+                       String path = paths[i];
+                       if (null == path) {
+                               continue;
+                       }
+                       if (path.startsWith("sp:")) {
+                               try {
+                                       path = System.getProperty(path.substring(3));
+                               } catch (Throwable t) {
+                                       path = null;
+                               }
+                               if (null == path) {
+                                       continue;
+                               }
+                       }
+                       try {
+                               File f = new File(path);
+                               if (f.exists() && f.canRead()) {
+                                       result = FileUtil.getBestFile(f);
+                               }
+                       } catch (Throwable t) {
+                               // swallow
+                       }
+               }
+               return result;
+       }
+
+       /**
+        * Render as best file, canonical or absolute.
+        *
+        * @param file the File to get the best File for (not null)
+        * @return File of the best-available path
+        * @throws IllegalArgumentException if file is null
+        */
+       public static File getBestFile(File file) {
+               LangUtil.throwIaxIfNull(file, "file");
+               if (file.exists()) {
+                       try {
+                               return file.getCanonicalFile();
+                       } catch (IOException e) {
+                               return file.getAbsoluteFile();
+                       }
+               } else {
+                       return file;
+               }
+       }
+
+       /**
+        * Render as best path, canonical or absolute.
+        *
+        * @param file the File to get the path for (not null)
+        * @return String of the best-available path
+        * @throws IllegalArgumentException if file is null
+        */
+       public static String getBestPath(File file) {
+               LangUtil.throwIaxIfNull(file, "file");
+               if (file.exists()) {
+                       try {
+                               return file.getCanonicalPath();
+                       } catch (IOException e) {
+                               return file.getAbsolutePath();
+                       }
+               } else {
+                       return file.getPath();
+               }
+       }
+
+       /** @return array same length as input, with String absolute paths */
+       public static String[] getAbsolutePaths(File[] files) {
+               if ((null == files) || (0 == files.length)) {
+                       return new String[0];
+               }
+               String[] result = new String[files.length];
+               for (int i = 0; i < result.length; i++) {
+                       if (null != files[i]) {
+                               result[i] = files[i].getAbsolutePath();
+                       }
+               }
+               return result;
+       }
+
+       /**
+        * Recursively delete the contents of dir, but not the dir itself
+        *
+        * @return the total number of files deleted
+        */
+       public static int deleteContents(File dir) {
+               return deleteContents(dir, ALL);
+       }
+
+       /**
+        * Recursively delete some contents of dir, but not the dir itself. This deletes any subdirectory which is empty after its files
+        * are deleted.
+        *
+        * @return the total number of files deleted
+        */
+       public static int deleteContents(File dir, FileFilter filter) {
+               return deleteContents(dir, filter, true);
+       }
+
+       /**
+        * Recursively delete some contents of dir, but not the dir itself. If deleteEmptyDirs is true, this deletes any subdirectory
+        * which is empty after its files are deleted.
+        *
+        * @param dir the File directory (if a file, the the file is deleted)
+        * @return the total number of files deleted
+        */
+       public static int deleteContents(File dir, FileFilter filter,
+                       boolean deleteEmptyDirs) {
+               if (null == dir) {
+                       throw new IllegalArgumentException("null dir");
+               }
+               if ((!dir.exists()) || (!dir.canWrite())) {
+                       return 0;
+               }
+               if (!dir.isDirectory()) {
+                       dir.delete();
+                       return 1;
+               }
+               String[] fromFiles = dir.list();
+               if (fromFiles == null) {
+                       return 0;
+               }
+               int result = 0;
+               for (int i = 0; i < fromFiles.length; i++) {
+                       String string = fromFiles[i];
+                       File file = new File(dir, string);
+                       if ((null == filter) || filter.accept(file)) {
+                               if (file.isDirectory()) {
+                                       result += deleteContents(file, filter, deleteEmptyDirs);
+                                       String[] fileContent = file.list();
+                                       if (deleteEmptyDirs && fileContent != null
+                                                       && 0 == fileContent.length) {
+                                               file.delete();
+                                       }
+                               } else {
+                                       /* boolean ret = */
+                                       file.delete();
+                                       result++;
+                               }
+                       }
+               }
+               return result;
+       }
+
+       /**
+        * Copy contents of fromDir into toDir
+        *
+        * @param fromDir must exist and be readable
+        * @param toDir must exist or be creatable and be writable
+        * @return the total number of files copied
+        */
+       public static int copyDir(File fromDir, File toDir) throws IOException {
+               return copyDir(fromDir, toDir, null, null);
+       }
+
+       /**
+        * Recursively copy files in fromDir (with any fromSuffix) to toDir, replacing fromSuffix with toSuffix if any. This silently
+        * ignores dirs and files that are not readable but throw IOException for directories that are not writable. This does not clean
+        * out the original contents of toDir. (subdirectories are not renamed per directory rules)
+        *
+        * @param fromSuffix select files with this suffix - select all if null or empty
+        * @param toSuffix replace fromSuffix with toSuffix in the destination file name - ignored if null or empty, appended to name if
+        *        fromSuffix is null or empty
+        * @return the total number of files copied
+        */
+       public static int copyDir(File fromDir, File toDir, final String fromSuffix, String toSuffix) throws IOException {
+               return copyDir(fromDir, toDir, fromSuffix, toSuffix, (FileFilter) null);
+       }
+
+       // /**
+       // * Recursively copy files in fromDir (with any fromSuffix) to toDir,
+       // * replacing fromSuffix with toSuffix if any, and adding the destination
+       // * file to any collector. This silently ignores dirs and files that are
+       // not
+       // * readable but throw IOException for directories that are not writable.
+       // * This does not clean out the original contents of toDir. (subdirectories
+       // * are not renamed per directory rules) This calls any delegate
+       // * FilenameFilter to collect any selected file.
+       // *
+       // * @param fromSuffix select files with this suffix - select all if null or
+       // * empty
+       // * @param toSuffix replace fromSuffix with toSuffix in the destination
+       // file
+       // * name - ignored if null or empty, appended to name if
+       // * fromSuffix is null or empty
+       // * @param collector the List sink for destination files - ignored if null
+       // * @return the total number of files copied
+       // */
+       // public static int copyDir(File fromDir, File toDir, final String
+       // fromSuffix, final String toSuffix, final List collector)
+       // throws IOException {
+       // // int before = collector.size();
+       // if (null == collector) {
+       // return copyDir(fromDir, toDir, fromSuffix, toSuffix);
+       // } else {
+       // FileFilter collect = new FileFilter() {
+       // public boolean accept(File pathname) {
+       // return collector.add(pathname);
+       // }
+       // };
+       // return copyDir(fromDir, toDir, fromSuffix, toSuffix, collect);
+       // }
+       // }
+
+       /**
+        * Recursively copy files in fromDir (with any fromSuffix) to toDir, replacing fromSuffix with toSuffix if any. This silently
+        * ignores dirs and files that are not readable but throw IOException for directories that are not writable. This does not clean
+        * out the original contents of toDir. (subdirectories are not renamed per directory rules) This calls any delegate
+        * FilenameFilter to collect any selected file.
+        *
+        * @param fromSuffix select files with this suffix - select all if null or empty
+        * @param toSuffix replace fromSuffix with toSuffix in the destination file name - ignored if null or empty, appended to name if
+        *        fromSuffix is null or empty
+        * @return the total number of files copied
+        */
+       public static int copyDir(File fromDir, File toDir, final String fromSuffix, final String toSuffix, final FileFilter delegate)
+                       throws IOException {
+
+               if ((null == fromDir) || (!fromDir.canRead())) {
+                       return 0;
+               }
+               final boolean haveSuffix = ((null != fromSuffix) && (0 < fromSuffix.length()));
+               final int slen = (!haveSuffix ? 0 : fromSuffix.length());
+
+               if (!toDir.exists()) {
+                       toDir.mkdirs();
+               }
+               final String[] fromFiles;
+               if (!haveSuffix) {
+                       fromFiles = fromDir.list();
+               } else {
+                       FilenameFilter filter = new FilenameFilter() {
+                               public boolean accept(File dir, String name) {
+                                       return (new File(dir, name).isDirectory() || (name.endsWith(fromSuffix)));
+                               }
+                       };
+                       fromFiles = fromDir.list(filter);
+               }
+               int result = 0;
+               final int MAX = (null == fromFiles ? 0 : fromFiles.length);
+               for (int i = 0; i < MAX; i++) {
+                       String filename = fromFiles[i];
+                       File fromFile = new File(fromDir, filename);
+                       if (fromFile.canRead()) {
+                               if (fromFile.isDirectory()) {
+                                       result += copyDir(fromFile, new File(toDir, filename), fromSuffix, toSuffix, delegate);
+                               } else if (fromFile.isFile()) {
+                                       if (haveSuffix) {
+                                               filename = filename.substring(0, filename.length() - slen);
+                                       }
+                                       if (null != toSuffix) {
+                                               filename = filename + toSuffix;
+                                       }
+                                       File targetFile = new File(toDir, filename);
+                                       if ((null == delegate) || delegate.accept(targetFile)) {
+                                               copyFile(fromFile, targetFile);
+                                       }
+                                       result++;
+                               }
+                       }
+               }
+               return result;
+       }
+
+       /**
+        * Recursively list files in srcDir.
+        *
+        * @return ArrayList with String paths of File under srcDir (relative to srcDir)
+        */
+       public static String[] listFiles(File srcDir) {
+               ArrayList<String> result = new ArrayList<String>();
+               if ((null != srcDir) && srcDir.canRead()) {
+                       listFiles(srcDir, null, result);
+               }
+               return result.toArray(new String[0]);
+       }
+
+       public static final FileFilter aspectjSourceFileFilter = new FileFilter() {
+               public boolean accept(File pathname) {
+                       String name = pathname.getName().toLowerCase();
+                       return name.endsWith(".java") || name.endsWith(".aj");
+               }
+       };
+
+       /**
+        * Recursively list files in srcDir.
+        *
+        * @return ArrayList with String paths of File under srcDir (relative to srcDir)
+        */
+       public static File[] listFiles(File srcDir, FileFilter fileFilter) {
+               ArrayList<File> result = new ArrayList<File>();
+               if ((null != srcDir) && srcDir.canRead()) {
+                       listFiles(srcDir, result, fileFilter);
+               }
+               return result.toArray(new File[result.size()]);
+       }
+
+       /**
+        * Recursively list .class files in specified directory
+        *
+        * @return List of File objects
+        */
+       public static List<File> listClassFiles(File dir) {
+               ArrayList<File> result = new ArrayList<File>();
+               if ((null != dir) && dir.canRead()) {
+                       listClassFiles(dir, result);
+               }
+               return result;
+       }
+
+       /**
+        * Convert String[] paths to File[] as offset of base directory
+        *
+        * @param basedir the non-null File base directory for File to create with paths
+        * @param paths the String[] of paths to create
+        * @return File[] with same length as paths
+        */
+       public static File[] getBaseDirFiles(File basedir, String[] paths) {
+               return getBaseDirFiles(basedir, paths, (String[]) null);
+       }
+
+       /**
+        * Convert String[] paths to File[] as offset of base directory
+        *
+        * @param basedir the non-null File base directory for File to create with paths
+        * @param paths the String[] of paths to create
+        * @param suffixes the String[] of suffixes to limit sources to - ignored if null
+        * @return File[] with same length as paths
+        */
+       public static File[] getBaseDirFiles(File basedir, String[] paths, String[] suffixes) {
+               LangUtil.throwIaxIfNull(basedir, "basedir");
+               LangUtil.throwIaxIfNull(paths, "paths");
+               File[] result = null;
+               if (!LangUtil.isEmpty(suffixes)) {
+                       ArrayList<File> list = new ArrayList<File>();
+                       for (int i = 0; i < paths.length; i++) {
+                               String path = paths[i];
+                               for (int j = 0; j < suffixes.length; j++) {
+                                       if (path.endsWith(suffixes[j])) {
+                                               list.add(new File(basedir, paths[i]));
+                                               break;
+                                       }
+                               }
+                       }
+                       result = list.toArray(new File[0]);
+               } else {
+                       result = new File[paths.length];
+                       for (int i = 0; i < result.length; i++) {
+                               result[i] = newFile(basedir, paths[i]);
+                       }
+               }
+               return result;
+       }
+
+       /**
+        * Create a new File, resolving paths ".." and "." specially.
+        *
+        * @param dir the File for the parent directory of the file
+        * @param path the path in the parent directory (filename only?)
+        * @return File for the new file.
+        */
+       private static File newFile(File dir, String path) {
+               if (".".equals(path)) {
+                       return dir;
+               } else if ("..".equals(path)) {
+                       File parentDir = dir.getParentFile();
+                       if (null != parentDir) {
+                               return parentDir;
+                       } else {
+                               return new File(dir, "..");
+                       }
+               } else {
+                       return new File(dir, path);
+               }
+       }
+
+       /**
+        * Copy files from source dir into destination directory, creating any needed directories. This differs from copyDir in not
+        * being recursive; each input with the source dir creates a full path. However, if the source is a directory, it is copied as
+        * such.
+        *
+        * @param srcDir an existing, readable directory containing relativePaths files
+        * @param relativePaths a set of paths relative to srcDir to readable File to copy
+        * @param destDir an existing, writable directory to copy files to
+        * @throws IllegalArgumentException if input invalid, IOException if operations fail
+        */
+       public static File[] copyFiles(File srcDir, String[] relativePaths, File destDir) throws IllegalArgumentException, IOException {
+               final String[] paths = relativePaths;
+               throwIaxUnlessCanReadDir(srcDir, "srcDir");
+               throwIaxUnlessCanWriteDir(destDir, "destDir");
+               LangUtil.throwIaxIfNull(paths, "relativePaths");
+               File[] result = new File[paths.length];
+               for (int i = 0; i < paths.length; i++) {
+                       String path = paths[i];
+                       LangUtil.throwIaxIfNull(path, "relativePaths-entry");
+                       File src = newFile(srcDir, paths[i]);
+                       File dest = newFile(destDir, path);
+                       File destParent = dest.getParentFile();
+                       if (!destParent.exists()) {
+                               destParent.mkdirs();
+                       }
+                       LangUtil.throwIaxIfFalse(canWriteDir(destParent), "dest-entry-parent");
+                       copyFile(src, dest); // both file-dir and dir-dir copies
+                       result[i] = dest;
+               }
+               return result;
+       }
+
+       /**
+        * Copy fromFile to toFile, handling file-file, dir-dir, and file-dir copies.
+        *
+        * @param fromFile the File path of the file or directory to copy - must be readable
+        * @param toFile the File path of the target file or directory - must be writable (will be created if it does not exist)
+        */
+       public static void copyFile(File fromFile, File toFile) throws IOException {
+               LangUtil.throwIaxIfNull(fromFile, "fromFile");
+               LangUtil.throwIaxIfNull(toFile, "toFile");
+               LangUtil.throwIaxIfFalse(!toFile.equals(fromFile), "same file");
+               if (toFile.isDirectory()) { // existing directory
+                       throwIaxUnlessCanWriteDir(toFile, "toFile");
+                       if (fromFile.isFile()) { // file-dir
+                               File targFile = new File(toFile, fromFile.getName());
+                               copyValidFiles(fromFile, targFile);
+                       } else if (fromFile.isDirectory()) { // dir-dir
+                               copyDir(fromFile, toFile);
+                       } else {
+                               LangUtil.throwIaxIfFalse(false, "not dir or file: " + fromFile);
+                       }
+               } else if (toFile.isFile()) { // target file exists
+                       if (fromFile.isDirectory()) {
+                               LangUtil.throwIaxIfFalse(false, "can't copy to file dir: " + fromFile);
+                       }
+                       copyValidFiles(fromFile, toFile); // file-file
+               } else { // target file is a non-existent path -- could be file or dir
+                       /* File toFileParent = */ensureParentWritable(toFile);
+                       if (fromFile.isFile()) {
+                               copyValidFiles(fromFile, toFile);
+                       } else if (fromFile.isDirectory()) {
+                               toFile.mkdirs();
+                               throwIaxUnlessCanWriteDir(toFile, "toFile");
+                               copyDir(fromFile, toFile);
+                       } else {
+                               LangUtil.throwIaxIfFalse(false, "not dir or file: " + fromFile);
+                       }
+               }
+       }
+
+       /**
+        * Ensure that the parent directory to path can be written. If the path has a null parent, DEFAULT_PARENT is tested. If the path
+        * parent does not exist, this tries to create it.
+        *
+        * @param path the File path whose parent should be writable
+        * @return the File path of the writable parent directory
+        * @throws IllegalArgumentException if parent cannot be written or path is null.
+        */
+       public static File ensureParentWritable(File path) {
+               LangUtil.throwIaxIfNull(path, "path");
+               File pathParent = path.getParentFile();
+               if (null == pathParent) {
+                       pathParent = DEFAULT_PARENT;
+               }
+               if (!pathParent.canWrite()) {
+                       pathParent.mkdirs();
+               }
+               throwIaxUnlessCanWriteDir(pathParent, "pathParent");
+               return pathParent;
+       }
+
+       /**
+        * Copy file to file.
+        *
+        * @param fromFile the File to copy (readable, non-null file)
+        * @param toFile the File to copy to (non-null, parent dir exists)
+        * @throws IOException
+        */
+       public static void copyValidFiles(File fromFile, File toFile) throws IOException {
+               FileInputStream in = null;
+               FileOutputStream out = null;
+               try {
+                       in = new FileInputStream(fromFile);
+                       out = new FileOutputStream(toFile);
+                       copyStream(in, out);
+               } finally {
+                       if (out != null) {
+                               out.close();
+                       }
+                       if (in != null) {
+                               in.close();
+                       }
+               }
+       }
+
+       /** do line-based copying */
+       @SuppressWarnings("deprecation")
+       public static void copyStream(DataInputStream in, PrintStream out) throws IOException {
+               LangUtil.throwIaxIfNull(in, "in");
+               LangUtil.throwIaxIfNull(in, "out");
+               String s;
+               while (null != (s = in.readLine())) {
+                       out.println(s);
+               }
+       }
+
+       public static void copyStream(InputStream in, OutputStream out) throws IOException {
+               final int MAX = 4096;
+               byte[] buf = new byte[MAX];
+               for (int bytesRead = in.read(buf, 0, MAX); bytesRead != -1; bytesRead = in.read(buf, 0, MAX)) {
+                       out.write(buf, 0, bytesRead);
+               }
+       }
+
+       public static void copyStream(Reader in, Writer out) throws IOException {
+               final int MAX = 4096;
+               char[] buf = new char[MAX];
+               for (int bytesRead = in.read(buf, 0, MAX); bytesRead != -1; bytesRead = in.read(buf, 0, MAX)) {
+                       out.write(buf, 0, bytesRead);
+               }
+       }
+
+       /**
+        * Make a new child directory of parent
+        *
+        * @param parent a File for the parent (writable)
+        * @param child a prefix for the child directory
+        * @return a File dir that exists with parentDir as the parent file or null
+        */
+       public static File makeNewChildDir(File parent, String child) {
+               if (null == parent || !parent.canWrite() || !parent.isDirectory()) {
+                       throw new IllegalArgumentException("bad parent: " + parent);
+               } else if (null == child) {
+                       child = "makeNewChildDir";
+               } else if (!isValidFileName(child)) {
+                       throw new IllegalArgumentException("bad child: " + child);
+               }
+               File result = new File(parent, child);
+               int safety = 1000;
+               for (String suffix = FileUtil.randomFileString(); ((0 < --safety) && result.exists()); suffix = FileUtil.randomFileString()) {
+                       result = new File(parent, child + suffix);
+               }
+               if (result.exists()) {
+                       System.err.println("exhausted files for child dir in " + parent);
+                       return null;
+               }
+               return ((result.mkdirs() && result.exists()) ? result : null);
+       }
+
+       /**
+        * Make a new temporary directory in the same directory that the system uses for temporary files, or if that files, in the
+        * current directory.
+        *
+        * @param name the preferred (simple) name of the directory - may be null.
+        * @return File of an existing new temp dir, or null if unable to create
+        */
+       public static File getTempDir(String name) {
+               if (null == name) {
+                       name = "FileUtil_getTempDir";
+               } else if (!isValidFileName(name)) {
+                       throw new IllegalArgumentException(" invalid: " + name);
+               }
+               File result = null;
+               File tempFile = null;
+               try {
+                       tempFile = File.createTempFile("ignoreMe", ".txt");
+                       File tempParent = tempFile.getParentFile();
+                       result = makeNewChildDir(tempParent, name);
+               } catch (IOException t) {
+                       result = makeNewChildDir(new File("."), name);
+               } finally {
+                       if (null != tempFile) {
+                               tempFile.delete();
+                       }
+               }
+               return result;
+       }
+
+       public static URL[] getFileURLs(File[] files) {
+               if ((null == files) || (0 == files.length)) {
+                       return new URL[0];
+               }
+               URL[] result = new URL[files.length]; // XXX dangerous non-copy...
+               for (int i = 0; i < result.length; i++) {
+                       result[i] = getFileURL(files[i]);
+               }
+               return result;
+       }
+
+       /**
+        * Get URL for a File. This appends "/" for directories. prints errors to System.err
+        *
+        * @param file the File to convert to URL (not null)
+        */
+       @SuppressWarnings("deprecation")
+       public static URL getFileURL(File file) {
+               LangUtil.throwIaxIfNull(file, "file");
+               URL result = null;
+               try {
+                       result = file.toURL();// TODO AV - was toURI.toURL that does not
+                       // works on Java 1.3
+                       if (null != result) {
+                               return result;
+                       }
+                       String url = "file:" + file.getAbsolutePath().replace('\\', '/');
+                       result = new URL(url + (file.isDirectory() ? "/" : ""));
+               } catch (MalformedURLException e) {
+                       String m = "Util.makeURL(\"" + file.getPath() + "\" MUE " + e.getMessage();
+                       System.err.println(m);
+               }
+               return result;
+       }
+
+       /**
+        * Write contents to file, returning null on success or error message otherwise. This tries to make any necessary parent
+        * directories first.
+        *
+        * @param file the File to write (not null)
+        * @param contents the String to write (use "" if null)
+        * @return String null on no error, error otherwise
+        */
+       public static String writeAsString(File file, String contents) {
+               LangUtil.throwIaxIfNull(file, "file");
+               if (null == contents) {
+                       contents = "";
+               }
+               Writer out = null;
+               try {
+                       File parentDir = file.getParentFile();
+                       if (!parentDir.exists() && !parentDir.mkdirs()) {
+                               return "unable to make parent dir for " + file;
+                       }
+                       Reader in = new StringReader(contents);
+                       out = new FileWriter(file);
+                       FileUtil.copyStream(in, out);
+                       return null;
+               } catch (IOException e) {
+                       return LangUtil.unqualifiedClassName(e) + " writing " + file + ": " + e.getMessage();
+               } finally {
+                       if (null != out) {
+                               try {
+                                       out.close();
+                               } catch (IOException e) {
+                               } // ignored
+                       }
+               }
+       }
+
+       /**
+        * Reads a boolean array with our encoding
+        */
+       public static boolean[] readBooleanArray(DataInputStream s) throws IOException {
+               int len = s.readInt();
+               boolean[] ret = new boolean[len];
+               for (int i = 0; i < len; i++) {
+                       ret[i] = s.readBoolean();
+               }
+               return ret;
+       }
+
+       /**
+        * Writes a boolean array with our encoding
+        */
+       public static void writeBooleanArray(boolean[] a, DataOutputStream s) throws IOException {
+               int len = a.length;
+               s.writeInt(len);
+               for (int i = 0; i < len; i++) {
+                       s.writeBoolean(a[i]);
+               }
+       }
+
+       /**
+        * Reads an int array with our encoding
+        */
+       public static int[] readIntArray(DataInputStream s) throws IOException {
+               int len = s.readInt();
+               int[] ret = new int[len];
+               for (int i = 0; i < len; i++) {
+                       ret[i] = s.readInt();
+               }
+               return ret;
+       }
+
+       /**
+        * Writes an int array with our encoding
+        */
+       public static void writeIntArray(int[] a, DataOutputStream s) throws IOException {
+               int len = a.length;
+               s.writeInt(len);
+               for (int i = 0; i < len; i++) {
+                       s.writeInt(a[i]);
+               }
+       }
+
+       /**
+        * Reads an int array with our encoding
+        */
+       public static String[] readStringArray(DataInputStream s) throws IOException {
+               int len = s.readInt();
+               String[] ret = new String[len];
+               for (int i = 0; i < len; i++) {
+                       ret[i] = s.readUTF();
+               }
+               return ret;
+       }
+
+       /**
+        * Writes an int array with our encoding
+        */
+       public static void writeStringArray(String[] a, DataOutputStream s) throws IOException {
+               if (a == null) {
+                       s.writeInt(0);
+                       return;
+               }
+               int len = a.length;
+               s.writeInt(len);
+               for (int i = 0; i < len; i++) {
+                       s.writeUTF(a[i]);
+               }
+       }
+
+       /**
+        * Returns the contents of this file as a String
+        */
+       public static String readAsString(File file) throws IOException {
+               BufferedReader r = new BufferedReader(new FileReader(file));
+               StringBuffer b = new StringBuffer();
+               while (true) {
+                       int ch = r.read();
+                       if (ch == -1) {
+                               break;
+                       }
+                       b.append((char) ch);
+               }
+               r.close();
+               return b.toString();
+       }
+
+       // /**
+       // * Returns the contents of this stream as a String
+       // */
+       // public static String readAsString(InputStream in) throws IOException {
+       // BufferedReader r = new BufferedReader(new InputStreamReader(in));
+       // StringBuffer b = new StringBuffer();
+       // while (true) {
+       // int ch = r.read();
+       // if (ch == -1)
+       // break;
+       // b.append((char) ch);
+       // }
+       // in.close();
+       // r.close();
+       // return b.toString();
+       // }
+
+       /**
+        * Returns the contents of this file as a byte[]
+        */
+       public static byte[] readAsByteArray(File file) throws IOException {
+               FileInputStream in = new FileInputStream(file);
+               byte[] ret = FileUtil.readAsByteArray(in);
+               in.close();
+               return ret;
+       }
+
+       /**
+        * Reads this input stream and returns contents as a byte[]
+        */
+       public static byte[] readAsByteArray(InputStream inStream) throws IOException {
+               int size = 1024;
+               byte[] ba = new byte[size];
+               int readSoFar = 0;
+
+               while (true) {
+                       int nRead = inStream.read(ba, readSoFar, size - readSoFar);
+                       if (nRead == -1) {
+                               break;
+                       }
+                       readSoFar += nRead;
+                       if (readSoFar == size) {
+                               int newSize = size * 2;
+                               byte[] newBa = new byte[newSize];
+                               System.arraycopy(ba, 0, newBa, 0, size);
+                               ba = newBa;
+                               size = newSize;
+                       }
+               }
+
+               byte[] newBa = new byte[readSoFar];
+               System.arraycopy(ba, 0, newBa, 0, readSoFar);
+               return newBa;
+       }
+
+       final static String FILECHARS = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+       /** @return semi-random String of length 6 usable as filename suffix */
+       static String randomFileString() {
+               final double FILECHARS_length = FILECHARS.length();
+               final int LEN = 6;
+               final char[] result = new char[LEN];
+               int index = (int) (Math.random() * 6d);
+               for (int i = 0; i < LEN; i++) {
+                       if (index >= LEN) {
+                               index = 0;
+                       }
+                       result[index++] = FILECHARS.charAt((int) (Math.random() * FILECHARS_length));
+               }
+               return new String(result);
+       }
+
+       public static InputStream getStreamFromZip(String zipFile, String name) {
+               try {
+                       ZipFile zf = new ZipFile(zipFile);
+                       try {
+                               ZipEntry entry = zf.getEntry(name);
+                               return zf.getInputStream(entry);
+                       } finally {
+                               // ??? is it safe not to close this zf.close();
+                       }
+               } catch (IOException ioe) {
+                       return null;
+               }
+       }
+
+       //
+       // public static void extractJar(String zipFile, String outDir) throws
+       // IOException {
+       // ZipInputStream zs = new ZipInputStream(new FileInputStream(zipFile));
+       // ZipEntry entry;
+       // while ((entry = zs.getNextEntry()) != null) {
+       // if (entry.isDirectory())
+       // continue;
+       // byte[] in = readAsByteArray(zs);
+       //
+       // File outFile = new File(outDir + "/" + entry.getName());
+       // // if (!outFile.getParentFile().exists())
+       // // System.err.println("parent: " + outFile.getParentFile());
+       // // System.err.println("parent: " + outFile.getParentFile());
+       // outFile.getParentFile().mkdirs();
+       // FileOutputStream os = new FileOutputStream(outFile);
+       // os.write(in);
+       // os.close();
+       // zs.closeEntry();
+       // }
+       // zs.close();
+       // }
+
+       /**
+        * Do line-based search for literal text in source files, returning file:line where found.
+        *
+        * @param sought the String text to seek in the file
+        * @param sources the List of String paths to the source files
+        * @param listAll if false, only list first match in file
+        * @param errorSink the PrintStream to print any errors to (one per line) (use null to silently ignore errors)
+        * @return List of String of the form file:line for each found entry (never null, might be empty)
+        */
+       // OPTIMIZE only used by tests? move it out
+       public static List<String> lineSeek(String sought, List<String> sources, boolean listAll, PrintStream errorSink) {
+               if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sources)) {
+                       return Collections.emptyList();
+               }
+               ArrayList<String> result = new ArrayList<String>();
+               for (Iterator<String> iter = sources.iterator(); iter.hasNext();) {
+                       String path = iter.next();
+                       String error = lineSeek(sought, path, listAll, result);
+                       if ((null != error) && (null != errorSink)) {
+                               errorSink.println(error);
+                       }
+               }
+               return result;
+       }
+
+       /**
+        * Do line-based search for literal text in source file, returning line where found as a String in the form
+        * {sourcePath}:line:column submitted to the collecting parameter sink. Any error is rendered to String and returned as the
+        * result.
+        *
+        * @param sought the String text to seek in the file
+        * @param sources the List of String paths to the source files
+        * @param listAll if false, only list first match in file
+        * @param List sink the List for String entries of the form {sourcePath}:line:column
+        * @return String error if any, or add String entries to sink
+        */
+       public static String lineSeek(String sought, String sourcePath, boolean listAll, ArrayList<String> sink) {
+               if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sourcePath)) {
+                       return "nothing sought";
+               }
+               if (LangUtil.isEmpty(sourcePath)) {
+                       return "no sourcePath";
+               }
+               final File file = new File(sourcePath);
+               if (!file.canRead() || !file.isFile()) {
+                       return "sourcePath not a readable file";
+               }
+               int lineNum = 0;
+               FileReader fin = null;
+               try {
+                       fin = new FileReader(file);
+                       BufferedReader reader = new BufferedReader(fin);
+                       String line;
+                       while (null != (line = reader.readLine())) {
+                               lineNum++;
+                               int loc = line.indexOf(sought);
+                               if (-1 != loc) {
+                                       sink.add(sourcePath + ":" + lineNum + ":" + loc);
+                                       if (!listAll) {
+                                               break;
+                                       }
+                               }
+                       }
+               } catch (IOException e) {
+                       return LangUtil.unqualifiedClassName(e) + " reading " + sourcePath + ":" + lineNum;
+               } finally {
+                       try {
+                               if (null != fin) {
+                                       fin.close();
+                               }
+                       } catch (IOException e) {
+                       } // ignore
+               }
+               return null;
+       }
+
+       public static BufferedOutputStream makeOutputStream(File file) throws FileNotFoundException {
+               File parent = file.getParentFile();
+               if (parent != null) {
+                       parent.mkdirs();
+               }
+               return new BufferedOutputStream(new FileOutputStream(file));
+       }
+
+       /**
+        * Sleep until after the last last-modified stamp from the files.
+        *
+        * @param files the File[] of files to inspect for last modified times (this ignores null or empty files array and null or
+        *        non-existing components of files array)
+        * @return true if succeeded without 100 interrupts
+        */
+       public static boolean sleepPastFinalModifiedTime(File[] files) {
+               if ((null == files) || (0 == files.length)) {
+                       return true;
+               }
+               long delayUntil = System.currentTimeMillis();
+               for (int i = 0; i < files.length; i++) {
+                       File file = files[i];
+                       if ((null == file) || !file.exists()) {
+                               continue;
+                       }
+                       long nextModTime = file.lastModified();
+                       if (nextModTime > delayUntil) {
+                               delayUntil = nextModTime;
+                       }
+               }
+               return LangUtil.sleepUntil(++delayUntil);
+       }
+
+       private static void listClassFiles(final File baseDir, ArrayList<File> result) {
+               File[] files = baseDir.listFiles();
+               for (int i = 0; i < files.length; i++) {
+                       File f = files[i];
+                       if (f.isDirectory()) {
+                               listClassFiles(f, result);
+                       } else {
+                               if (f.getName().endsWith(".class")) {
+                                       result.add(f);
+                               }
+                       }
+               }
+       }
+
+       private static void listFiles(final File baseDir, ArrayList<File> result, FileFilter filter) {
+               File[] files = baseDir.listFiles();
+               // hack https://bugs.eclipse.org/bugs/show_bug.cgi?id=48650
+               final boolean skipCVS = (!PERMIT_CVS && (filter == aspectjSourceFileFilter));
+               for (int i = 0; i < files.length; i++) {
+                       File f = files[i];
+                       if (f.isDirectory()) {
+                               if (skipCVS) {
+                                       String name = f.getName().toLowerCase();
+                                       if ("cvs".equals(name) || "sccs".equals(name)) {
+                                               continue;
+                                       }
+                               }
+                               listFiles(f, result, filter);
+                       } else {
+                               if (filter.accept(f)) {
+                                       result.add(f);
+                               }
+                       }
+               }
+       }
+
+       /** @return true if input is not null and contains no path separator */
+       private static boolean isValidFileName(String input) {
+               return ((null != input) && (-1 == input.indexOf(File.pathSeparator)));
+       }
+
+       private static void listFiles(final File baseDir, String dir, ArrayList<String> result) {
+               final String dirPrefix = (null == dir ? "" : dir + "/");
+               final File dirFile = (null == dir ? baseDir : new File(baseDir.getPath() + "/" + dir));
+               final String[] files = dirFile.list();
+               for (int i = 0; i < files.length; i++) {
+                       File f = new File(dirFile, files[i]);
+                       String path = dirPrefix + files[i];
+                       if (f.isDirectory()) {
+                               listFiles(baseDir, path, result);
+                       } else {
+                               result.add(path);
+                       }
+               }
+       }
+
+       private FileUtil() {
+       }
+
+       public static List<String> makeClasspath(URL[] urls) {
+               List<String> ret = new LinkedList<String>();
+               if (urls != null) {
+                       for (int i = 0; i < urls.length; i++) {
+                               ret.add(toPathString(urls[i]));
+                       }
+               }
+               return ret;
+       }
+
+       private static String toPathString(URL url) {
+               try {
+                       return url.toURI().getPath();
+               } catch (URISyntaxException e) {
+                       System.err.println("Warning!! Malformed URL may cause problems: "+url); // TODO: Better way to report this?
+                       // In this case it was likely not using properly escaped
+                       // characters so we just use the 'bad' method that doesn't decode
+                       // special chars
+                       return url.getPath();
+               }
+       }
+
+       /**
+        * A pipe when run reads from an input stream to an output stream, optionally sleeping between reads.
+        *
+        * @see #copyStream(InputStream, OutputStream)
+        */
+       public static class Pipe implements Runnable {
+               private final InputStream in;
+               private final OutputStream out;
+               private final long sleep;
+               private ByteArrayOutputStream snoop;
+               private long totalWritten;
+               private Throwable thrown;
+               private boolean halt;
+               /**
+                * Seem to be unable to detect erroroneous closing of System.out...
+                */
+               private final boolean closeInput;
+               private final boolean closeOutput;
+
+               /**
+                * If true, then continue processing stream until no characters are returned when halting.
+                */
+               private boolean finishStream;
+
+               private boolean done; // true after completing() completes
+
+               /**
+                * alias for <code>Pipe(in, out, 100l, false, false)</code>
+                *
+                * @param in the InputStream source to read
+                * @param out the OutputStream sink to write
+                */
+               Pipe(InputStream in, OutputStream out) {
+                       this(in, out, 100l, false, false);
+               }
+
+               /**
+                * @param in the InputStream source to read
+                * @param out the OutputStream sink to write
+                * @param tryClosingStreams if true, then try closing both streams when done
+                * @param sleep milliseconds to delay between reads (pinned to 0..1 minute)
+                */
+               Pipe(InputStream in, OutputStream out, long sleep, boolean closeInput, boolean closeOutput) {
+                       LangUtil.throwIaxIfNull(in, "in");
+                       LangUtil.throwIaxIfNull(out, "out");
+                       this.in = in;
+                       this.out = out;
+                       this.closeInput = closeInput;
+                       this.closeOutput = closeOutput;
+                       this.sleep = Math.min(0l, Math.max(60l * 1000l, sleep));
+               }
+
+               public void setSnoop(ByteArrayOutputStream snoop) {
+                       this.snoop = snoop;
+               }
+
+               /**
+                * Run the pipe. This halts on the first Throwable thrown or when a read returns -1 (for end-of-file) or on demand.
+                */
+               public void run() {
+                       totalWritten = 0;
+                       if (halt) {
+                               return;
+                       }
+                       try {
+                               final int MAX = 4096;
+                               byte[] buf = new byte[MAX];
+                               // TODO this blocks, hanging the harness
+                               int count = in.read(buf, 0, MAX);
+                               ByteArrayOutputStream mySnoop;
+                               while ((halt && finishStream && (0 < count)) || (!halt && (-1 != count))) {
+                                       out.write(buf, 0, count);
+                                       mySnoop = snoop;
+                                       if (null != mySnoop) {
+                                               mySnoop.write(buf, 0, count);
+                                       }
+                                       totalWritten += count;
+                                       if (halt && !finishStream) {
+                                               break;
+                                       }
+                                       if (!halt && (0 < sleep)) {
+                                               Thread.sleep(sleep);
+                                       }
+                                       if (halt && !finishStream) {
+                                               break;
+                                       }
+                                       count = in.read(buf, 0, MAX);
+                               }
+                       } catch (Throwable e) {
+                               thrown = e;
+                       } finally {
+                               halt = true;
+                               if (closeInput) {
+                                       try {
+                                               in.close();
+                                       } catch (IOException e) {
+                                               // ignore
+                                       }
+                               }
+                               if (closeOutput) {
+                                       try {
+                                               out.close();
+                                       } catch (IOException e) {
+                                               // ignore
+                                       }
+                               }
+                               done = true;
+                               completing(totalWritten, thrown);
+                       }
+
+               }
+
+               /**
+                * Tell the pipe to halt the next time it gains control.
+                *
+                * @param wait if true, this waits synchronously until pipe is done
+                * @param finishStream if true, then continue until a read from the input stream returns no bytes, then halt.
+                * @return true if <code>run()</code> will return the next time it gains control
+                */
+               public boolean halt(boolean wait, boolean finishStream) {
+                       if (!halt) {
+                               halt = true;
+                       }
+                       if (wait) {
+                               while (!done) {
+                                       synchronized (this) {
+                                               notifyAll();
+                                       }
+                                       if (!done) {
+                                               try {
+                                                       Thread.sleep(5l);
+                                               } catch (InterruptedException e) {
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+                       return halt;
+               }
+
+               /** @return the total number of bytes written */
+               public long totalWritten() {
+                       return totalWritten;
+               }
+
+               /** @return any exception thrown when reading/writing */
+               public Throwable getThrown() {
+                       return thrown;
+               }
+
+               /**
+                * This is called when the pipe is completing. This implementation does nothing. Subclasses implement this to get notice.
+                * Note that halt(true, true) might or might not have completed before this method is called.
+                */
+               protected void completing(long totalWritten, Throwable thrown) {
+               }
+       }
+
+}
diff --git a/util/src/main/java/org/aspectj/util/FuzzyBoolean.java b/util/src/main/java/org/aspectj/util/FuzzyBoolean.java
new file mode 100644 (file)
index 0000000..807d980
--- /dev/null
@@ -0,0 +1,174 @@
+/* *******************************************************************
+ * Copyright (c) 1999-2001 Xerox Corporation, 
+ *               2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/epl-v10.html 
+ *  
+ * Contributors: 
+ *     Xerox/PARC     initial implementation 
+ * ******************************************************************/
+package org.aspectj.util;
+
+/** 
+ * This class implements boolean that include a "maybe"
+ */
+public abstract class FuzzyBoolean {
+    public abstract boolean alwaysTrue();
+    public abstract boolean alwaysFalse();
+    public abstract boolean maybeTrue();
+    public abstract boolean maybeFalse();
+    
+    public abstract FuzzyBoolean and(FuzzyBoolean other);
+    public abstract FuzzyBoolean or(FuzzyBoolean other);
+    public abstract FuzzyBoolean not();
+    
+    private static class YesFuzzyBoolean extends FuzzyBoolean {
+        public boolean alwaysFalse() {
+            return false;
+        }
+
+        public boolean alwaysTrue() {
+            return true;
+        }
+
+        public boolean maybeFalse() {
+            return false;
+        }
+
+        public boolean maybeTrue() {
+            return true;
+        }
+        
+        public FuzzyBoolean and(FuzzyBoolean other) {
+            return other;
+        }
+
+        public FuzzyBoolean not() {
+            return FuzzyBoolean.NO;
+        }
+
+        public FuzzyBoolean or(FuzzyBoolean other) {
+            return this;
+        }
+
+        public String toString() {
+            return "YES";
+        }
+    }    
+    private static class NoFuzzyBoolean extends FuzzyBoolean {
+        public boolean alwaysFalse() {
+            return true;
+        }
+
+        public boolean alwaysTrue() {
+            return false;
+        }
+
+
+        public boolean maybeFalse() {
+            return true;
+        }
+
+        public boolean maybeTrue() {
+            return false;
+        }
+        
+        public FuzzyBoolean and(FuzzyBoolean other) {
+            return this;
+        }
+
+        public FuzzyBoolean not() {
+            return FuzzyBoolean.YES;
+        }
+
+        public FuzzyBoolean or(FuzzyBoolean other) {
+            return other;
+        }
+
+        public String toString() {
+            return "NO";
+        }
+    }
+    private static class NeverFuzzyBoolean extends FuzzyBoolean {
+        public boolean alwaysFalse() {
+            return true;
+        }
+
+        public boolean alwaysTrue() {
+            return false;
+        }
+
+
+        public boolean maybeFalse() {
+            return true;
+        }
+
+        public boolean maybeTrue() {
+            return false;
+        }
+        
+        public FuzzyBoolean and(FuzzyBoolean other) {
+            return this;
+        }
+
+        public FuzzyBoolean not() {
+            return this;
+        }
+
+        public FuzzyBoolean or(FuzzyBoolean other) {
+            return this;
+        }
+
+        public String toString() {
+            return "NEVER";
+        }
+    }
+    
+    private static class MaybeFuzzyBoolean extends FuzzyBoolean {
+        public boolean alwaysFalse() {
+            return false;
+        }
+
+        public boolean alwaysTrue() {
+            return false;
+        }
+
+
+        public boolean maybeFalse() {
+            return true;
+        }
+
+        public boolean maybeTrue() {
+            return true;
+        }
+        
+        public FuzzyBoolean and(FuzzyBoolean other) {
+            return other.alwaysFalse() ? other : this;
+        }
+
+        public FuzzyBoolean not() {
+            return this;
+        }
+
+        public FuzzyBoolean or(FuzzyBoolean other) {
+            return other.alwaysTrue() ? other : this;
+        }
+
+        public String toString() {
+            return "MAYBE";
+        }
+    }
+    
+    public static final FuzzyBoolean YES   = new YesFuzzyBoolean();
+    public static final FuzzyBoolean NO    = new NoFuzzyBoolean();
+    public static final FuzzyBoolean MAYBE = new MaybeFuzzyBoolean();
+    public static final FuzzyBoolean NEVER = new NeverFuzzyBoolean();
+
+       public static final FuzzyBoolean fromBoolean(boolean b) {
+               return b ? YES : NO;
+       }
+
+}
diff --git a/util/src/main/java/org/aspectj/util/GenericSignature.java b/util/src/main/java/org/aspectj/util/GenericSignature.java
new file mode 100644 (file)
index 0000000..bfefc00
--- /dev/null
@@ -0,0 +1,252 @@
+/* *******************************************************************
+ * Copyright (c) 1999-2001 Xerox Corporation, 
+ *               2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/epl-v10.html 
+ *  
+ * ******************************************************************/
+package org.aspectj.util;
+
+/**
+ * Encapsulate generic signature parsing
+ * 
+ * @author Adrian Colyer
+ * @author Andy Clement
+ */
+public class GenericSignature {
+
+       /**
+        * structure holding a parsed class signature
+        */
+       public static class ClassSignature {
+               public FormalTypeParameter[] formalTypeParameters = FormalTypeParameter.NONE;
+               public ClassTypeSignature superclassSignature;
+               public ClassTypeSignature[] superInterfaceSignatures = ClassTypeSignature.NONE;
+
+               public String toString() {
+                       StringBuffer ret = new StringBuffer();
+                       ret.append(formalTypeParameters.toString());
+                       ret.append(superclassSignature.toString());
+                       for (int i = 0; i < superInterfaceSignatures.length; i++) {
+                               ret.append(superInterfaceSignatures[i].toString());
+                       }
+                       return ret.toString();
+               }
+       }
+
+       public static class MethodTypeSignature {
+               public FormalTypeParameter[] formalTypeParameters = new FormalTypeParameter[0];
+               public TypeSignature[] parameters = new TypeSignature[0];
+               public TypeSignature returnType;
+               public FieldTypeSignature[] throwsSignatures = new FieldTypeSignature[0];
+
+               public MethodTypeSignature(FormalTypeParameter[] aFormalParameterList, TypeSignature[] aParameterList,
+                               TypeSignature aReturnType, FieldTypeSignature[] aThrowsSignatureList) {
+                       this.formalTypeParameters = aFormalParameterList;
+                       this.parameters = aParameterList;
+                       this.returnType = aReturnType;
+                       this.throwsSignatures = aThrowsSignatureList;
+               }
+
+               public String toString() {
+                       StringBuffer sb = new StringBuffer();
+                       if (formalTypeParameters.length > 0) {
+                               sb.append("<");
+                               for (int i = 0; i < formalTypeParameters.length; i++) {
+                                       sb.append(formalTypeParameters[i].toString());
+                               }
+                               sb.append(">");
+                       }
+                       sb.append("(");
+                       for (int i = 0; i < parameters.length; i++) {
+                               sb.append(parameters[i].toString());
+                       }
+                       sb.append(")");
+                       sb.append(returnType.toString());
+                       for (int i = 0; i < throwsSignatures.length; i++) {
+                               sb.append("^");
+                               sb.append(throwsSignatures[i].toString());
+                       }
+                       return sb.toString();
+               }
+       }
+
+       /**
+        * structure capturing a FormalTypeParameter from the Signature grammar
+        */
+       public static class FormalTypeParameter {
+               public static final FormalTypeParameter[] NONE = new FormalTypeParameter[0];
+               public String identifier;
+               public FieldTypeSignature classBound;
+               public FieldTypeSignature[] interfaceBounds;
+
+               public String toString() {
+                       StringBuffer ret = new StringBuffer();
+                       ret.append("T");
+                       ret.append(identifier);
+                       ret.append(":");
+                       ret.append(classBound.toString());
+                       for (int i = 0; i < interfaceBounds.length; i++) {
+                               ret.append(":");
+                               ret.append(interfaceBounds[i].toString());
+                       }
+                       return ret.toString();
+               }
+       }
+
+       public static abstract class TypeSignature {
+               public boolean isBaseType() {
+                       return false;
+               }
+       }
+
+       public static class BaseTypeSignature extends TypeSignature {
+               private final String sig;
+
+               public BaseTypeSignature(String aPrimitiveType) {
+                       sig = aPrimitiveType;
+               }
+
+               public boolean isBaseType() {
+                       return true;
+               }
+
+               public String toString() {
+                       return sig;
+               }
+       }
+
+       public static abstract class FieldTypeSignature extends TypeSignature {
+               public boolean isClassTypeSignature() {
+                       return false;
+               }
+
+               public boolean isTypeVariableSignature() {
+                       return false;
+               }
+
+               public boolean isArrayTypeSignature() {
+                       return false;
+               }
+       }
+
+       public static class ClassTypeSignature extends FieldTypeSignature {
+
+               public static final ClassTypeSignature[] NONE = new ClassTypeSignature[0];
+               public String classSignature;
+               public SimpleClassTypeSignature outerType;
+               public SimpleClassTypeSignature[] nestedTypes;
+
+               public ClassTypeSignature(String sig, String identifier) {
+                       this.classSignature = sig;
+                       this.outerType = new SimpleClassTypeSignature(identifier);
+                       this.nestedTypes = new SimpleClassTypeSignature[0];
+               }
+
+               public ClassTypeSignature(String sig, SimpleClassTypeSignature outer, SimpleClassTypeSignature[] inners) {
+                       this.classSignature = sig;
+                       this.outerType = outer;
+                       this.nestedTypes = inners;
+               }
+
+               public boolean isClassTypeSignature() {
+                       return true;
+               }
+
+               public String toString() {
+                       return classSignature;
+               }
+       }
+
+       public static class TypeVariableSignature extends FieldTypeSignature {
+               public String typeVariableName;
+
+               public TypeVariableSignature(String typeVarToken) {
+                       this.typeVariableName = typeVarToken.substring(1);
+               }
+
+               public boolean isTypeVariableSignature() {
+                       return true;
+               }
+
+               public String toString() {
+                       return "T" + typeVariableName + ";";
+               }
+       }
+
+       public static class ArrayTypeSignature extends FieldTypeSignature {
+               public TypeSignature typeSig;
+
+               public ArrayTypeSignature(TypeSignature aTypeSig) {
+                       this.typeSig = aTypeSig;
+               }
+
+               public boolean isArrayTypeSignature() {
+                       return true;
+               }
+
+               public String toString() {
+                       return "[" + typeSig.toString();
+               }
+       }
+
+       public static class SimpleClassTypeSignature {
+               public String identifier;
+               public TypeArgument[] typeArguments;
+
+               public SimpleClassTypeSignature(String identifier) {
+                       this.identifier = identifier;
+                       this.typeArguments = new TypeArgument[0];
+               }
+
+               public SimpleClassTypeSignature(String identifier, TypeArgument[] args) {
+                       this.identifier = identifier;
+                       this.typeArguments = args;
+               }
+
+               public String toString() {
+                       StringBuffer sb = new StringBuffer();
+                       sb.append(identifier);
+                       if (typeArguments.length > 0) {
+                               sb.append("<");
+                               for (int i = 0; i < typeArguments.length; i++) {
+                                       sb.append(typeArguments[i].toString());
+                               }
+                               sb.append(">");
+                       }
+                       return sb.toString();
+               }
+       }
+
+       public static class TypeArgument {
+               public boolean isWildcard = false;
+               public boolean isPlus = false;
+               public boolean isMinus = false;
+               public FieldTypeSignature signature; // null if isWildcard
+
+               public TypeArgument() {
+                       isWildcard = true;
+               }
+
+               public TypeArgument(boolean plus, boolean minus, FieldTypeSignature aSig) {
+                       this.isPlus = plus;
+                       this.isMinus = minus;
+                       this.signature = aSig;
+               }
+
+               public String toString() {
+                       if (isWildcard)
+                               return "*";
+                       StringBuffer sb = new StringBuffer();
+                       if (isPlus)
+                               sb.append("+");
+                       if (isMinus)
+                               sb.append("-");
+                       sb.append(signature.toString());
+                       return sb.toString();
+               }
+       }
+}
diff --git a/util/src/main/java/org/aspectj/util/GenericSignatureParser.java b/util/src/main/java/org/aspectj/util/GenericSignatureParser.java
new file mode 100644 (file)
index 0000000..28c8db8
--- /dev/null
@@ -0,0 +1,426 @@
+/* *******************************************************************
+ * Copyright (c) 2005-2008 Contributors.
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://eclipse.org/legal/epl-v10.html 
+ *  
+ * ******************************************************************/
+package org.aspectj.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.aspectj.util.GenericSignature.ArrayTypeSignature;
+import org.aspectj.util.GenericSignature.BaseTypeSignature;
+import org.aspectj.util.GenericSignature.ClassTypeSignature;
+import org.aspectj.util.GenericSignature.FieldTypeSignature;
+import org.aspectj.util.GenericSignature.FormalTypeParameter;
+import org.aspectj.util.GenericSignature.MethodTypeSignature;
+import org.aspectj.util.GenericSignature.SimpleClassTypeSignature;
+import org.aspectj.util.GenericSignature.TypeArgument;
+import org.aspectj.util.GenericSignature.TypeSignature;
+import org.aspectj.util.GenericSignature.TypeVariableSignature;
+
+/**
+ * Parses the generic signature attribute as defined in the JVM spec.
+ * 
+ * @author Adrian Colyer
+ * @author Andy Clement
+ */
+public class GenericSignatureParser {
+
+       private String inputString;
+       private String[] tokenStream; // for parse in flight
+       private int tokenIndex = 0;
+
+       /**
+        * AMC. Parse the signature string interpreting it as a ClassSignature according to the grammar defined in Section 4.4.4 of the
+        * JVM specification.
+        */
+       public GenericSignature.ClassSignature parseAsClassSignature(String sig) {
+               this.inputString = sig;
+               tokenStream = tokenize(sig);
+               tokenIndex = 0;
+               GenericSignature.ClassSignature classSig = new GenericSignature.ClassSignature();
+               // FormalTypeParameters-opt
+               if (maybeEat("<")) {
+                       List<FormalTypeParameter> formalTypeParametersList = new ArrayList<FormalTypeParameter>();
+                       do {
+                               formalTypeParametersList.add(parseFormalTypeParameter());
+                       } while (!maybeEat(">"));
+                       classSig.formalTypeParameters = new FormalTypeParameter[formalTypeParametersList.size()];
+                       formalTypeParametersList.toArray(classSig.formalTypeParameters);
+               }
+               classSig.superclassSignature = parseClassTypeSignature();
+               List<ClassTypeSignature> superIntSigs = new ArrayList<ClassTypeSignature>();
+               while (tokenIndex < tokenStream.length) {
+                       superIntSigs.add(parseClassTypeSignature());
+               }
+               classSig.superInterfaceSignatures = new ClassTypeSignature[superIntSigs.size()];
+               superIntSigs.toArray(classSig.superInterfaceSignatures);
+               return classSig;
+       }
+
+       /**
+        * AMC. Parse the signature string interpreting it as a MethodTypeSignature according to the grammar defined in Section 4.4.4 of
+        * the JVM specification.
+        */
+       public MethodTypeSignature parseAsMethodSignature(String sig) {
+               this.inputString = sig;
+               tokenStream = tokenize(sig);
+               tokenIndex = 0;
+               FormalTypeParameter[] formals = new FormalTypeParameter[0];
+               TypeSignature returnType = null;
+               // FormalTypeParameters-opt
+               if (maybeEat("<")) {
+                       List<FormalTypeParameter> formalTypeParametersList = new ArrayList<FormalTypeParameter>();
+                       do {
+                               formalTypeParametersList.add(parseFormalTypeParameter());
+                       } while (!maybeEat(">"));
+                       formals = new FormalTypeParameter[formalTypeParametersList.size()];
+                       formalTypeParametersList.toArray(formals);
+               }
+               // Parameters
+               eat("(");
+               List<TypeSignature> paramList = new ArrayList<TypeSignature>();
+               while (!maybeEat(")")) {
+                       FieldTypeSignature fsig = parseFieldTypeSignature(true);
+                       if (fsig != null) {
+                               paramList.add(fsig);
+                       } else {
+                               paramList.add(new GenericSignature.BaseTypeSignature(eatIdentifier()));
+                       }
+               }
+               TypeSignature[] params = new TypeSignature[paramList.size()];
+               paramList.toArray(params);
+               // return type
+               returnType = parseFieldTypeSignature(true);
+               if (returnType == null)
+                       returnType = new GenericSignature.BaseTypeSignature(eatIdentifier());
+               // throws
+               List<FieldTypeSignature> throwsList = new ArrayList<FieldTypeSignature>();
+               while (maybeEat("^")) {
+                       FieldTypeSignature fsig = parseFieldTypeSignature(false);
+                       throwsList.add(fsig);
+               }
+               FieldTypeSignature[] throwsSigs = new FieldTypeSignature[throwsList.size()];
+               throwsList.toArray(throwsSigs);
+               return new GenericSignature.MethodTypeSignature(formals, params, returnType, throwsSigs);
+       }
+
+       /**
+        * AMC. Parse the signature string interpreting it as a FieldTypeSignature according to the grammar defined in Section 4.4.4 of
+        * the JVM specification.
+        */
+       public FieldTypeSignature parseAsFieldSignature(String sig) {
+               this.inputString = sig;
+               tokenStream = tokenize(sig);
+               tokenIndex = 0;
+               return parseFieldTypeSignature(false);
+       }
+
+       private FormalTypeParameter parseFormalTypeParameter() {
+               FormalTypeParameter ftp = new FormalTypeParameter();
+               // Identifier
+               ftp.identifier = eatIdentifier();
+               // ClassBound
+               eat(":");
+               ftp.classBound = parseFieldTypeSignature(true);
+               if (ftp.classBound == null) {
+                       ftp.classBound = new ClassTypeSignature("Ljava/lang/Object;", "Ljava/lang/Object");
+               }
+               // Optional InterfaceBounds
+               List<FieldTypeSignature> optionalBounds = new ArrayList<FieldTypeSignature>();
+               while (maybeEat(":")) {
+                       optionalBounds.add(parseFieldTypeSignature(false));
+               }
+               ftp.interfaceBounds = new FieldTypeSignature[optionalBounds.size()];
+               optionalBounds.toArray(ftp.interfaceBounds);
+               return ftp;
+       }
+
+       private FieldTypeSignature parseFieldTypeSignature(boolean isOptional) {
+               if (isOptional) {
+                       // anything other than 'L', 'T' or '[' and we're out of here
+                       if (!tokenStream[tokenIndex].startsWith("L") && !tokenStream[tokenIndex].startsWith("T")
+                                       && !tokenStream[tokenIndex].startsWith("[")) {
+                               return null;
+                       }
+               }
+               if (maybeEat("[")) {
+                       return parseArrayTypeSignature();
+               } else if (tokenStream[tokenIndex].startsWith("L")) {
+                       return parseClassTypeSignature();
+               } else if (tokenStream[tokenIndex].startsWith("T")) {
+                       return parseTypeVariableSignature();
+               } else {
+                       throw new IllegalStateException("Expecting [,L, or T, but found " + tokenStream[tokenIndex] + " while unpacking "
+                                       + inputString);
+               }
+       }
+
+       private ArrayTypeSignature parseArrayTypeSignature() {
+               // opening [ already eaten
+               FieldTypeSignature fieldType = parseFieldTypeSignature(true);
+               if (fieldType != null) {
+                       return new ArrayTypeSignature(fieldType);
+               } else {
+                       // must be BaseType array
+                       return new ArrayTypeSignature(new BaseTypeSignature(eatIdentifier()));
+               }
+       }
+
+       // L PackageSpecifier* SimpleClassTypeSignature ClassTypeSignature* ;
+       private ClassTypeSignature parseClassTypeSignature() {
+               SimpleClassTypeSignature outerType = null;
+               SimpleClassTypeSignature[] nestedTypes = new SimpleClassTypeSignature[0];
+               StringBuffer ret = new StringBuffer();
+               String identifier = eatIdentifier();
+               ret.append(identifier);
+               while (maybeEat("/")) {
+                       ret.append("/"); // dont forget this...
+                       ret.append(eatIdentifier());
+               }
+               identifier = ret.toString();
+               // now we have either a "." indicating the start of a nested type,
+               // or a "<" indication type arguments, or ";" and we are done.
+               while (!maybeEat(";")) {
+                       if (tokenStream[tokenIndex].equals(".")) {
+                               // outer type completed
+                               outerType = new SimpleClassTypeSignature(identifier);
+                               nestedTypes = parseNestedTypesHelper(ret);
+                       } else if (tokenStream[tokenIndex].equals("<")) {
+                               ret.append("<");
+                               TypeArgument[] tArgs = maybeParseTypeArguments();
+                               for (int i = 0; i < tArgs.length; i++) {
+                                       ret.append(tArgs[i].toString());
+                               }
+                               ret.append(">");
+                               outerType = new SimpleClassTypeSignature(identifier, tArgs);
+                               nestedTypes = parseNestedTypesHelper(ret);
+                       } else {
+                               throw new IllegalStateException("Expecting .,<, or ;, but found " + tokenStream[tokenIndex] + " while unpacking "
+                                               + inputString);
+                       }
+               }
+               ret.append(";");
+               if (outerType == null)
+                       outerType = new SimpleClassTypeSignature(ret.toString());
+               return new ClassTypeSignature(ret.toString(), outerType, nestedTypes);
+       }
+
+       /**
+        * Helper method to digest nested types, slightly more complex than necessary to cope with some android related
+        * incorrect classes (see bug 406167)
+        */
+       private SimpleClassTypeSignature[] parseNestedTypesHelper(StringBuffer ret) {
+               boolean brokenSignature = false;
+               SimpleClassTypeSignature[] nestedTypes;
+               List<SimpleClassTypeSignature> nestedTypeList = new ArrayList<SimpleClassTypeSignature>();
+               while (maybeEat(".")) {
+                       ret.append(".");
+                       SimpleClassTypeSignature sig = parseSimpleClassTypeSignature();
+                       if (tokenStream[tokenIndex].equals("/")) {
+                               if (!brokenSignature) {
+                                       System.err.println("[See bug 406167] Bad class file signature encountered, nested types appear package qualified, ignoring those incorrect pieces. Signature: "+inputString);
+                               }
+                               brokenSignature = true;
+                               // hit something like: Lcom/a/a/b/t<TK;TV;>.com/a/a/b/af.com/a/a/b/ag;
+                               // and we are looking at the '/' after the com
+                               tokenIndex++; // pointing at the next identifier
+                               while (tokenStream[tokenIndex+1].equals("/")) {
+                                       tokenIndex+=2; // jump over an 'identifier' '/' pair
+                               }
+                               // now tokenIndex is the final bit of the name (which we'll treat as the inner type name)
+                               sig = parseSimpleClassTypeSignature();
+                       }
+                       ret.append(sig.toString());
+                       nestedTypeList.add(sig);
+               };
+               nestedTypes = new SimpleClassTypeSignature[nestedTypeList.size()];
+               nestedTypeList.toArray(nestedTypes);
+               return nestedTypes;
+       }
+
+       private SimpleClassTypeSignature parseSimpleClassTypeSignature() {
+               String identifier = eatIdentifier();
+               TypeArgument[] tArgs = maybeParseTypeArguments();
+               if (tArgs != null) {
+                       return new SimpleClassTypeSignature(identifier, tArgs);
+               } else {
+                       return new SimpleClassTypeSignature(identifier);
+               }
+       }
+
+       private TypeArgument parseTypeArgument() {
+               boolean isPlus = false;
+               boolean isMinus = false;
+               if (maybeEat("*")) {
+                       return new TypeArgument();
+               } else if (maybeEat("+")) {
+                       isPlus = true;
+               } else if (maybeEat("-")) {
+                       isMinus = true;
+               }
+               FieldTypeSignature sig = parseFieldTypeSignature(false);
+               return new TypeArgument(isPlus, isMinus, sig);
+       }
+
+       private TypeArgument[] maybeParseTypeArguments() {
+               if (maybeEat("<")) {
+                       List<TypeArgument> typeArgs = new ArrayList<TypeArgument>();
+                       do {
+                               TypeArgument arg = parseTypeArgument();
+                               typeArgs.add(arg);
+                       } while (!maybeEat(">"));
+                       TypeArgument[] tArgs = new TypeArgument[typeArgs.size()];
+                       typeArgs.toArray(tArgs);
+                       return tArgs;
+               } else {
+                       return null;
+               }
+       }
+
+       private TypeVariableSignature parseTypeVariableSignature() {
+               TypeVariableSignature tv = new TypeVariableSignature(eatIdentifier());
+               eat(";");
+               return tv;
+       }
+
+       private boolean maybeEat(String token) {
+               if (tokenStream.length <= tokenIndex)
+                       return false;
+               if (tokenStream[tokenIndex].equals(token)) {
+                       tokenIndex++;
+                       return true;
+               }
+               return false;
+       }
+
+       private void eat(String token) {
+               if (!tokenStream[tokenIndex].equals(token)) {
+                       throw new IllegalStateException("Expecting " + token + " but found " + tokenStream[tokenIndex] + " while unpacking "
+                                       + inputString);
+               }
+               tokenIndex++;
+       }
+
+       private String eatIdentifier() {
+               return tokenStream[tokenIndex++];
+       }
+
+       /**
+        * non-private for test visibility Splits a string containing a generic signature into tokens for consumption by the parser.
+        */
+       public String[] tokenize(String signatureString) {
+               char[] chars = signatureString.toCharArray();
+               int index = 0;
+               List<String> tokens = new ArrayList<String>();
+               StringBuffer identifier = new StringBuffer();
+               boolean inParens = false;
+               boolean inArray = false;
+               boolean couldSeePrimitive = false;
+               do {
+                       switch (chars[index]) {
+                       case '<':
+                               if (identifier.length() > 0)
+                                       tokens.add(identifier.toString());
+                               identifier = new StringBuffer();
+                               tokens.add("<");
+                               break;
+                       case '>':
+                               if (identifier.length() > 0)
+                                       tokens.add(identifier.toString());
+                               identifier = new StringBuffer();
+                               tokens.add(">");
+                               break;
+                       case ':':
+                               if (identifier.length() > 0)
+                                       tokens.add(identifier.toString());
+                               identifier = new StringBuffer();
+                               tokens.add(":");
+                               break;
+                       case '/':
+                               if (identifier.length() > 0)
+                                       tokens.add(identifier.toString());
+                               identifier = new StringBuffer();
+                               tokens.add("/");
+                               couldSeePrimitive = false;
+                               break;
+                       case ';':
+                               if (identifier.length() > 0)
+                                       tokens.add(identifier.toString());
+                               identifier = new StringBuffer();
+                               tokens.add(";");
+                               couldSeePrimitive = true;
+                               inArray = false;
+                               break;
+                       case '^':
+                               if (identifier.length() > 0)
+                                       tokens.add(identifier.toString());
+                               identifier = new StringBuffer();
+                               tokens.add("^");
+                               break;
+                       case '+':
+                               tokens.add("+");
+                               break;
+                       case '-':
+                               tokens.add("-");
+                               break;
+                       case '*':
+                               tokens.add("*");
+                               break;
+                       case '.':
+                               if (identifier.length() > 0)
+                                       tokens.add(identifier.toString());
+                               identifier = new StringBuffer();
+                               couldSeePrimitive = false;
+                               tokens.add(".");
+                               break;
+                       case '(':
+                               tokens.add("(");
+                               inParens = true;
+                               couldSeePrimitive = true;
+                               break;
+                       case ')':
+                               tokens.add(")");
+                               inParens = false;
+                               break;
+                       case '[':
+                               tokens.add("[");
+                               couldSeePrimitive = true;
+                               inArray = true;
+                               break;
+                       case 'B':
+                       case 'C':
+                       case 'D':
+                       case 'F':
+                       case 'I':
+                       case 'J':
+                       case 'S':
+                       case 'V':
+                       case 'Z':
+                               if ((inParens || inArray) && couldSeePrimitive && identifier.length() == 0) {
+                                       tokens.add(new String("" + chars[index]));
+                               } else {
+                                       identifier.append(chars[index]);
+                               }
+                               inArray = false;
+                               break;
+                       case 'L':
+                               couldSeePrimitive = false;
+                               // deliberate fall-through
+                       default:
+                               identifier.append(chars[index]);
+                       }
+               } while ((++index) < chars.length);
+               if (identifier.length() > 0)
+                       tokens.add(identifier.toString());
+               String[] tokenArray = new String[tokens.size()];
+               tokens.toArray(tokenArray);
+               return tokenArray;
+       }
+
+}
diff --git a/util/src/main/java/org/aspectj/util/IStructureModel.java b/util/src/main/java/org/aspectj/util/IStructureModel.java
new file mode 100644 (file)
index 0000000..9008f08
--- /dev/null
@@ -0,0 +1,21 @@
+/* *******************************************************************
+ * Copyright (c) 2008 Contributors.
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://eclipse.org/legal/epl-v10.html 
+ *  
+ * Contributors: 
+ *   Andy Clement
+ * ******************************************************************/
+package org.aspectj.util;
+
+/**
+ * Abstraction of a structure model
+ * 
+ * @author Andy Clement
+ */
+public interface IStructureModel {
+
+}
diff --git a/util/src/main/java/org/aspectj/util/LangUtil.java b/util/src/main/java/org/aspectj/util/LangUtil.java
new file mode 100644 (file)
index 0000000..82bd942
--- /dev/null
@@ -0,0 +1,1511 @@
+/* *******************************************************************
+ * Copyright (c) 1999-2001 Xerox Corporation, 
+ *               2002 Palo Alto Research Center, Incorporated (PARC).
+ *               2018 Contributors
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/epl-v10.html 
+ *  
+ * Contributors: 
+ *     Xerox/PARC     initial implementation 
+ * ******************************************************************/
+package org.aspectj.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.security.PrivilegedActionException;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * 
+ */
+public class LangUtil {
+
+       public static final String EOL;
+
+       public static final String JRT_FS = "jrt-fs.jar";
+
+       private static double vmVersion;
+
+       /**
+        * @return the vm version (1.1, 1.2, 1.3, 1.4, etc)
+        */
+       public static String getVmVersionString() {
+               return Double.toString(vmVersion);
+       }
+       
+       public static double getVmVersion() {
+               return vmVersion;
+       }
+       
+       static {
+               StringWriter buf = new StringWriter();
+               PrintWriter writer = new PrintWriter(buf);
+               writer.println("");
+               String eol = "\n";
+               try {
+                       buf.close();
+                       StringBuffer sb = buf.getBuffer();
+                       if (sb != null) {
+                               eol = buf.toString();
+                       }
+               } catch (Throwable t) {
+               }
+               EOL = eol;
+       }
+
+       static {
+               // http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html
+               // http://openjdk.java.net/jeps/223 "New Version-String Scheme"
+               try {
+                       String vm = System.getProperty("java.version"); // JLS 20.18.7
+                       if (vm == null) {
+                               vm = System.getProperty("java.runtime.version");
+                       }
+                       if (vm == null) {
+                               vm = System.getProperty("java.vm.version");
+                       }
+                       if (vm == null) {
+                               new RuntimeException(
+                                               "System properties appear damaged, cannot find: java.version/java.runtime.version/java.vm.version")
+                                               .printStackTrace(System.err);
+                               vmVersion = 1.5;
+                       } else {
+                               // Version: [1-9][0-9]*((\.0)*\.[1-9][0-9]*)* 
+                               // Care about the first set of digits and second set if first digit is 1
+                               try {
+                                       List<Integer> numbers = getFirstNumbers(vm);
+                                       if (numbers.get(0) == 1) {
+                                               // Old school for 1.0 > 1.8
+                                               vmVersion = numbers.get(0)+(numbers.get(1)/10d);
+                                       } else {
+                                               // numbers.get(0) is the major version (9 and above)
+                                               // Note here the number will be 9 (or 10), *not* 1.9 or 1.10
+                                               vmVersion = numbers.get(0);
+                                       }
+                               } catch (Throwable t) {
+                                       // Give up
+                                       vmVersion = 1.5;
+                               }
+                       }
+               } catch (Throwable t) {
+                       new RuntimeException(
+                                       "System properties appear damaged, cannot find: java.version/java.runtime.version/java.vm.version", t)
+                                       .printStackTrace(System.err);
+                       vmVersion = 1.5;
+               }
+       }
+       
+       private static List<Integer> getFirstNumbers(String vm) {
+               List<Integer> result = new ArrayList<Integer>();
+               StringTokenizer st = new StringTokenizer(vm,".-_");
+               try {
+                       result.add(Integer.parseInt(st.nextToken()));
+                       result.add(Integer.parseInt(st.nextToken()));
+               } catch (Exception e) {
+                       // NoSuchElementException if no more tokens
+                       // NumberFormatException if not a number
+               }
+               return result;
+       }
+
+       public static boolean is13VMOrGreater() {
+               return 1.3 <= vmVersion;
+       }
+
+       public static boolean is14VMOrGreater() {
+               return 1.4 <= vmVersion;
+       }
+
+       public static boolean is15VMOrGreater() {
+               return 1.5 <= vmVersion;
+       }
+
+       public static boolean is16VMOrGreater() {
+               return 1.6 <= vmVersion;
+       }
+
+       public static boolean is17VMOrGreater() {
+               return 1.7 <= vmVersion;
+       }
+       
+       public static boolean is18VMOrGreater() {
+               return 1.8 <= vmVersion;
+       }
+       
+       public static boolean is19VMOrGreater() {
+               return 9 <= vmVersion;
+       }
+       
+       public static boolean is10VMOrGreater() {
+               return 10 <= vmVersion;
+       }
+
+       public static boolean is11VMOrGreater() {
+               return 11 <= vmVersion;
+       }
+
+       /**
+        * Shorthand for "if null, throw IllegalArgumentException"
+        * 
+        * @throws IllegalArgumentException "null {name}" if o is null
+        */
+       public static final void throwIaxIfNull(final Object o, final String name) {
+               if (null == o) {
+                       String message = "null " + (null == name ? "input" : name);
+                       throw new IllegalArgumentException(message);
+               }
+       }
+
+       /**
+        * Shorthand for "if not null or not assignable, throw IllegalArgumentException"
+        * 
+        * @param c the Class to check - use null to ignore type check
+        * @throws IllegalArgumentException "null {name}" if o is null
+        */
+       public static final void throwIaxIfNotAssignable(final Object ra[], final Class<?> c, final String name) {
+               throwIaxIfNull(ra, name);
+               String label = (null == name ? "input" : name);
+               for (int i = 0; i < ra.length; i++) {
+                       if (null == ra[i]) {
+                               String m = " null " + label + "[" + i + "]";
+                               throw new IllegalArgumentException(m);
+                       } else if (null != c) {
+                               Class<?> actualClass = ra[i].getClass();
+                               if (!c.isAssignableFrom(actualClass)) {
+                                       String message = label + " not assignable to " + c.getName();
+                                       throw new IllegalArgumentException(message);
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Shorthand for "if not null or not assignable, throw IllegalArgumentException"
+        * 
+        * @throws IllegalArgumentException "null {name}" if o is null
+        */
+       public static final void throwIaxIfNotAssignable(final Object o, final Class<?> c, final String name) {
+               throwIaxIfNull(o, name);
+               if (null != c) {
+                       Class<?> actualClass = o.getClass();
+                       if (!c.isAssignableFrom(actualClass)) {
+                               String message = name + " not assignable to " + c.getName();
+                               throw new IllegalArgumentException(message);
+                       }
+               }
+       }
+
+       // /**
+       // * Shorthand for
+       // "if any not null or not assignable, throw IllegalArgumentException"
+       // * @throws IllegalArgumentException "{name} is not assignable to {c}"
+       // */
+       // public static final void throwIaxIfNotAllAssignable(final Collection
+       // collection,
+       // final Class c, final String name) {
+       // throwIaxIfNull(collection, name);
+       // if (null != c) {
+       // for (Iterator iter = collection.iterator(); iter.hasNext();) {
+       // throwIaxIfNotAssignable(iter.next(), c, name);
+       //                              
+       // }
+       // }
+       // }
+       /**
+        * Shorthand for "if false, throw IllegalArgumentException"
+        * 
+        * @throws IllegalArgumentException "{message}" if test is false
+        */
+       public static final void throwIaxIfFalse(final boolean test, final String message) {
+               if (!test) {
+                       throw new IllegalArgumentException(message);
+               }
+       }
+
+       // /** @return ((null == s) || (0 == s.trim().length())); */
+       // public static boolean isEmptyTrimmed(String s) {
+       // return ((null == s) || (0 == s.length())
+       // || (0 == s.trim().length()));
+       // }
+
+       /** @return ((null == s) || (0 == s.length())); */
+       public static boolean isEmpty(String s) {
+               return ((null == s) || (0 == s.length()));
+       }
+
+       /** @return ((null == ra) || (0 == ra.length)) */
+       public static boolean isEmpty(Object[] ra) {
+               return ((null == ra) || (0 == ra.length));
+       }
+
+       /** @return ((null == ra) || (0 == ra.length)) */
+       public static boolean isEmpty(byte[] ra) {
+               return ((null == ra) || (0 == ra.length));
+       }
+
+       /** @return ((null == collection) || (0 == collection.size())) */
+       public static boolean isEmpty(Collection<?> collection) {
+               return ((null == collection) || (0 == collection.size()));
+       }
+
+       /** @return ((null == map) || (0 == map.size())) */
+       public static boolean isEmpty(Map<?,?> map) {
+               return ((null == map) || (0 == map.size()));
+       }
+
+       /**
+        * Splits <code>text</code> at whitespace.
+        * 
+        * @param text <code>String</code> to split.
+        */
+       public static String[] split(String text) {
+               return strings(text).toArray(new String[0]);
+       }
+
+       /**
+        * Splits <code>input</code> at commas, trimming any white space.
+        * 
+        * @param input <code>String</code> to split.
+        * @return List of String of elements.
+        */
+       public static List<String> commaSplit(String input) {
+               return anySplit(input, ",");
+       }
+
+       /**
+        * Split string as classpath, delimited at File.pathSeparator. Entries are not trimmed, but empty entries are ignored.
+        * 
+        * @param classpath the String to split - may be null or empty
+        * @return String[] of classpath entries
+        */
+       public static String[] splitClasspath(String classpath) {
+               if (LangUtil.isEmpty(classpath)) {
+                       return new String[0];
+               }
+               StringTokenizer st = new StringTokenizer(classpath, File.pathSeparator);
+               ArrayList<String> result = new ArrayList<String>(st.countTokens());
+               while (st.hasMoreTokens()) {
+                       String entry = st.nextToken();
+                       if (!LangUtil.isEmpty(entry)) {
+                               result.add(entry);
+                       }
+               }
+               return result.toArray(new String[0]);
+       }
+
+       /**
+        * Get System property as boolean, but use default value where the system property is not set.
+        * 
+        * @return true if value is set to true, false otherwise
+        */
+       public static boolean getBoolean(String propertyName, boolean defaultValue) {
+               if (null != propertyName) {
+                       try {
+                               String value = System.getProperty(propertyName);
+                               if (null != value) {
+                                       return Boolean.valueOf(value).booleanValue();
+                               }
+                       } catch (Throwable t) {
+                               // default below
+                       }
+               }
+               return defaultValue;
+       }
+
+       /**
+        * Splits <code>input</code>, removing delimiter and trimming any white space. Returns an empty collection if the input is null.
+        * If delimiter is null or empty or if the input contains no delimiters, the input itself is returned after trimming white
+        * space.
+        * 
+        * @param input <code>String</code> to split.
+        * @param delim <code>String</code> separators for input.
+        * @return List of String of elements.
+        */
+       public static List<String> anySplit(String input, String delim) {
+               if (null == input) {
+                       return Collections.emptyList();
+               }
+               ArrayList<String> result = new ArrayList<String>();
+
+               if (LangUtil.isEmpty(delim) || (-1 == input.indexOf(delim))) {
+                       result.add(input.trim());
+               } else {
+                       StringTokenizer st = new StringTokenizer(input, delim);
+                       while (st.hasMoreTokens()) {
+                               result.add(st.nextToken().trim());
+                       }
+               }
+               return result;
+       }
+
+       /**
+        * Splits strings into a <code>List</code> using a <code>StringTokenizer</code>.
+        * 
+        * @param text <code>String</code> to split.
+        */
+       public static List<String> strings(String text) {
+               if (LangUtil.isEmpty(text)) {
+                       return Collections.emptyList();
+               }
+               List<String> strings = new ArrayList<String>();
+               StringTokenizer tok = new StringTokenizer(text);
+               while (tok.hasMoreTokens()) {
+                       strings.add(tok.nextToken());
+               }
+               return strings;
+       }
+
+       /** @return a non-null unmodifiable List */
+       public static <T> List<T> safeList(List<T> list) {
+               return (null == list ? Collections.<T>emptyList() : Collections.unmodifiableList(list));
+       }
+
+       // /**
+       // * Select from input String[] based on suffix-matching
+       // * @param inputs String[] of input - null ignored
+       // * @param suffixes String[] of suffix selectors - null ignored
+       // * @param ignoreCase if true, ignore case
+       // * @return String[] of input that end with any input
+       // */
+       // public static String[] endsWith(String[] inputs, String[] suffixes,
+       // boolean ignoreCase) {
+       // if (LangUtil.isEmpty(inputs) || LangUtil.isEmpty(suffixes)) {
+       // return new String[0];
+       // }
+       // if (ignoreCase) {
+       // String[] temp = new String[suffixes.length];
+       // for (int i = 0; i < temp.length; i++) {
+       // String suff = suffixes[i];
+       // temp[i] = (null == suff ? null : suff.toLowerCase());
+       // }
+       // suffixes = temp;
+       // }
+       // ArrayList result = new ArrayList();
+       // for (int i = 0; i < inputs.length; i++) {
+       // String input = inputs[i];
+       // if (null == input) {
+       // continue;
+       // }
+       // if (!ignoreCase) {
+       // input = input.toLowerCase();
+       // }
+       // for (int j = 0; j < suffixes.length; j++) {
+       // String suffix = suffixes[j];
+       // if (null == suffix) {
+       // continue;
+       // }
+       // if (input.endsWith(suffix)) {
+       // result.add(input);
+       // break;
+       // }
+       // }
+       // }
+       // return (String[]) result.toArray(new String[0]);
+       // }
+       //    
+       // /**
+       // * Select from input String[] if readable directories
+       // * @param inputs String[] of input - null ignored
+       // * @param baseDir the base directory of the input
+       // * @return String[] of input that end with any input
+       // */
+       // public static String[] selectDirectories(String[] inputs, File baseDir) {
+       // if (LangUtil.isEmpty(inputs)) {
+       // return new String[0];
+       // }
+       // ArrayList result = new ArrayList();
+       // for (int i = 0; i < inputs.length; i++) {
+       // String input = inputs[i];
+       // if (null == input) {
+       // continue;
+       // }
+       // File inputFile = new File(baseDir, input);
+       // if (inputFile.canRead() && inputFile.isDirectory()) {
+       // result.add(input);
+       // }
+       // }
+       // return (String[]) result.toArray(new String[0]);
+       // }
+
+       /**
+        * copy non-null two-dimensional String[][]
+        * 
+        * @see extractOptions(String[], String[][])
+        */
+       public static String[][] copyStrings(String[][] in) {
+               String[][] out = new String[in.length][];
+               for (int i = 0; i < out.length; i++) {
+                       out[i] = new String[in[i].length];
+                       System.arraycopy(in[i], 0, out[i], 0, out[i].length);
+               }
+               return out;
+       }
+
+       /**
+        * Extract options and arguments to input option list, returning remainder. The input options will be nullified if not found.
+        * e.g.,
+        * 
+        * <pre>
+        * String[] options = new String[][] { new String[] { &quot;-verbose&quot; }, new String[] { &quot;-classpath&quot;, null } };
+        * String[] args = extractOptions(args, options);
+        * boolean verbose = null != options[0][0];
+        * boolean classpath = options[1][1];
+        * </pre>
+        * 
+        * @param args the String[] input options
+        * @param options the String[][]options to find in the input args - not null for each String[] component the first subcomponent
+        *        is the option itself, and there is one String subcomponent for each additional argument.
+        * @return String[] of args remaining after extracting options to extracted
+        */
+       public static String[] extractOptions(String[] args, String[][] options) {
+               if (LangUtil.isEmpty(args) || LangUtil.isEmpty(options)) {
+                       return args;
+               }
+               BitSet foundSet = new BitSet();
+               String[] result = new String[args.length];
+               int resultIndex = 0;
+               for (int j = 0; j < args.length; j++) {
+                       boolean found = false;
+                       for (int i = 0; !found && (i < options.length); i++) {
+                               String[] option = options[i];
+                               LangUtil.throwIaxIfFalse(!LangUtil.isEmpty(option), "options");
+                               String sought = option[0];
+                               found = sought.equals(args[j]);
+                               if (found) {
+                                       foundSet.set(i);
+                                       int doMore = option.length - 1;
+                                       if (0 < doMore) {
+                                               final int MAX = j + doMore;
+                                               if (MAX >= args.length) {
+                                                       String s = "expecting " + doMore + " args after ";
+                                                       throw new IllegalArgumentException(s + args[j]);
+                                               }
+                                               for (int k = 1; k < option.length; k++) {
+                                                       option[k] = args[++j];
+                                               }
+                                       }
+                               }
+                       }
+                       if (!found) {
+                               result[resultIndex++] = args[j];
+                       }
+               }
+
+               // unset any not found
+               for (int i = 0; i < options.length; i++) {
+                       if (!foundSet.get(i)) {
+                               options[i][0] = null;
+                       }
+               }
+               // fixup remainder
+               if (resultIndex < args.length) {
+                       String[] temp = new String[resultIndex];
+                       System.arraycopy(result, 0, temp, 0, resultIndex);
+                       args = temp;
+               }
+
+               return args;
+       }
+
+       //    
+       // /**
+       // * Extract options and arguments to input parameter list, returning
+       // remainder.
+       // * @param args the String[] input options
+       // * @param validOptions the String[] options to find in the input args -
+       // not null
+       // * @param optionArgs the int[] number of arguments for each option in
+       // validOptions
+       // * (if null, then no arguments for any option)
+       // * @param extracted the List for the matched options
+       // * @return String[] of args remaining after extracting options to
+       // extracted
+       // */
+       // public static String[] extractOptions(String[] args, String[]
+       // validOptions,
+       // int[] optionArgs, List extracted) {
+       // if (LangUtil.isEmpty(args)
+       // || LangUtil.isEmpty(validOptions) ) {
+       // return args;
+       // }
+       // if (null != optionArgs) {
+       // if (optionArgs.length != validOptions.length) {
+       // throw new IllegalArgumentException("args must match options");
+       // }
+       // }
+       // String[] result = new String[args.length];
+       // int resultIndex = 0;
+       // for (int j = 0; j < args.length; j++) {
+       // boolean found = false;
+       // for (int i = 0; !found && (i < validOptions.length); i++) {
+       // String sought = validOptions[i];
+       // int doMore = (null == optionArgs ? 0 : optionArgs[i]);
+       // if (LangUtil.isEmpty(sought)) {
+       // continue;
+       // }
+       // found = sought.equals(args[j]);
+       // if (found) {
+       // if (null != extracted) {
+       // extracted.add(sought);
+       // }
+       // if (0 < doMore) {
+       // final int MAX = j + doMore;
+       // if (MAX >= args.length) {
+       // String s = "expecting " + doMore + " args after ";
+       // throw new IllegalArgumentException(s + args[j]);
+       // }
+       // if (null != extracted) {
+       // while (j < MAX) {
+       // extracted.add(args[++j]);
+       // }
+       // } else {
+       // j = MAX;
+       // }
+       // }
+       // break;
+       // }
+       // }
+       // if (!found) {
+       // result[resultIndex++] = args[j];
+       // }
+       // }
+       // if (resultIndex < args.length) {
+       // String[] temp = new String[resultIndex];
+       // System.arraycopy(result, 0, temp, 0, resultIndex);
+       // args = temp;
+       // }
+       // return args;
+       // }
+
+       // /** @return String[] of entries in validOptions found in args */
+       // public static String[] selectOptions(String[] args, String[]
+       // validOptions) {
+       // if (LangUtil.isEmpty(args) || LangUtil.isEmpty(validOptions)) {
+       // return new String[0];
+       // }
+       // ArrayList result = new ArrayList();
+       // for (int i = 0; i < validOptions.length; i++) {
+       // String sought = validOptions[i];
+       // if (LangUtil.isEmpty(sought)) {
+       // continue;
+       // }
+       // for (int j = 0; j < args.length; j++) {
+       // if (sought.equals(args[j])) {
+       // result.add(sought);
+       // break;
+       // }
+       // }
+       // }
+       // return (String[]) result.toArray(new String[0]);
+       // }
+
+       // /** @return String[] of entries in validOptions found in args */
+       // public static String[] selectOptions(List args, String[] validOptions) {
+       // if (LangUtil.isEmpty(args) || LangUtil.isEmpty(validOptions)) {
+       // return new String[0];
+       // }
+       // ArrayList result = new ArrayList();
+       // for (int i = 0; i < validOptions.length; i++) {
+       // String sought = validOptions[i];
+       // if (LangUtil.isEmpty(sought)) {
+       // continue;
+       // }
+       // for (Iterator iter = args.iterator(); iter.hasNext();) {
+       // String arg = (String) iter.next();
+       // if (sought.equals(arg)) {
+       // result.add(sought);
+       // break;
+       // }
+       // }
+       // }
+       // return (String[]) result.toArray(new String[0]);
+       // }
+
+       // /**
+       // * Generate variants of String[] options by creating an extra set for
+       // * each option that ends with "-". If none end with "-", then an
+       // * array equal to <code>new String[][] { options }</code> is returned;
+       // * if one ends with "-", then two sets are returned,
+       // * three causes eight sets, etc.
+       // * @return String[][] with each option set.
+       // * @throws IllegalArgumentException if any option is null or empty.
+       // */
+       // public static String[][] optionVariants(String[] options) {
+       // if ((null == options) || (0 == options.length)) {
+       // return new String[][] { new String[0]};
+       // }
+       // // be nice, don't stomp input
+       // String[] temp = new String[options.length];
+       // System.arraycopy(options, 0, temp, 0, temp.length);
+       // options = temp;
+       // boolean[] dup = new boolean[options.length];
+       // int numDups = 0;
+       //        
+       // for (int i = 0; i < options.length; i++) {
+       // String option = options[i];
+       // if (LangUtil.isEmpty(option)) {
+       // throw new IllegalArgumentException("empty option at " + i);
+       // }
+       // if (option.endsWith("-")) {
+       // options[i] = option.substring(0, option.length()-1);
+       // dup[i] = true;
+       // numDups++;
+       // }
+       // }
+       // final String[] NONE = new String[0];
+       // final int variants = exp(2, numDups);
+       // final String[][] result = new String[variants][];
+       // // variant is a bitmap wrt doing extra value when dup[k]=true
+       // for (int variant = 0; variant < variants; variant++) {
+       // ArrayList next = new ArrayList();
+       // int nextOption = 0;
+       // for (int k = 0; k < options.length; k++) {
+       // if (!dup[k] || (0 != (variant & (1 << (nextOption++))))) {
+       // next.add(options[k]);
+       // }
+       // }
+       // result[variant] = (String[]) next.toArray(NONE);
+       // }
+       // return result;
+       // }
+       //    
+       // private static int exp(int base, int power) { // not in Math?
+       // if (0 > power) {
+       // throw new IllegalArgumentException("negative power: " + power);
+       // }
+       // int result = 1;
+       // while (0 < power--) {
+       // result *= base;
+       // }
+       // return result;
+       // }
+
+       // /**
+       // * Make a copy of the array.
+       // * @return an array with the same component type as source
+       // * containing same elements, even if null.
+       // * @throws IllegalArgumentException if source is null
+       // */
+       // public static final Object[] copy(Object[] source) {
+       // LangUtil.throwIaxIfNull(source, "source");
+       // final Class c = source.getClass().getComponentType();
+       // Object[] result = (Object[]) Array.newInstance(c, source.length);
+       // System.arraycopy(source, 0, result, 0, result.length);
+       // return result;
+       // }
+
+       /**
+        * Convert arrays safely. The number of elements in the result will be 1 smaller for each element that is null or not
+        * assignable. This will use sink if it has exactly the right size. The result will always have the same component type as sink.
+        * 
+        * @return an array with the same component type as sink containing any assignable elements in source (in the same order).
+        * @throws IllegalArgumentException if either is null
+        */
+       public static Object[] safeCopy(Object[] source, Object[] sink) {
+               final Class<?> sinkType = (null == sink ? Object.class : sink.getClass().getComponentType());
+               final int sourceLength = (null == source ? 0 : source.length);
+               final int sinkLength = (null == sink ? 0 : sink.length);
+
+               final int resultSize;
+               ArrayList<Object> result = null;
+               if (0 == sourceLength) {
+                       resultSize = 0;
+               } else {
+                       result = new ArrayList<Object>(sourceLength);
+                       for (int i = 0; i < sourceLength; i++) {
+                               if ((null != source[i]) && (sinkType.isAssignableFrom(source[i].getClass()))) {
+                                       result.add(source[i]);
+                               }
+                       }
+                       resultSize = result.size();
+               }
+               if (resultSize != sinkLength) {
+                       sink = (Object[]) Array.newInstance(sinkType, result.size());
+               }
+               if (0 < resultSize) {
+                       sink = result.toArray(sink);
+               }
+               return sink;
+       }
+
+       /**
+        * @return a String with the unqualified class name of the class (or "null")
+        */
+       public static String unqualifiedClassName(Class<?> c) {
+               if (null == c) {
+                       return "null";
+               }
+               String name = c.getName();
+               int loc = name.lastIndexOf(".");
+               if (-1 != loc) {
+                       name = name.substring(1 + loc);
+               }
+               return name;
+       }
+
+       /**
+        * @return a String with the unqualified class name of the object (or "null")
+        */
+       public static String unqualifiedClassName(Object o) {
+               return LangUtil.unqualifiedClassName(null == o ? null : o.getClass());
+       }
+
+       /** inefficient way to replace all instances of sought with replace */
+       public static String replace(String in, String sought, String replace) {
+               if (LangUtil.isEmpty(in) || LangUtil.isEmpty(sought)) {
+                       return in;
+               }
+               StringBuffer result = new StringBuffer();
+               final int len = sought.length();
+               int start = 0;
+               int loc;
+               while (-1 != (loc = in.indexOf(sought, start))) {
+                       result.append(in.substring(start, loc));
+                       if (!LangUtil.isEmpty(replace)) {
+                               result.append(replace);
+                       }
+                       start = loc + len;
+               }
+               result.append(in.substring(start));
+               return result.toString();
+       }
+
+       /** render i right-justified with a given width less than about 40 */
+       public static String toSizedString(long i, int width) {
+               String result = "" + i;
+               int size = result.length();
+               if (width > size) {
+                       final String pad = "                                              ";
+                       final int padLength = pad.length();
+                       if (width > padLength) {
+                               width = padLength;
+                       }
+                       int topad = width - size;
+                       result = pad.substring(0, topad) + result;
+               }
+               return result;
+       }
+
+       // /** clip StringBuffer to maximum number of lines */
+       // static String clipBuffer(StringBuffer buffer, int maxLines) {
+       // if ((null == buffer) || (1 > buffer.length())) return "";
+       // StringBuffer result = new StringBuffer();
+       // int j = 0;
+       // final int MAX = maxLines;
+       // final int N = buffer.length();
+       // for (int i = 0, srcBegin = 0; i < MAX; srcBegin += j) {
+       // // todo: replace with String variant if/since getting char?
+       // char[] chars = new char[128];
+       // int srcEnd = srcBegin+chars.length;
+       // if (srcEnd >= N) {
+       // srcEnd = N-1;
+       // }
+       // if (srcBegin == srcEnd) break;
+       // //log("srcBegin:" + srcBegin + ":srcEnd:" + srcEnd);
+       // buffer.getChars(srcBegin, srcEnd, chars, 0);
+       // for (j = 0; j < srcEnd-srcBegin/*chars.length*/; j++) {
+       // char c = chars[j];
+       // if (c == '\n') {
+       // i++;
+       // j++;
+       // break;
+       // }
+       // }
+       // try { result.append(chars, 0, j); }
+       // catch (Throwable t) { }
+       // }
+       // return result.toString();
+       // }
+
+       /**
+        * @return "({UnqualifiedExceptionClass}) {message}"
+        */
+       public static String renderExceptionShort(Throwable e) {
+               if (null == e) {
+                       return "(Throwable) null";
+               }
+               return "(" + LangUtil.unqualifiedClassName(e) + ") " + e.getMessage();
+       }
+
+       /**
+        * Renders exception <code>t</code> after unwrapping and eliding any test packages.
+        * 
+        * @param t <code>Throwable</code> to print.
+        * @see #maxStackTrace
+        */
+       public static String renderException(Throwable t) {
+               return renderException(t, true);
+       }
+
+       /**
+        * Renders exception <code>t</code>, unwrapping, optionally eliding and limiting total number of lines.
+        * 
+        * @param t <code>Throwable</code> to print.
+        * @param elide true to limit to 100 lines and elide test packages
+        * @see StringChecker#TEST_PACKAGES
+        */
+       public static String renderException(Throwable t, boolean elide) {
+               if (null == t) {
+                       return "null throwable";
+               }
+               t = unwrapException(t);
+               StringBuffer stack = stackToString(t, false);
+               if (elide) {
+                       elideEndingLines(StringChecker.TEST_PACKAGES, stack, 100);
+               }
+               return stack.toString();
+       }
+
+       /**
+        * Trim ending lines from a StringBuffer, clipping to maxLines and further removing any number of trailing lines accepted by
+        * checker.
+        * 
+        * @param checker returns true if trailing line should be elided.
+        * @param stack StringBuffer with lines to elide
+        * @param maxLines int for maximum number of resulting lines
+        */
+       static void elideEndingLines(StringChecker checker, StringBuffer stack, int maxLines) {
+               if (null == checker || (null == stack) || (0 == stack.length())) {
+                       return;
+               }
+               final LinkedList<String> lines = new LinkedList<String>();
+               StringTokenizer st = new StringTokenizer(stack.toString(), "\n\r");
+               while (st.hasMoreTokens() && (0 < --maxLines)) {
+                       lines.add(st.nextToken());
+               }
+               st = null;
+
+               String line;
+               int elided = 0;
+               while (!lines.isEmpty()) {
+                       line = lines.getLast();
+                       if (!checker.acceptString(line)) {
+                               break;
+                       } else {
+                               elided++;
+                               lines.removeLast();
+                       }
+               }
+               if ((elided > 0) || (maxLines < 1)) {
+                       final int EOL_LEN = EOL.length();
+                       int totalLength = 0;
+                       while (!lines.isEmpty()) {
+                               totalLength += EOL_LEN + lines.getFirst().length();
+                               lines.removeFirst();
+                       }
+                       if (stack.length() > totalLength) {
+                               stack.setLength(totalLength);
+                               if (elided > 0) {
+                                       stack.append("    (... " + elided + " lines...)");
+                               }
+                       }
+               }
+       }
+
+       /** Dump message and stack to StringBuffer. */
+       public static StringBuffer stackToString(Throwable throwable, boolean skipMessage) {
+               if (null == throwable) {
+                       return new StringBuffer();
+               }
+               StringWriter buf = new StringWriter();
+               PrintWriter writer = new PrintWriter(buf);
+               if (!skipMessage) {
+                       writer.println(throwable.getMessage());
+               }
+               throwable.printStackTrace(writer);
+               try {
+                       buf.close();
+               } catch (IOException ioe) {
+               } // ignored
+               return buf.getBuffer();
+       }
+
+       /** @return Throwable input or tail of any wrapped exception chain */
+       public static Throwable unwrapException(Throwable t) {
+               Throwable current = t;
+               Throwable next = null;
+               while (current != null) {
+                       // Java 1.2 exceptions that carry exceptions
+                       if (current instanceof InvocationTargetException) {
+                               next = ((InvocationTargetException) current).getTargetException();
+                       } else if (current instanceof ClassNotFoundException) {
+                               next = ((ClassNotFoundException) current).getException();
+                       } else if (current instanceof ExceptionInInitializerError) {
+                               next = ((ExceptionInInitializerError) current).getException();
+                       } else if (current instanceof PrivilegedActionException) {
+                               next = ((PrivilegedActionException) current).getException();
+                       } else if (current instanceof SQLException) {
+                               next = ((SQLException) current).getNextException();
+                       }
+                       // ...getException():
+                       // javax.naming.event.NamingExceptionEvent
+                       // javax.naming.ldap.UnsolicitedNotification
+                       // javax.xml.parsers.FactoryConfigurationError
+                       // javax.xml.transform.TransformerFactoryConfigurationError
+                       // javax.xml.transform.TransformerException
+                       // org.xml.sax.SAXException
+                       // 1.4: Throwable.getCause
+                       // java.util.logging.LogRecord.getThrown()
+                       if (null == next) {
+                               break;
+                       } else {
+                               current = next;
+                               next = null;
+                       }
+               }
+               return current;
+       }
+
+       /**
+        * Replacement for Arrays.asList(..) which gacks on null and returns a List in which remove is an unsupported operation.
+        * 
+        * @param array the Object[] to convert (may be null)
+        * @return the List corresponding to array (never null)
+        */
+       public static <T> List<T> arrayAsList(T[] array) {
+               if ((null == array) || (1 > array.length)) {
+                       return Collections.emptyList();
+               }
+               ArrayList<T> list = new ArrayList<T>();
+               list.addAll(Arrays.asList(array));
+               return list;
+       }
+
+       /** check if input contains any packages to elide. */
+       public static class StringChecker {
+               static StringChecker TEST_PACKAGES = new StringChecker(new String[] { "org.aspectj.testing",
+                               "org.eclipse.jdt.internal.junit", "junit.framework.",
+                               "org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" });
+
+               String[] infixes;
+
+               /** @param infixes adopted */
+               StringChecker(String[] infixes) {
+                       this.infixes = infixes;
+               }
+
+               /** @return true if input contains infixes */
+               public boolean acceptString(String input) {
+                       boolean result = false;
+                       if (!LangUtil.isEmpty(input)) {
+                               for (int i = 0; !result && (i < infixes.length); i++) {
+                                       result = (-1 != input.indexOf(infixes[i]));
+                               }
+                       }
+                       return result;
+               }
+       }
+
+       /**
+        * Gen classpath.
+        * 
+        * @param bootclasspath
+        * @param classpath
+        * @param classesDir
+        * @param outputJar
+        * @return String combining classpath elements
+        */
+       public static String makeClasspath( // XXX dumb implementation
+                       String bootclasspath, String classpath, String classesDir, String outputJar) {
+               StringBuffer sb = new StringBuffer();
+               addIfNotEmpty(bootclasspath, sb, File.pathSeparator);
+               addIfNotEmpty(classpath, sb, File.pathSeparator);
+               if (!addIfNotEmpty(classesDir, sb, File.pathSeparator)) {
+                       addIfNotEmpty(outputJar, sb, File.pathSeparator);
+               }
+               return sb.toString();
+       }
+
+       /**
+        * @param input ignored if null
+        * @param sink the StringBuffer to add input to - return false if null
+        * @param delimiter the String to append to input when added - ignored if empty
+        * @return true if input + delimiter added to sink
+        */
+       private static boolean addIfNotEmpty(String input, StringBuffer sink, String delimiter) {
+               if (LangUtil.isEmpty(input) || (null == sink)) {
+                       return false;
+               }
+               sink.append(input);
+               if (!LangUtil.isEmpty(delimiter)) {
+                       sink.append(delimiter);
+               }
+               return true;
+       }
+
+       /**
+        * Create or initialize a process controller to run a process in another VM asynchronously.
+        * 
+        * @param controller the ProcessController to initialize, if not null
+        * @param classpath
+        * @param mainClass
+        * @param args
+        * @return initialized ProcessController
+        */
+       public static ProcessController makeProcess(ProcessController controller, String classpath, String mainClass, String[] args) {
+               File java = LangUtil.getJavaExecutable();
+               ArrayList<String> cmd = new ArrayList<String>();
+               cmd.add(java.getAbsolutePath());
+               cmd.add("-classpath");
+               cmd.add(classpath);
+               cmd.add(mainClass);
+               if (!LangUtil.isEmpty(args)) {
+                       cmd.addAll(Arrays.asList(args));
+               }
+               String[] command = cmd.toArray(new String[0]);
+               if (null == controller) {
+                       controller = new ProcessController();
+               }
+               controller.init(command, mainClass);
+               return controller;
+       }
+
+       // /**
+       // * Create a process to run asynchronously.
+       // * @param controller if not null, initialize this one
+       // * @param command the String[] command to run
+       // * @param controller the ProcessControl for streams and results
+       // */
+       // public static ProcessController makeProcess( // not needed?
+       // ProcessController controller,
+       // String[] command,
+       // String label) {
+       // if (null == controller) {
+       // controller = new ProcessController();
+       // }
+       // controller.init(command, label);
+       // return controller;
+       // }
+
+       /**
+        * Find java executable File path from java.home system property.
+        * 
+        * @return File associated with the java command, or null if not found.
+        */
+       public static File getJavaExecutable() {
+               String javaHome = null;
+               File result = null;
+               // java.home
+               // java.class.path
+               // java.ext.dirs
+               try {
+                       javaHome = System.getProperty("java.home");
+               } catch (Throwable t) {
+                       // ignore
+               }
+               if (null != javaHome) {
+                       File binDir = new File(javaHome, "bin");
+                       if (binDir.isDirectory() && binDir.canRead()) {
+                               String[] execs = new String[] { "java", "java.exe" };
+                               for (int i = 0; i < execs.length; i++) {
+                                       result = new File(binDir, execs[i]);
+                                       if (result.canRead()) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               return result;
+       }
+
+       // /**
+       // * Sleep for a particular period (in milliseconds).
+       // *
+       // * @param time the long time in milliseconds to sleep
+       // * @return true if delay succeeded, false if interrupted 100 times
+       // */
+       // public static boolean sleep(long milliseconds) {
+       // if (milliseconds == 0) {
+       // return true;
+       // } else if (milliseconds < 0) {
+       // throw new IllegalArgumentException("negative: " + milliseconds);
+       // }
+       // return sleepUntil(milliseconds + System.currentTimeMillis());
+       // }
+
+       /**
+        * Sleep until a particular time.
+        * 
+        * @param time the long time in milliseconds to sleep until
+        * @return true if delay succeeded, false if interrupted 100 times
+        */
+       public static boolean sleepUntil(long time) {
+               if (time == 0) {
+                       return true;
+               } else if (time < 0) {
+                       throw new IllegalArgumentException("negative: " + time);
+               }
+               // final Thread thread = Thread.currentThread();
+               long curTime = System.currentTimeMillis();
+               for (int i = 0; (i < 100) && (curTime < time); i++) {
+                       try {
+                               Thread.sleep(time - curTime);
+                       } catch (InterruptedException e) {
+                               // ignore
+                       }
+                       curTime = System.currentTimeMillis();
+               }
+               return (curTime >= time);
+       }
+
+       /**
+        * Handle an external process asynchrously. <code>start()</code> launches a main thread to wait for the process and pipes
+        * streams (in child threads) through to the corresponding streams (e.g., the process System.err to this System.err). This can
+        * complete normally, by exception, or on demand by a client. Clients can implement <code>doCompleting(..)</code> to get notice
+        * when the process completes.
+        * <p>
+        * The following sample code creates a process with a completion callback starts it, and some time later retries the process.
+        * 
+        * <pre>
+        * LangUtil.ProcessController controller = new LangUtil.ProcessController() {
+        *      protected void doCompleting(LangUtil.ProcessController.Thrown thrown, int result) {
+        *              // signal result 
+        *      }
+        * };
+        * controller.init(new String[] { &quot;java&quot;, &quot;-version&quot; }, &quot;java version&quot;);
+        * controller.start();
+        * // some time later...
+        * // retry...
+        * if (!controller.completed()) {
+        *      controller.stop();
+        *      controller.reinit();
+        *      controller.start();
+        * }
+        * </pre>
+        * 
+        * <u>warning</u>: Currently this does not close the input or output streams, since doing so prevents their use later.
+        */
+       public static class ProcessController {
+               /*
+                * XXX not verified thread-safe, but should be. Known problems: - user stops (completed = true) then exception thrown from
+                * destroying process (stop() expects !completed) ...
+                */
+               private String[] command;
+               private String[] envp;
+               private String label;
+
+               private boolean init;
+               private boolean started;
+               private boolean completed;
+               /** if true, stopped by user when not completed */
+               private boolean userStopped;
+
+               private Process process;
+               private FileUtil.Pipe errStream;
+               private FileUtil.Pipe outStream;
+               private FileUtil.Pipe inStream;
+               private ByteArrayOutputStream errSnoop;
+               private ByteArrayOutputStream outSnoop;
+
+               private int result;
+               private Thrown thrown;
+
+               public ProcessController() {
+               }
+
+               /**
+                * Permit re-running using the same command if this is not started or if completed. Can also call this when done with
+                * results to release references associated with results (e.g., stack traces).
+                */
+               public final void reinit() {
+                       if (!init) {
+                               throw new IllegalStateException("must init(..) before reinit()");
+                       }
+                       if (started && !completed) {
+                               throw new IllegalStateException("not completed - do stop()");
+                       }
+                       // init everything but command and label
+                       started = false;
+                       completed = false;
+                       result = Integer.MIN_VALUE;
+                       thrown = null;
+                       process = null;
+                       errStream = null;
+                       outStream = null;
+                       inStream = null;
+               }
+
+               public final void init(String classpath, String mainClass, String[] args) {
+                       init(LangUtil.getJavaExecutable(), classpath, mainClass, args);
+               }
+
+               public final void init(File java, String classpath, String mainClass, String[] args) {
+                       LangUtil.throwIaxIfNull(java, "java");
+                       LangUtil.throwIaxIfNull(mainClass, "mainClass");
+                       LangUtil.throwIaxIfNull(args, "args");
+                       ArrayList<String> cmd = new ArrayList<String>();
+                       cmd.add(java.getAbsolutePath());
+                       cmd.add("-classpath");
+                       cmd.add(classpath);
+                       cmd.add(mainClass);
+                       if (!LangUtil.isEmpty(args)) {
+                               cmd.addAll(Arrays.asList(args));
+                       }
+                       init(cmd.toArray(new String[0]), mainClass);
+               }
+
+               public final void init(String[] command, String label) {
+                       this.command = (String[]) LangUtil.safeCopy(command, new String[0]);
+                       if (1 > this.command.length) {
+                               throw new IllegalArgumentException("empty command");
+                       }
+                       this.label = LangUtil.isEmpty(label) ? command[0] : label;
+                       init = true;
+                       reinit();
+               }
+
+               public final void setEnvp(String[] envp) {
+                       this.envp = (String[]) LangUtil.safeCopy(envp, new String[0]);
+                       if (1 > this.envp.length) {
+                               throw new IllegalArgumentException("empty envp");
+                       }
+               }
+
+               public final void setErrSnoop(ByteArrayOutputStream snoop) {
+                       errSnoop = snoop;
+                       if (null != errStream) {
+                               errStream.setSnoop(errSnoop);
+                       }
+               }
+
+               public final void setOutSnoop(ByteArrayOutputStream snoop) {
+                       outSnoop = snoop;
+                       if (null != outStream) {
+                               outStream.setSnoop(outSnoop);
+                       }
+               }
+
+               /**
+                * Start running the process and pipes asynchronously.
+                * 
+                * @return Thread started or null if unable to start thread (results available via <code>getThrown()</code>, etc.)
+                */
+               public final Thread start() {
+                       if (!init) {
+                               throw new IllegalStateException("not initialized");
+                       }
+                       synchronized (this) {
+                               if (started) {
+                                       throw new IllegalStateException("already started");
+                               }
+                               started = true;
+                       }
+                       try {
+                               process = Runtime.getRuntime().exec(command);
+                       } catch (IOException e) {
+                               stop(e, Integer.MIN_VALUE);
+                               return null;
+                       }
+                       errStream = new FileUtil.Pipe(process.getErrorStream(), System.err);
+                       if (null != errSnoop) {
+                               errStream.setSnoop(errSnoop);
+                       }
+                       outStream = new FileUtil.Pipe(process.getInputStream(), System.out);
+                       if (null != outSnoop) {
+                               outStream.setSnoop(outSnoop);
+                       }
+                       inStream = new FileUtil.Pipe(System.in, process.getOutputStream());
+                       // start 4 threads, process & pipes for in, err, out
+                       Runnable processRunner = new Runnable() {
+                               @Override
+                               public void run() {
+                                       Throwable thrown = null;
+                                       int result = Integer.MIN_VALUE;
+                                       try {
+                                               // pipe threads are children
+                                               new Thread(errStream).start();
+                                               new Thread(outStream).start();
+                                               new Thread(inStream).start();
+                                               process.waitFor();
+                                               result = process.exitValue();
+                                       } catch (Throwable e) {
+                                               thrown = e;
+                                       } finally {
+                                               stop(thrown, result);
+                                       }
+                               }
+                       };
+                       Thread result = new Thread(processRunner, label);
+                       result.start();
+                       return result;
+               }
+
+               /**
+                * Destroy any process, stop any pipes. This waits for the pipes to clear (reading until no more input is available), but
+                * does not wait for the input stream for the pipe to close (i.e., not waiting for end-of-file on input stream).
+                */
+               public final synchronized void stop() {
+                       if (completed) {
+                               return;
+                       }
+                       userStopped = true;
+                       stop(null, Integer.MIN_VALUE);
+               }
+
+               public final String[] getCommand() {
+                       String[] toCopy = command;
+                       if (LangUtil.isEmpty(toCopy)) {
+                               return new String[0];
+                       }
+                       String[] result = new String[toCopy.length];
+                       System.arraycopy(toCopy, 0, result, 0, result.length);
+                       return result;
+               }
+
+               public final boolean completed() {
+                       return completed;
+               }
+
+               public final boolean started() {
+                       return started;
+               }
+
+               public final boolean userStopped() {
+                       return userStopped;
+               }
+
+               /**
+                * Get any Throwable thrown. Note that the process can complete normally (with a valid return value), at the same time the
+                * pipes throw exceptions, and that this may return some exceptions even if the process is not complete.
+                * 
+                * @return null if not complete or Thrown containing exceptions thrown by the process and streams.
+                */
+               public final Thrown getThrown() { // cache this
+                       return makeThrown(null);
+               }
+
+               public final int getResult() {
+                       return result;
+               }
+
+               /**
+                * Subclasses implement this to get synchronous notice of completion. All pipes and processes should be complete at this
+                * time. To get the exceptions thrown for the pipes, use <code>getThrown()</code>. If there is an exception, the process
+                * completed abruptly (including side-effects of the user halting the process). If <code>userStopped()</code> is true, then
+                * some client asked that the process be destroyed using <code>stop()</code>. Otherwise, the result code should be the
+                * result value returned by the process.
+                * 
+                * @param thrown same as <code>getThrown().fromProcess</code>.
+                * @param result same as <code>getResult()</code>
+                * @see getThrown()
+                * @see getResult()
+                * @see stop()
+                */
+               protected void doCompleting(Thrown thrown, int result) {
+               }
+
+               /**
+                * Handle termination (on-demand, abrupt, or normal) by destroying and/or halting process and pipes.
+                * 
+                * @param thrown ignored if null
+                * @param result ignored if Integer.MIN_VALUE
+                */
+               private final synchronized void stop(Throwable thrown, int result) {
+                       if (completed) {
+                               throw new IllegalStateException("already completed");
+                       } else if (null != this.thrown) {
+                               throw new IllegalStateException("already set thrown: " + thrown);
+                       }
+                       // assert null == this.thrown
+                       this.thrown = makeThrown(thrown);
+                       if (null != process) {
+                               process.destroy();
+                       }
+                       if (null != inStream) {
+                               inStream.halt(false, true); // this will block if waiting
+                               inStream = null;
+                       }
+                       if (null != outStream) {
+                               outStream.halt(true, true);
+                               outStream = null;
+                       }
+                       if (null != errStream) {
+                               errStream.halt(true, true);
+                               errStream = null;
+                       }
+                       if (Integer.MIN_VALUE != result) {
+                               this.result = result;
+                       }
+                       completed = true;
+                       doCompleting(this.thrown, result);
+               }
+
+               /**
+                * Create snapshot of Throwable's thrown.
+                * 
+                * @param thrown ignored if null or if this.thrown is not null
+                */
+               private final synchronized Thrown makeThrown(Throwable processThrown) {
+                       if (null != thrown) {
+                               return thrown;
+                       }
+                       return new Thrown(processThrown, (null == outStream ? null : outStream.getThrown()), (null == errStream ? null
+                                       : errStream.getThrown()), (null == inStream ? null : inStream.getThrown()));
+               }
+
+               public static class Thrown {
+                       public final Throwable fromProcess;
+                       public final Throwable fromErrPipe;
+                       public final Throwable fromOutPipe;
+                       public final Throwable fromInPipe;
+                       /** true only if some Throwable is not null */
+                       public final boolean thrown;
+
+                       private Thrown(Throwable fromProcess, Throwable fromOutPipe, Throwable fromErrPipe, Throwable fromInPipe) {
+                               this.fromProcess = fromProcess;
+                               this.fromErrPipe = fromErrPipe;
+                               this.fromOutPipe = fromOutPipe;
+                               this.fromInPipe = fromInPipe;
+                               thrown = ((null != fromProcess) || (null != fromInPipe) || (null != fromOutPipe) || (null != fromErrPipe));
+                       }
+
+                       @Override
+                       public String toString() {
+                               StringBuffer sb = new StringBuffer();
+                               append(sb, fromProcess, "process");
+                               append(sb, fromOutPipe, " stdout");
+                               append(sb, fromErrPipe, " stderr");
+                               append(sb, fromInPipe, "  stdin");
+                               if (0 == sb.length()) {
+                                       return "Thrown (none)";
+                               } else {
+                                       return sb.toString();
+                               }
+                       }
+
+                       private void append(StringBuffer sb, Throwable thrown, String label) {
+                               if (null != thrown) {
+                                       sb.append("from " + label + ": ");
+                                       sb.append(LangUtil.renderExceptionShort(thrown));
+                                       sb.append(LangUtil.EOL);
+                               }
+                       }
+               } // class Thrown
+       }
+       
+       public static String getJrtFsFilePath() {
+               return getJavaHome() + File.separator + "lib" + File.separator + JRT_FS;
+       }
+               
+       public static String getJavaHome() {
+           return System.getProperty("java.home");
+       }
+
+}
diff --git a/util/src/main/java/org/aspectj/util/PartialOrder.java b/util/src/main/java/org/aspectj/util/PartialOrder.java
new file mode 100644 (file)
index 0000000..8bb9f3b
--- /dev/null
@@ -0,0 +1,223 @@
+/* *******************************************************************
+ * Copyright (c) 1999-2001 Xerox Corporation, 
+ *               2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/epl-v10.html 
+ *  
+ * Contributors: 
+ *     Xerox/PARC     initial implementation 
+ * ******************************************************************/
+
+package org.aspectj.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This class implements a partial order
+ * 
+ * It includes routines for doing a topo-sort
+ */
+
+public class PartialOrder {
+
+       /**
+        * All classes that want to be part of a partial order must implement PartialOrder.PartialComparable.
+        */
+       public static interface PartialComparable {
+               /**
+                * @returns <ul>
+                *          <li>+1 if this is greater than other</li>
+                *          <li>-1 if this is less than other</li>
+                *          <li>0 if this is not comparable to other</li>
+                *          </ul>
+                * 
+                *          <b> Note: returning 0 from this method doesn't mean the same thing as returning 0 from
+                *          java.util.Comparable.compareTo()</b>
+                */
+               public int compareTo(Object other);
+
+               /**
+                * This method can provide a deterministic ordering for elements that are strictly not comparable. If you have no need for
+                * this, this method can just return 0 whenever called.
+                */
+               public int fallbackCompareTo(Object other);
+       }
+
+       private static class SortObject<T extends PartialComparable> {
+               T object;
+               List<SortObject<T>> smallerObjects = new LinkedList<SortObject<T>>();
+               List<SortObject<T>> biggerObjects = new LinkedList<SortObject<T>>();
+
+               public SortObject(T o) {
+                       object = o;
+               }
+
+               boolean hasNoSmallerObjects() {
+                       return smallerObjects.size() == 0;
+               }
+
+               boolean removeSmallerObject(SortObject<T> o) {
+                       smallerObjects.remove(o);
+                       return hasNoSmallerObjects();
+               }
+
+               void addDirectedLinks(SortObject<T> other) {
+                       int cmp = object.compareTo(other.object);
+                       if (cmp == 0) {
+                               return;
+                       }
+                       if (cmp > 0) {
+                               this.smallerObjects.add(other);
+                               other.biggerObjects.add(this);
+                       } else {
+                               this.biggerObjects.add(other);
+                               other.smallerObjects.add(this);
+                       }
+               }
+
+               public String toString() {
+                       return object.toString(); // +smallerObjects+biggerObjects;
+               }
+       }
+
+       private static <T extends PartialComparable> void addNewPartialComparable(List<SortObject<T>> graph, T o) {
+               SortObject<T> so = new SortObject<T>(o);
+               for (Iterator<SortObject<T>> i = graph.iterator(); i.hasNext();) {
+                       SortObject<T> other = i.next();
+                       so.addDirectedLinks(other);
+               }
+               graph.add(so);
+       }
+
+       private static <T extends PartialComparable> void removeFromGraph(List<SortObject<T>> graph, SortObject<T> o) {
+               for (Iterator<SortObject<T>> i = graph.iterator(); i.hasNext();) {
+                       SortObject<T> other = i.next();
+
+                       if (o == other) {
+                               i.remove();
+                       }
+                       // ??? could use this to build up a new queue of objects with no
+                       // ??? smaller ones
+                       other.removeSmallerObject(o);
+               }
+       }
+
+       /**
+        * @param objects must all implement PartialComparable
+        * 
+        * @returns the same members as objects, but sorted according to their partial order. returns null if the objects are cyclical
+        * 
+        */
+       public static <T extends PartialComparable> List<T> sort(List<T> objects) {
+               // lists of size 0 or 1 don't need any sorting
+               if (objects.size() < 2) {
+                       return objects;
+               }
+
+               // ??? we might want to optimize a few other cases of small size
+
+               // ??? I don't like creating this data structure, but it does give good
+               // ??? separation of concerns.
+               List<SortObject<T>> sortList = new LinkedList<SortObject<T>>();
+               for (Iterator<T> i = objects.iterator(); i.hasNext();) {
+                       addNewPartialComparable(sortList, i.next());
+               }
+
+               // System.out.println(sortList);
+
+               // now we have built our directed graph
+               // use a simple sort algorithm from here
+               // can increase efficiency later
+               // List ret = new ArrayList(objects.size());
+               final int N = objects.size();
+               for (int index = 0; index < N; index++) {
+                       // System.out.println(sortList);
+                       // System.out.println("-->" + ret);
+
+                       SortObject<T> leastWithNoSmallers = null;
+
+                       for (SortObject<T> so: sortList) {
+                               if (so.hasNoSmallerObjects()) {
+                                       if (leastWithNoSmallers == null || so.object.fallbackCompareTo(leastWithNoSmallers.object) < 0) {
+                                               leastWithNoSmallers = so;
+                                       }
+                               }
+                       }
+
+                       if (leastWithNoSmallers == null) {
+                               return null;
+                       }
+
+                       removeFromGraph(sortList, leastWithNoSmallers);
+                       objects.set(index, leastWithNoSmallers.object);
+               }
+
+               return objects;
+       }
+
+       /***********************************************************************************
+        * /* a minimal testing harness
+        ***********************************************************************************/
+       static class Token implements PartialComparable {
+               private String s;
+
+               Token(String s) {
+                       this.s = s;
+               }
+
+               public int compareTo(Object other) {
+                       Token t = (Token) other;
+
+                       int cmp = s.charAt(0) - t.s.charAt(0);
+                       if (cmp == 1) {
+                               return 1;
+                       }
+                       if (cmp == -1) {
+                               return -1;
+                       }
+                       return 0;
+               }
+
+               public int fallbackCompareTo(Object other) {
+                       return -s.compareTo(((Token) other).s);
+               }
+
+               public String toString() {
+                       return s;
+               }
+       }
+
+       public static void main(String[] args) {
+               List<Token> l = new ArrayList<Token>();
+               l.add(new Token("a1"));
+               l.add(new Token("c2"));
+               l.add(new Token("b3"));
+               l.add(new Token("f4"));
+               l.add(new Token("e5"));
+               l.add(new Token("d6"));
+               l.add(new Token("c7"));
+               l.add(new Token("b8"));
+
+               l.add(new Token("z"));
+               l.add(new Token("x"));
+
+               l.add(new Token("f9"));
+               l.add(new Token("e10"));
+               l.add(new Token("a11"));
+               l.add(new Token("d12"));
+               l.add(new Token("b13"));
+               l.add(new Token("c14"));
+
+               System.out.println(l);
+
+               sort(l);
+
+               System.out.println(l);
+       }
+}
diff --git a/util/src/main/java/org/aspectj/util/Reflection.java b/util/src/main/java/org/aspectj/util/Reflection.java
new file mode 100644 (file)
index 0000000..83ceaac
--- /dev/null
@@ -0,0 +1,180 @@
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/epl-v10.html 
+ *  
+ * Contributors: 
+ *     Xerox/PARC     initial implementation 
+ * ******************************************************************/
+
+
+package org.aspectj.util;
+
+import java.io.File;
+import java.lang.reflect.*;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+//import java.util.StringTokenizer;
+
+public class Reflection {
+    public static final Class<?>[] MAIN_PARM_TYPES = new Class[] {String[].class};    
+
+       private Reflection() {
+       }
+       
+       public static Object invokestaticN(Class<?> class_, String name, Object[] args) {
+               return invokeN(class_, name, null, args);
+       }
+
+       public static Object invoke(Class<?> class_, Object target, String name, Object arg1, Object arg2) {
+               return invokeN(class_, name, target, new Object[] { arg1, arg2 });
+       }
+       
+       public static Object invoke(Class<?> class_, Object target, String name, Object arg1, Object arg2, Object arg3) {
+               return invokeN(class_, name, target, new Object[] { arg1, arg2, arg3 });
+       }
+       
+       
+       public static Object invokeN(Class<?> class_, String name, Object target, Object[] args) {
+               Method meth = getMatchingMethod(class_, name, args);
+               try {
+                       return meth.invoke(target, args);
+               } catch (IllegalAccessException e) {
+                       throw new RuntimeException(e.toString());                       
+               } catch (InvocationTargetException e) {
+                       Throwable t = e.getTargetException();
+                       if (t instanceof Error) throw (Error)t;
+                       if (t instanceof RuntimeException) throw (RuntimeException)t;
+                       t.printStackTrace();
+                       throw new RuntimeException(t.toString());
+               }
+       }
+
+
+       public static Method getMatchingMethod(Class<?> class_, String name, Object[] args) {
+               Method[] meths = class_.getMethods();
+               for (int i=0; i < meths.length; i++) {
+                       Method meth = meths[i];
+                       if (meth.getName().equals(name) && isCompatible(meth, args)) {
+                               return meth;
+                       }
+               }
+               return null;
+       }
+
+       private static boolean isCompatible(Method meth, Object[] args) {
+               // ignore methods with overloading other than lengths
+               return meth.getParameterTypes().length == args.length;
+       }
+
+
+       
+
+       public static Object getStaticField(Class<?> class_, String name) {
+               try {
+                       return class_.getField(name).get(null);
+               } catch (IllegalAccessException e) {
+                       throw new RuntimeException("unimplemented");
+               } catch (NoSuchFieldException e) {
+                       throw new RuntimeException("unimplemented");
+               }
+       }
+
+    public static void runMainInSameVM(
+            String classpath,
+            String className, 
+            String[] args) 
+            throws SecurityException, NoSuchMethodException, 
+            IllegalArgumentException, IllegalAccessException, 
+            InvocationTargetException, ClassNotFoundException {
+        LangUtil.throwIaxIfNull(className, "class name");
+        if (LangUtil.isEmpty(classpath)) {
+            Class<?> mainClass = Class.forName(className);
+            runMainInSameVM(mainClass, args);
+            return;            
+        }
+        ArrayList<File> dirs = new ArrayList<File>();
+        ArrayList<File> libs = new ArrayList<File>();
+        ArrayList<URL> urls = new ArrayList<URL>();
+        String[] entries = LangUtil.splitClasspath(classpath);
+        for (int i = 0; i < entries.length; i++) {
+            String entry = entries[i];
+            URL url = makeURL(entry);
+            if (null != url) {
+                urls.add(url);
+            }
+            File file = new File(entries[i]);
+// tolerate bad entries b/c bootclasspath sometimes has them
+//            if (!file.canRead()) {
+//                throw new IllegalArgumentException("cannot read " + file);
+//            }
+            if (FileUtil.isZipFile(file)) {
+                libs.add(file);
+            } else if (file.isDirectory()) {
+                dirs.add(file);
+            } else {
+                // not URL, zip, or dir - unsure what to do
+            }
+        }
+        File[] dirRa = (File[]) dirs.toArray(new File[0]);
+        File[] libRa = (File[]) libs.toArray(new File[0]);
+        URL[] urlRa = (URL[]) urls.toArray(new URL[0]);
+        runMainInSameVM(urlRa, libRa, dirRa, className, args);
+    }
+        
+    public static void runMainInSameVM(
+            URL[] urls,
+            File[] libs, 
+            File[] dirs, 
+            String className, 
+            String[] args) 
+            throws SecurityException, NoSuchMethodException, 
+            IllegalArgumentException, IllegalAccessException, 
+            InvocationTargetException, ClassNotFoundException {
+        LangUtil.throwIaxIfNull(className, "class name");
+        LangUtil.throwIaxIfNotAssignable(libs, File.class, "jars");
+        LangUtil.throwIaxIfNotAssignable(dirs, File.class, "dirs");
+        URL[] libUrls = FileUtil.getFileURLs(libs);
+        if (!LangUtil.isEmpty(libUrls)) {
+            if (!LangUtil.isEmpty(urls)) {
+                URL[] temp = new URL[libUrls.length + urls.length];
+                System.arraycopy(urls, 0, temp, 0, urls.length);
+                System.arraycopy(urls, 0, temp, libUrls.length, urls.length);
+                urls = temp;
+            } else {
+                urls = libUrls;
+            }
+        }
+        UtilClassLoader loader = new UtilClassLoader(urls, dirs);
+        Class<?> targetClass = null;
+        try {
+            targetClass = loader.loadClass(className);
+        } catch (ClassNotFoundException e) {
+            String s = "unable to load class " + className
+                + " using class loader " + loader; 
+            throw new ClassNotFoundException(s);
+        }
+        Method main = targetClass.getMethod("main", MAIN_PARM_TYPES);
+        main.invoke(null, new Object[] { args });
+    }
+    
+    public static void runMainInSameVM(Class<?> mainClass, String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+        LangUtil.throwIaxIfNull(mainClass, "main class");
+        Method main = mainClass.getMethod("main", MAIN_PARM_TYPES);
+        main.invoke(null, new Object[] { args });
+    }
+
+    /** @return URL if the input is valid as such */
+    private static URL makeURL(String s) {
+        try {
+            return new URL(s);            
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+}
diff --git a/util/src/main/java/org/aspectj/util/SoftHashMap.java b/util/src/main/java/org/aspectj/util/SoftHashMap.java
new file mode 100644 (file)
index 0000000..94ae834
--- /dev/null
@@ -0,0 +1,95 @@
+/* *******************************************************************
+ * Copyright (c) 2017 Contributors
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/epl-v10.html 
+ * ******************************************************************/package org.aspectj.util;
+
+import java.lang.ref.*;
+import java.util.*;
+
+public class SoftHashMap<K,V> extends AbstractMap<K,V> {
+       private Map<K, SpecialValue> map;
+       private ReferenceQueue<? super V> rq = new ReferenceQueue();
+
+       public SoftHashMap() {
+               this.map = new HashMap<K,SpecialValue>();
+       }
+       
+       class SpecialValue extends SoftReference<V> {
+               private final K key;
+
+               SpecialValue(K k, V v) {
+                       super(v, rq);
+                       this.key = k;
+               }
+       }
+
+       @SuppressWarnings("unchecked")
+       private void processQueue() {
+               SpecialValue sv = null;
+               while ((sv = (SpecialValue)rq.poll()) != null) {
+                       map.remove(sv.key);
+               }
+       }
+
+       @Override
+       public V get(Object key) {
+               SpecialValue ref = map.get(key);
+               if (ref == null) {
+                       map.remove(key);
+                       return null;
+               }
+               V value = ref.get();
+               if (value == null) {
+                       map.remove(ref.key);
+                       return null;
+               }
+               return value;
+       }
+
+       @Override
+       public V put(K k, V v) {
+               processQueue();
+               SpecialValue sv = new SpecialValue(k, v);
+               SpecialValue result = map.put(k, sv);
+               return (result == null ? null : result.get());
+       }
+
+       @Override
+       public java.util.Set<Map.Entry<K,V>> entrySet() {
+               if (map.isEmpty()) { return Collections.<K,V>emptyMap().entrySet(); }
+               Map<K,V> currentContents = new HashMap<K,V>();
+               for (Map.Entry<K,SpecialValue> entry: map.entrySet()) {
+                       V currentValueForEntry = entry.getValue().get();
+                       if (currentValueForEntry != null) {
+                               currentContents.put(entry.getKey(), currentValueForEntry);
+                       }
+               }
+               return currentContents.entrySet();
+       }
+
+       @Override
+       public void clear() {
+               processQueue();
+               map.clear();
+       }
+
+       @Override
+       public int size() {
+               processQueue();
+               return map.size();
+       }
+
+       @Override
+       public V remove(Object k) {
+               processQueue();
+               SpecialValue ref = map.remove(k);
+               if (ref == null) {
+                       return null;
+               }
+               return ref.get();
+       }
+}
diff --git a/util/src/main/java/org/aspectj/util/TypeSafeEnum.java b/util/src/main/java/org/aspectj/util/TypeSafeEnum.java
new file mode 100644 (file)
index 0000000..99d2231
--- /dev/null
@@ -0,0 +1,44 @@
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/epl-v10.html 
+ *  
+ * Contributors: 
+ *     Xerox/PARC     initial implementation 
+ * ******************************************************************/
+
+package org.aspectj.util;
+
+import java.io.*;
+
+public class TypeSafeEnum {
+       private byte key;
+       private String name;
+
+       public TypeSafeEnum(String name, int key) {
+               this.name = name;
+               if (key > Byte.MAX_VALUE || key < Byte.MIN_VALUE) {
+                       throw new IllegalArgumentException("key doesn't fit into a byte: " + key);
+               }
+               this.key = (byte) key;
+       }
+
+       public String toString() {
+               return name;
+       }
+
+       public String getName() {
+               return name;
+       }
+
+       public byte getKey() {
+               return key;
+       }
+
+       public void write(DataOutputStream s) throws IOException {
+               s.writeByte(key);
+       }
+}
diff --git a/util/src/main/java/org/aspectj/util/UtilClassLoader.java b/util/src/main/java/org/aspectj/util/UtilClassLoader.java
new file mode 100644 (file)
index 0000000..091e022
--- /dev/null
@@ -0,0 +1,140 @@
+/* *******************************************************************
+ * Copyright (c) 2003 Contributors.
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/epl-v10.html 
+ *  
+ * Contributors: 
+ *     Isberg        initial implementation 
+ * ******************************************************************/
+
+package org.aspectj.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Load classes as File from File[] dirs or URL[] jars.
+ */
+public class UtilClassLoader extends URLClassLoader {
+
+    /** seek classes in dirs first */
+    List<File> dirs;
+
+    /** save URL[] only for toString */
+    private URL[] urlsForDebugString;
+    
+    public UtilClassLoader(URL[] urls, File[] dirs) {
+        super(urls);
+        LangUtil.throwIaxIfNotAssignable(dirs, File.class, "dirs");
+        this.urlsForDebugString = urls;
+        ArrayList<File> dcopy = new ArrayList<File>();
+        
+        if (!LangUtil.isEmpty(dirs)) {
+            dcopy.addAll(Arrays.asList(dirs));
+        }
+        this.dirs = Collections.unmodifiableList(dcopy);
+    }
+
+    
+    public URL getResource(String name) {
+        return ClassLoader.getSystemResource(name);
+    }
+    
+    public InputStream getResourceAsStream(String name) {
+        return ClassLoader.getSystemResourceAsStream(name);
+    } 
+    
+    public synchronized Class<?> loadClass(String name, boolean resolve)
+        throws ClassNotFoundException {
+        // search the cache, our dirs (if maybe test), 
+        // the system, the superclass (URL[]),
+        // and our dirs again (if not maybe test)
+        ClassNotFoundException thrown = null;
+        Class<?> result =  findLoadedClass(name);
+        if (null != result) {
+            resolve = false;
+        } else {
+            try { 
+                result = findSystemClass(name); 
+            } catch (ClassNotFoundException e) { 
+                thrown = e; 
+            }
+        }
+        if (null == result) {
+            try {
+                result = super.loadClass(name, resolve);
+            } catch (ClassNotFoundException e) {
+                thrown = e;
+            }
+            if (null != result) { // resolved by superclass
+                return result; 
+            }
+        }
+        if (null == result) {
+            byte[] data = readClass(name);
+            if (data != null) {
+                result = defineClass(name, data, 0, data.length);
+            } // handle ClassFormatError?            
+        }
+        
+        if (null == result) {
+            throw (null != thrown ? thrown : new ClassNotFoundException(name));
+        }
+        if (resolve) {
+            resolveClass(result);
+        }
+        return result;
+    }
+    
+    /** @return null if class not found or byte[] of class otherwise */
+    private byte[] readClass(String className) throws ClassNotFoundException {
+        final String fileName = className.replace('.', '/')+".class";
+        for (Iterator<File> iter = dirs.iterator(); iter.hasNext();) {
+            File file = new File(iter.next(), fileName);
+            if (file.canRead()) { 
+                return getClassData(file);
+            }
+        }
+        return null; 
+    }
+        
+    private byte[] getClassData(File f) {
+        try {
+            FileInputStream stream= new FileInputStream(f);
+            ByteArrayOutputStream out= new ByteArrayOutputStream(1000);
+            byte[] b= new byte[4096];
+            int n;
+            while ((n= stream.read(b)) != -1) {
+                out.write(b, 0, n);
+            }
+            stream.close();
+            out.close();
+            return out.toByteArray();
+        } catch (IOException e) {
+        }
+        return null;
+    }
+    
+    /** @return String with debug info: urls and classes used */
+    public String toString() {
+        return "UtilClassLoader(urls=" 
+            + Arrays.asList(urlsForDebugString)
+            + ", dirs="
+            + dirs
+            + ")";
+    }
+}
+
diff --git a/util/src/org/aspectj/util/FileUtil.java b/util/src/org/aspectj/util/FileUtil.java
deleted file mode 100644 (file)
index f3db2bd..0000000
+++ /dev/null
@@ -1,1577 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 1999-2001 Xerox Corporation, 
- *               2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *     Xerox/PARC     initial implementation 
- * ******************************************************************/
-
-package org.aspectj.util;
-
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.Writer;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-/**
- * @author Andy Clement
- * @author Kris De Volder
- */
-public class FileUtil {
-       /** default parent directory File when a file has a null parent */
-       public static final File DEFAULT_PARENT = new File("."); // XXX user.dir?
-
-       /** unmodifiable List of String source file suffixes (including leading ".") */
-       public static final List<String> SOURCE_SUFFIXES = Collections.unmodifiableList(Arrays.asList(new String[] { ".java", ".aj" }));
-
-       public static final FileFilter ZIP_FILTER = new FileFilter() {
-               public boolean accept(File file) {
-                       return isZipFile(file);
-               }
-
-               public String toString() {
-                       return "ZIP_FILTER";
-               }
-       };
-
-       // public static final FileFilter SOURCE_FILTER = new FileFilter() {
-       // public boolean accept(File file) {
-       // return hasSourceSuffix(file);
-       // }
-       //
-       // public String toString() {
-       // return "SOURCE_FILTER";
-       // }
-       // };
-
-       final static int[] INT_RA = new int[0];
-
-       /** accept all files */
-       public static final FileFilter ALL = new FileFilter() {
-               public boolean accept(File f) {
-                       return true;
-               }
-       };
-       public static final FileFilter DIRS_AND_WRITABLE_CLASSES = new FileFilter() {
-               public boolean accept(File file) {
-                       return ((null != file) && (file.isDirectory() || (file.canWrite() && file.getName().toLowerCase().endsWith(".class"))));
-               }
-       };
-       private static final boolean PERMIT_CVS;
-       static {
-               String name = FileUtil.class.getName() + ".PERMIT_CVS";
-               PERMIT_CVS = LangUtil.getBoolean(name, false);
-       }
-
-       /** @return true if file exists and is a zip file */
-       public static boolean isZipFile(File file) {
-               try {
-                       return (null != file) && new ZipFile(file) != null;
-               } catch (IOException e) {
-                       return false;
-               }
-       }
-
-       /** @return true if path ends with .zip or .jar */
-       // public static boolean hasZipSuffix(String path) {
-       // return ((null != path) && (0 != zipSuffixLength(path)));
-       // }
-       /** @return 0 if file has no zip/jar suffix or 4 otherwise */
-       public static int zipSuffixLength(File file) {
-               return (null == file ? 0 : zipSuffixLength(file.getPath()));
-       }
-
-       /** @return 0 if no zip/jar suffix or 4 otherwise */
-       public static int zipSuffixLength(String path) {
-               if ((null != path) && (4 < path.length())) {
-                       String test = path.substring(path.length() - 4).toLowerCase();
-                       if (".zip".equals(test) || ".jar".equals(test)) {
-                               return 4;
-                       }
-               }
-               return 0;
-       }
-
-       /** @return true if file path has a source suffix */
-       public static boolean hasSourceSuffix(File file) {
-               return ((null != file) && hasSourceSuffix(file.getPath()));
-       }
-
-       /** @return true if path ends with .java or .aj */
-       public static boolean hasSourceSuffix(String path) {
-               return ((null != path) && (0 != sourceSuffixLength(path)));
-       }
-
-       /**
-        * @return 0 if file has no source suffix or the length of the suffix otherwise
-        */
-       public static int sourceSuffixLength(File file) {
-               return (null == file ? 0 : sourceSuffixLength(file.getPath()));
-       }
-
-       /** @return 0 if no source suffix or the length of the suffix otherwise */
-       public static int sourceSuffixLength(String path) {
-               if (LangUtil.isEmpty(path)) {
-                       return 0;
-               }
-
-               for (Iterator<String> iter = SOURCE_SUFFIXES.iterator(); iter.hasNext();) {
-                       String suffix = iter.next();
-                       if (path.endsWith(suffix) || path.toLowerCase().endsWith(suffix)) {
-                               return suffix.length();
-                       }
-               }
-               return 0;
-       }
-
-       /** @return true if this is a readable directory */
-       public static boolean canReadDir(File dir) {
-               return ((null != dir) && dir.canRead() && dir.isDirectory());
-       }
-
-       /** @return true if this is a readable file */
-       public static boolean canReadFile(File file) {
-               return ((null != file) && file.canRead() && file.isFile());
-       }
-
-       /** @return true if dir is a writable directory */
-       public static boolean canWriteDir(File dir) {
-               return ((null != dir) && dir.canWrite() && dir.isDirectory());
-       }
-
-       /** @return true if this is a writable file */
-       public static boolean canWriteFile(File file) {
-               return ((null != file) && file.canWrite() && file.isFile());
-       }
-
-       // /**
-       // * @throws IllegalArgumentException unless file is readable and not a
-       // * directory
-       // */
-       // public static void throwIaxUnlessCanReadFile(File file, String label) {
-       // if (!canReadFile(file)) {
-       // throw new IllegalArgumentException(label + " not readable file: " +
-       // file);
-       // }
-       // }
-
-       /**
-        * @throws IllegalArgumentException unless dir is a readable directory
-        */
-       public static void throwIaxUnlessCanReadDir(File dir, String label) {
-               if (!canReadDir(dir)) {
-                       throw new IllegalArgumentException(label + " not readable dir: " + dir);
-               }
-       }
-
-       /**
-        * @throws IllegalArgumentException unless file is readable and not a directory
-        */
-       public static void throwIaxUnlessCanWriteFile(File file, String label) {
-               if (!canWriteFile(file)) {
-                       throw new IllegalArgumentException(label + " not writable file: " + file);
-               }
-       }
-
-       /** @throws IllegalArgumentException unless dir is a readable directory */
-       public static void throwIaxUnlessCanWriteDir(File dir, String label) {
-               if (!canWriteDir(dir)) {
-                       throw new IllegalArgumentException(label + " not writable dir: " + dir);
-               }
-       }
-
-       /** @return array same length as input, with String paths */
-       public static String[] getPaths(File[] files) {
-               if ((null == files) || (0 == files.length)) {
-                       return new String[0];
-               }
-               String[] result = new String[files.length];
-               for (int i = 0; i < result.length; i++) {
-                       if (null != files[i]) {
-                               result[i] = files[i].getPath();
-                       }
-               }
-               return result;
-       }
-
-       /** @return array same length as input, with String paths */
-       public static String[] getPaths(List<File> files) {
-               final int size = (null == files ? 0 : files.size());
-               if (0 == size) {
-                       return new String[0];
-               }
-               String[] result = new String[size];
-               for (int i = 0; i < size; i++) {
-                       File file = files.get(i);
-                       if (null != file) {
-                               result[i] = file.getPath();
-                       }
-               }
-               return result;
-       }
-
-       /**
-        * Extract the name of a class from the path to its file. If the basedir is null, then the class is assumed to be in the default
-        * package unless the classFile has one of the top-level suffixes { com, org, java, javax } as a parent directory.
-        *
-        * @param basedir the File of the base directory (prefix of classFile)
-        * @param classFile the File of the class to extract the name for
-        * @throws IllegalArgumentException if classFile is null or does not end with ".class" or a non-null basedir is not a prefix of
-        *         classFile
-        */
-       public static String fileToClassName(File basedir, File classFile) {
-               LangUtil.throwIaxIfNull(classFile, "classFile");
-               String classFilePath = normalizedPath(classFile);
-               if (!classFilePath.endsWith(".class")) {
-                       String m = classFile + " does not end with .class";
-                       throw new IllegalArgumentException(m);
-               }
-               classFilePath = classFilePath.substring(0, classFilePath.length() - 6);
-               if (null != basedir) {
-                       String basePath = normalizedPath(basedir);
-                       if (!classFilePath.startsWith(basePath)) {
-                               String m = classFile + " does not start with " + basedir;
-                               throw new IllegalArgumentException(m);
-                       }
-                       classFilePath = classFilePath.substring(basePath.length() + 1);
-               } else {
-                       final String[] suffixes = new String[] { "com", "org", "java", "javax" };
-                       boolean found = false;
-                       for (int i = 0; !found && (i < suffixes.length); i++) {
-                               int loc = classFilePath.indexOf(suffixes[i] + "/");
-                               if ((0 == loc) || ((-1 != loc) && ('/' == classFilePath.charAt(loc - 1)))) {
-                                       classFilePath = classFilePath.substring(loc);
-                                       found = true;
-                               }
-                       }
-                       if (!found) {
-                               int loc = classFilePath.lastIndexOf("/");
-                               if (-1 != loc) { // treat as default package
-                                       classFilePath = classFilePath.substring(loc + 1);
-                               }
-                       }
-               }
-               return classFilePath.replace('/', '.');
-       }
-
-       /**
-        * Normalize path for comparisons by rendering absolute, clipping basedir prefix, trimming and changing '\\' to '/'
-        *
-        * @param file the File with the path to normalize
-        * @param basedir the File for the prefix of the file to normalize - ignored if null
-        * @return "" if null or normalized path otherwise
-        * @throws IllegalArgumentException if basedir is not a prefix of file
-        */
-       public static String normalizedPath(File file, File basedir) {
-               String filePath = normalizedPath(file);
-               if (null != basedir) {
-                       String basePath = normalizedPath(basedir);
-                       if (filePath.startsWith(basePath)) {
-                               filePath = filePath.substring(basePath.length());
-                               if (filePath.startsWith("/")) {
-                                       filePath = filePath.substring(1);
-                               }
-                       }
-               }
-               return filePath;
-       }
-
-       /**
-        * Render a set of files to String as a path by getting absolute paths of each and delimiting with infix.
-        *
-        * @param files the File[] to flatten - may be null or empty
-        * @param infix the String delimiter internally between entries (if null, then use File.pathSeparator). (alias to
-        *        <code>flatten(getAbsolutePaths(files), infix)</code>
-        * @return String with absolute paths to entries in order, delimited with infix
-        */
-       public static String flatten(File[] files, String infix) {
-               if (LangUtil.isEmpty(files)) {
-                       return "";
-               }
-               return flatten(getPaths(files), infix);
-       }
-
-       /**
-        * Flatten File[] to String.
-        *
-        * @param files the File[] of paths to flatten - null ignored
-        * @param infix the String infix to use - null treated as File.pathSeparator
-        */
-       public static String flatten(String[] paths, String infix) {
-               if (null == infix) {
-                       infix = File.pathSeparator;
-               }
-               StringBuffer result = new StringBuffer();
-               boolean first = true;
-               for (int i = 0; i < paths.length; i++) {
-                       String path = paths[i];
-                       if (null == path) {
-                               continue;
-                       }
-                       if (first) {
-                               first = false;
-                       } else {
-                               result.append(infix);
-                       }
-                       result.append(path);
-               }
-               return result.toString();
-       }
-
-       /**
-        * Normalize path for comparisons by rendering absolute trimming and changing '\\' to '/'
-        *
-        * @return "" if null or normalized path otherwise
-        */
-       public static String normalizedPath(File file) {
-               return (null == file ? "" : weakNormalize(file.getAbsolutePath()));
-       }
-
-       /**
-        * Weakly normalize path for comparisons by trimming and changing '\\' to '/'
-        */
-       public static String weakNormalize(String path) {
-               if (null != path) {
-                       path = path.replace('\\', '/').trim();
-               }
-               return path;
-       }
-
-       /**
-        * Get best File for the first-readable path in input paths, treating entries prefixed "sp:" as system property keys. Safe to
-        * call in static initializers.
-        *
-        * @param paths the String[] of paths to check.
-        * @return null if not found, or valid File otherwise
-        */
-       public static File getBestFile(String[] paths) {
-               if (null == paths) {
-                       return null;
-               }
-               File result = null;
-               for (int i = 0; (null == result) && (i < paths.length); i++) {
-                       String path = paths[i];
-                       if (null == path) {
-                               continue;
-                       }
-                       if (path.startsWith("sp:")) {
-                               try {
-                                       path = System.getProperty(path.substring(3));
-                               } catch (Throwable t) {
-                                       path = null;
-                               }
-                               if (null == path) {
-                                       continue;
-                               }
-                       }
-                       try {
-                               File f = new File(path);
-                               if (f.exists() && f.canRead()) {
-                                       result = FileUtil.getBestFile(f);
-                               }
-                       } catch (Throwable t) {
-                               // swallow
-                       }
-               }
-               return result;
-       }
-
-       /**
-        * Render as best file, canonical or absolute.
-        *
-        * @param file the File to get the best File for (not null)
-        * @return File of the best-available path
-        * @throws IllegalArgumentException if file is null
-        */
-       public static File getBestFile(File file) {
-               LangUtil.throwIaxIfNull(file, "file");
-               if (file.exists()) {
-                       try {
-                               return file.getCanonicalFile();
-                       } catch (IOException e) {
-                               return file.getAbsoluteFile();
-                       }
-               } else {
-                       return file;
-               }
-       }
-
-       /**
-        * Render as best path, canonical or absolute.
-        *
-        * @param file the File to get the path for (not null)
-        * @return String of the best-available path
-        * @throws IllegalArgumentException if file is null
-        */
-       public static String getBestPath(File file) {
-               LangUtil.throwIaxIfNull(file, "file");
-               if (file.exists()) {
-                       try {
-                               return file.getCanonicalPath();
-                       } catch (IOException e) {
-                               return file.getAbsolutePath();
-                       }
-               } else {
-                       return file.getPath();
-               }
-       }
-
-       /** @return array same length as input, with String absolute paths */
-       public static String[] getAbsolutePaths(File[] files) {
-               if ((null == files) || (0 == files.length)) {
-                       return new String[0];
-               }
-               String[] result = new String[files.length];
-               for (int i = 0; i < result.length; i++) {
-                       if (null != files[i]) {
-                               result[i] = files[i].getAbsolutePath();
-                       }
-               }
-               return result;
-       }
-
-       /**
-        * Recursively delete the contents of dir, but not the dir itself
-        *
-        * @return the total number of files deleted
-        */
-       public static int deleteContents(File dir) {
-               return deleteContents(dir, ALL);
-       }
-
-       /**
-        * Recursively delete some contents of dir, but not the dir itself. This deletes any subdirectory which is empty after its files
-        * are deleted.
-        *
-        * @return the total number of files deleted
-        */
-       public static int deleteContents(File dir, FileFilter filter) {
-               return deleteContents(dir, filter, true);
-       }
-
-       /**
-        * Recursively delete some contents of dir, but not the dir itself. If deleteEmptyDirs is true, this deletes any subdirectory
-        * which is empty after its files are deleted.
-        *
-        * @param dir the File directory (if a file, the the file is deleted)
-        * @return the total number of files deleted
-        */
-       public static int deleteContents(File dir, FileFilter filter,
-                       boolean deleteEmptyDirs) {
-               if (null == dir) {
-                       throw new IllegalArgumentException("null dir");
-               }
-               if ((!dir.exists()) || (!dir.canWrite())) {
-                       return 0;
-               }
-               if (!dir.isDirectory()) {
-                       dir.delete();
-                       return 1;
-               }
-               String[] fromFiles = dir.list();
-               if (fromFiles == null) {
-                       return 0;
-               }
-               int result = 0;
-               for (int i = 0; i < fromFiles.length; i++) {
-                       String string = fromFiles[i];
-                       File file = new File(dir, string);
-                       if ((null == filter) || filter.accept(file)) {
-                               if (file.isDirectory()) {
-                                       result += deleteContents(file, filter, deleteEmptyDirs);
-                                       String[] fileContent = file.list();
-                                       if (deleteEmptyDirs && fileContent != null
-                                                       && 0 == fileContent.length) {
-                                               file.delete();
-                                       }
-                               } else {
-                                       /* boolean ret = */
-                                       file.delete();
-                                       result++;
-                               }
-                       }
-               }
-               return result;
-       }
-
-       /**
-        * Copy contents of fromDir into toDir
-        *
-        * @param fromDir must exist and be readable
-        * @param toDir must exist or be creatable and be writable
-        * @return the total number of files copied
-        */
-       public static int copyDir(File fromDir, File toDir) throws IOException {
-               return copyDir(fromDir, toDir, null, null);
-       }
-
-       /**
-        * Recursively copy files in fromDir (with any fromSuffix) to toDir, replacing fromSuffix with toSuffix if any. This silently
-        * ignores dirs and files that are not readable but throw IOException for directories that are not writable. This does not clean
-        * out the original contents of toDir. (subdirectories are not renamed per directory rules)
-        *
-        * @param fromSuffix select files with this suffix - select all if null or empty
-        * @param toSuffix replace fromSuffix with toSuffix in the destination file name - ignored if null or empty, appended to name if
-        *        fromSuffix is null or empty
-        * @return the total number of files copied
-        */
-       public static int copyDir(File fromDir, File toDir, final String fromSuffix, String toSuffix) throws IOException {
-               return copyDir(fromDir, toDir, fromSuffix, toSuffix, (FileFilter) null);
-       }
-
-       // /**
-       // * Recursively copy files in fromDir (with any fromSuffix) to toDir,
-       // * replacing fromSuffix with toSuffix if any, and adding the destination
-       // * file to any collector. This silently ignores dirs and files that are
-       // not
-       // * readable but throw IOException for directories that are not writable.
-       // * This does not clean out the original contents of toDir. (subdirectories
-       // * are not renamed per directory rules) This calls any delegate
-       // * FilenameFilter to collect any selected file.
-       // *
-       // * @param fromSuffix select files with this suffix - select all if null or
-       // * empty
-       // * @param toSuffix replace fromSuffix with toSuffix in the destination
-       // file
-       // * name - ignored if null or empty, appended to name if
-       // * fromSuffix is null or empty
-       // * @param collector the List sink for destination files - ignored if null
-       // * @return the total number of files copied
-       // */
-       // public static int copyDir(File fromDir, File toDir, final String
-       // fromSuffix, final String toSuffix, final List collector)
-       // throws IOException {
-       // // int before = collector.size();
-       // if (null == collector) {
-       // return copyDir(fromDir, toDir, fromSuffix, toSuffix);
-       // } else {
-       // FileFilter collect = new FileFilter() {
-       // public boolean accept(File pathname) {
-       // return collector.add(pathname);
-       // }
-       // };
-       // return copyDir(fromDir, toDir, fromSuffix, toSuffix, collect);
-       // }
-       // }
-
-       /**
-        * Recursively copy files in fromDir (with any fromSuffix) to toDir, replacing fromSuffix with toSuffix if any. This silently
-        * ignores dirs and files that are not readable but throw IOException for directories that are not writable. This does not clean
-        * out the original contents of toDir. (subdirectories are not renamed per directory rules) This calls any delegate
-        * FilenameFilter to collect any selected file.
-        *
-        * @param fromSuffix select files with this suffix - select all if null or empty
-        * @param toSuffix replace fromSuffix with toSuffix in the destination file name - ignored if null or empty, appended to name if
-        *        fromSuffix is null or empty
-        * @return the total number of files copied
-        */
-       public static int copyDir(File fromDir, File toDir, final String fromSuffix, final String toSuffix, final FileFilter delegate)
-                       throws IOException {
-
-               if ((null == fromDir) || (!fromDir.canRead())) {
-                       return 0;
-               }
-               final boolean haveSuffix = ((null != fromSuffix) && (0 < fromSuffix.length()));
-               final int slen = (!haveSuffix ? 0 : fromSuffix.length());
-
-               if (!toDir.exists()) {
-                       toDir.mkdirs();
-               }
-               final String[] fromFiles;
-               if (!haveSuffix) {
-                       fromFiles = fromDir.list();
-               } else {
-                       FilenameFilter filter = new FilenameFilter() {
-                               public boolean accept(File dir, String name) {
-                                       return (new File(dir, name).isDirectory() || (name.endsWith(fromSuffix)));
-                               }
-                       };
-                       fromFiles = fromDir.list(filter);
-               }
-               int result = 0;
-               final int MAX = (null == fromFiles ? 0 : fromFiles.length);
-               for (int i = 0; i < MAX; i++) {
-                       String filename = fromFiles[i];
-                       File fromFile = new File(fromDir, filename);
-                       if (fromFile.canRead()) {
-                               if (fromFile.isDirectory()) {
-                                       result += copyDir(fromFile, new File(toDir, filename), fromSuffix, toSuffix, delegate);
-                               } else if (fromFile.isFile()) {
-                                       if (haveSuffix) {
-                                               filename = filename.substring(0, filename.length() - slen);
-                                       }
-                                       if (null != toSuffix) {
-                                               filename = filename + toSuffix;
-                                       }
-                                       File targetFile = new File(toDir, filename);
-                                       if ((null == delegate) || delegate.accept(targetFile)) {
-                                               copyFile(fromFile, targetFile);
-                                       }
-                                       result++;
-                               }
-                       }
-               }
-               return result;
-       }
-
-       /**
-        * Recursively list files in srcDir.
-        *
-        * @return ArrayList with String paths of File under srcDir (relative to srcDir)
-        */
-       public static String[] listFiles(File srcDir) {
-               ArrayList<String> result = new ArrayList<String>();
-               if ((null != srcDir) && srcDir.canRead()) {
-                       listFiles(srcDir, null, result);
-               }
-               return result.toArray(new String[0]);
-       }
-
-       public static final FileFilter aspectjSourceFileFilter = new FileFilter() {
-               public boolean accept(File pathname) {
-                       String name = pathname.getName().toLowerCase();
-                       return name.endsWith(".java") || name.endsWith(".aj");
-               }
-       };
-
-       /**
-        * Recursively list files in srcDir.
-        *
-        * @return ArrayList with String paths of File under srcDir (relative to srcDir)
-        */
-       public static File[] listFiles(File srcDir, FileFilter fileFilter) {
-               ArrayList<File> result = new ArrayList<File>();
-               if ((null != srcDir) && srcDir.canRead()) {
-                       listFiles(srcDir, result, fileFilter);
-               }
-               return result.toArray(new File[result.size()]);
-       }
-
-       /**
-        * Recursively list .class files in specified directory
-        *
-        * @return List of File objects
-        */
-       public static List<File> listClassFiles(File dir) {
-               ArrayList<File> result = new ArrayList<File>();
-               if ((null != dir) && dir.canRead()) {
-                       listClassFiles(dir, result);
-               }
-               return result;
-       }
-
-       /**
-        * Convert String[] paths to File[] as offset of base directory
-        *
-        * @param basedir the non-null File base directory for File to create with paths
-        * @param paths the String[] of paths to create
-        * @return File[] with same length as paths
-        */
-       public static File[] getBaseDirFiles(File basedir, String[] paths) {
-               return getBaseDirFiles(basedir, paths, (String[]) null);
-       }
-
-       /**
-        * Convert String[] paths to File[] as offset of base directory
-        *
-        * @param basedir the non-null File base directory for File to create with paths
-        * @param paths the String[] of paths to create
-        * @param suffixes the String[] of suffixes to limit sources to - ignored if null
-        * @return File[] with same length as paths
-        */
-       public static File[] getBaseDirFiles(File basedir, String[] paths, String[] suffixes) {
-               LangUtil.throwIaxIfNull(basedir, "basedir");
-               LangUtil.throwIaxIfNull(paths, "paths");
-               File[] result = null;
-               if (!LangUtil.isEmpty(suffixes)) {
-                       ArrayList<File> list = new ArrayList<File>();
-                       for (int i = 0; i < paths.length; i++) {
-                               String path = paths[i];
-                               for (int j = 0; j < suffixes.length; j++) {
-                                       if (path.endsWith(suffixes[j])) {
-                                               list.add(new File(basedir, paths[i]));
-                                               break;
-                                       }
-                               }
-                       }
-                       result = list.toArray(new File[0]);
-               } else {
-                       result = new File[paths.length];
-                       for (int i = 0; i < result.length; i++) {
-                               result[i] = newFile(basedir, paths[i]);
-                       }
-               }
-               return result;
-       }
-
-       /**
-        * Create a new File, resolving paths ".." and "." specially.
-        *
-        * @param dir the File for the parent directory of the file
-        * @param path the path in the parent directory (filename only?)
-        * @return File for the new file.
-        */
-       private static File newFile(File dir, String path) {
-               if (".".equals(path)) {
-                       return dir;
-               } else if ("..".equals(path)) {
-                       File parentDir = dir.getParentFile();
-                       if (null != parentDir) {
-                               return parentDir;
-                       } else {
-                               return new File(dir, "..");
-                       }
-               } else {
-                       return new File(dir, path);
-               }
-       }
-
-       /**
-        * Copy files from source dir into destination directory, creating any needed directories. This differs from copyDir in not
-        * being recursive; each input with the source dir creates a full path. However, if the source is a directory, it is copied as
-        * such.
-        *
-        * @param srcDir an existing, readable directory containing relativePaths files
-        * @param relativePaths a set of paths relative to srcDir to readable File to copy
-        * @param destDir an existing, writable directory to copy files to
-        * @throws IllegalArgumentException if input invalid, IOException if operations fail
-        */
-       public static File[] copyFiles(File srcDir, String[] relativePaths, File destDir) throws IllegalArgumentException, IOException {
-               final String[] paths = relativePaths;
-               throwIaxUnlessCanReadDir(srcDir, "srcDir");
-               throwIaxUnlessCanWriteDir(destDir, "destDir");
-               LangUtil.throwIaxIfNull(paths, "relativePaths");
-               File[] result = new File[paths.length];
-               for (int i = 0; i < paths.length; i++) {
-                       String path = paths[i];
-                       LangUtil.throwIaxIfNull(path, "relativePaths-entry");
-                       File src = newFile(srcDir, paths[i]);
-                       File dest = newFile(destDir, path);
-                       File destParent = dest.getParentFile();
-                       if (!destParent.exists()) {
-                               destParent.mkdirs();
-                       }
-                       LangUtil.throwIaxIfFalse(canWriteDir(destParent), "dest-entry-parent");
-                       copyFile(src, dest); // both file-dir and dir-dir copies
-                       result[i] = dest;
-               }
-               return result;
-       }
-
-       /**
-        * Copy fromFile to toFile, handling file-file, dir-dir, and file-dir copies.
-        *
-        * @param fromFile the File path of the file or directory to copy - must be readable
-        * @param toFile the File path of the target file or directory - must be writable (will be created if it does not exist)
-        */
-       public static void copyFile(File fromFile, File toFile) throws IOException {
-               LangUtil.throwIaxIfNull(fromFile, "fromFile");
-               LangUtil.throwIaxIfNull(toFile, "toFile");
-               LangUtil.throwIaxIfFalse(!toFile.equals(fromFile), "same file");
-               if (toFile.isDirectory()) { // existing directory
-                       throwIaxUnlessCanWriteDir(toFile, "toFile");
-                       if (fromFile.isFile()) { // file-dir
-                               File targFile = new File(toFile, fromFile.getName());
-                               copyValidFiles(fromFile, targFile);
-                       } else if (fromFile.isDirectory()) { // dir-dir
-                               copyDir(fromFile, toFile);
-                       } else {
-                               LangUtil.throwIaxIfFalse(false, "not dir or file: " + fromFile);
-                       }
-               } else if (toFile.isFile()) { // target file exists
-                       if (fromFile.isDirectory()) {
-                               LangUtil.throwIaxIfFalse(false, "can't copy to file dir: " + fromFile);
-                       }
-                       copyValidFiles(fromFile, toFile); // file-file
-               } else { // target file is a non-existent path -- could be file or dir
-                       /* File toFileParent = */ensureParentWritable(toFile);
-                       if (fromFile.isFile()) {
-                               copyValidFiles(fromFile, toFile);
-                       } else if (fromFile.isDirectory()) {
-                               toFile.mkdirs();
-                               throwIaxUnlessCanWriteDir(toFile, "toFile");
-                               copyDir(fromFile, toFile);
-                       } else {
-                               LangUtil.throwIaxIfFalse(false, "not dir or file: " + fromFile);
-                       }
-               }
-       }
-
-       /**
-        * Ensure that the parent directory to path can be written. If the path has a null parent, DEFAULT_PARENT is tested. If the path
-        * parent does not exist, this tries to create it.
-        *
-        * @param path the File path whose parent should be writable
-        * @return the File path of the writable parent directory
-        * @throws IllegalArgumentException if parent cannot be written or path is null.
-        */
-       public static File ensureParentWritable(File path) {
-               LangUtil.throwIaxIfNull(path, "path");
-               File pathParent = path.getParentFile();
-               if (null == pathParent) {
-                       pathParent = DEFAULT_PARENT;
-               }
-               if (!pathParent.canWrite()) {
-                       pathParent.mkdirs();
-               }
-               throwIaxUnlessCanWriteDir(pathParent, "pathParent");
-               return pathParent;
-       }
-
-       /**
-        * Copy file to file.
-        *
-        * @param fromFile the File to copy (readable, non-null file)
-        * @param toFile the File to copy to (non-null, parent dir exists)
-        * @throws IOException
-        */
-       public static void copyValidFiles(File fromFile, File toFile) throws IOException {
-               FileInputStream in = null;
-               FileOutputStream out = null;
-               try {
-                       in = new FileInputStream(fromFile);
-                       out = new FileOutputStream(toFile);
-                       copyStream(in, out);
-               } finally {
-                       if (out != null) {
-                               out.close();
-                       }
-                       if (in != null) {
-                               in.close();
-                       }
-               }
-       }
-
-       /** do line-based copying */
-       @SuppressWarnings("deprecation")
-       public static void copyStream(DataInputStream in, PrintStream out) throws IOException {
-               LangUtil.throwIaxIfNull(in, "in");
-               LangUtil.throwIaxIfNull(in, "out");
-               String s;
-               while (null != (s = in.readLine())) {
-                       out.println(s);
-               }
-       }
-
-       public static void copyStream(InputStream in, OutputStream out) throws IOException {
-               final int MAX = 4096;
-               byte[] buf = new byte[MAX];
-               for (int bytesRead = in.read(buf, 0, MAX); bytesRead != -1; bytesRead = in.read(buf, 0, MAX)) {
-                       out.write(buf, 0, bytesRead);
-               }
-       }
-
-       public static void copyStream(Reader in, Writer out) throws IOException {
-               final int MAX = 4096;
-               char[] buf = new char[MAX];
-               for (int bytesRead = in.read(buf, 0, MAX); bytesRead != -1; bytesRead = in.read(buf, 0, MAX)) {
-                       out.write(buf, 0, bytesRead);
-               }
-       }
-
-       /**
-        * Make a new child directory of parent
-        *
-        * @param parent a File for the parent (writable)
-        * @param child a prefix for the child directory
-        * @return a File dir that exists with parentDir as the parent file or null
-        */
-       public static File makeNewChildDir(File parent, String child) {
-               if (null == parent || !parent.canWrite() || !parent.isDirectory()) {
-                       throw new IllegalArgumentException("bad parent: " + parent);
-               } else if (null == child) {
-                       child = "makeNewChildDir";
-               } else if (!isValidFileName(child)) {
-                       throw new IllegalArgumentException("bad child: " + child);
-               }
-               File result = new File(parent, child);
-               int safety = 1000;
-               for (String suffix = FileUtil.randomFileString(); ((0 < --safety) && result.exists()); suffix = FileUtil.randomFileString()) {
-                       result = new File(parent, child + suffix);
-               }
-               if (result.exists()) {
-                       System.err.println("exhausted files for child dir in " + parent);
-                       return null;
-               }
-               return ((result.mkdirs() && result.exists()) ? result : null);
-       }
-
-       /**
-        * Make a new temporary directory in the same directory that the system uses for temporary files, or if that files, in the
-        * current directory.
-        *
-        * @param name the preferred (simple) name of the directory - may be null.
-        * @return File of an existing new temp dir, or null if unable to create
-        */
-       public static File getTempDir(String name) {
-               if (null == name) {
-                       name = "FileUtil_getTempDir";
-               } else if (!isValidFileName(name)) {
-                       throw new IllegalArgumentException(" invalid: " + name);
-               }
-               File result = null;
-               File tempFile = null;
-               try {
-                       tempFile = File.createTempFile("ignoreMe", ".txt");
-                       File tempParent = tempFile.getParentFile();
-                       result = makeNewChildDir(tempParent, name);
-               } catch (IOException t) {
-                       result = makeNewChildDir(new File("."), name);
-               } finally {
-                       if (null != tempFile) {
-                               tempFile.delete();
-                       }
-               }
-               return result;
-       }
-
-       public static URL[] getFileURLs(File[] files) {
-               if ((null == files) || (0 == files.length)) {
-                       return new URL[0];
-               }
-               URL[] result = new URL[files.length]; // XXX dangerous non-copy...
-               for (int i = 0; i < result.length; i++) {
-                       result[i] = getFileURL(files[i]);
-               }
-               return result;
-       }
-
-       /**
-        * Get URL for a File. This appends "/" for directories. prints errors to System.err
-        *
-        * @param file the File to convert to URL (not null)
-        */
-       @SuppressWarnings("deprecation")
-       public static URL getFileURL(File file) {
-               LangUtil.throwIaxIfNull(file, "file");
-               URL result = null;
-               try {
-                       result = file.toURL();// TODO AV - was toURI.toURL that does not
-                       // works on Java 1.3
-                       if (null != result) {
-                               return result;
-                       }
-                       String url = "file:" + file.getAbsolutePath().replace('\\', '/');
-                       result = new URL(url + (file.isDirectory() ? "/" : ""));
-               } catch (MalformedURLException e) {
-                       String m = "Util.makeURL(\"" + file.getPath() + "\" MUE " + e.getMessage();
-                       System.err.println(m);
-               }
-               return result;
-       }
-
-       /**
-        * Write contents to file, returning null on success or error message otherwise. This tries to make any necessary parent
-        * directories first.
-        *
-        * @param file the File to write (not null)
-        * @param contents the String to write (use "" if null)
-        * @return String null on no error, error otherwise
-        */
-       public static String writeAsString(File file, String contents) {
-               LangUtil.throwIaxIfNull(file, "file");
-               if (null == contents) {
-                       contents = "";
-               }
-               Writer out = null;
-               try {
-                       File parentDir = file.getParentFile();
-                       if (!parentDir.exists() && !parentDir.mkdirs()) {
-                               return "unable to make parent dir for " + file;
-                       }
-                       Reader in = new StringReader(contents);
-                       out = new FileWriter(file);
-                       FileUtil.copyStream(in, out);
-                       return null;
-               } catch (IOException e) {
-                       return LangUtil.unqualifiedClassName(e) + " writing " + file + ": " + e.getMessage();
-               } finally {
-                       if (null != out) {
-                               try {
-                                       out.close();
-                               } catch (IOException e) {
-                               } // ignored
-                       }
-               }
-       }
-
-       /**
-        * Reads a boolean array with our encoding
-        */
-       public static boolean[] readBooleanArray(DataInputStream s) throws IOException {
-               int len = s.readInt();
-               boolean[] ret = new boolean[len];
-               for (int i = 0; i < len; i++) {
-                       ret[i] = s.readBoolean();
-               }
-               return ret;
-       }
-
-       /**
-        * Writes a boolean array with our encoding
-        */
-       public static void writeBooleanArray(boolean[] a, DataOutputStream s) throws IOException {
-               int len = a.length;
-               s.writeInt(len);
-               for (int i = 0; i < len; i++) {
-                       s.writeBoolean(a[i]);
-               }
-       }
-
-       /**
-        * Reads an int array with our encoding
-        */
-       public static int[] readIntArray(DataInputStream s) throws IOException {
-               int len = s.readInt();
-               int[] ret = new int[len];
-               for (int i = 0; i < len; i++) {
-                       ret[i] = s.readInt();
-               }
-               return ret;
-       }
-
-       /**
-        * Writes an int array with our encoding
-        */
-       public static void writeIntArray(int[] a, DataOutputStream s) throws IOException {
-               int len = a.length;
-               s.writeInt(len);
-               for (int i = 0; i < len; i++) {
-                       s.writeInt(a[i]);
-               }
-       }
-
-       /**
-        * Reads an int array with our encoding
-        */
-       public static String[] readStringArray(DataInputStream s) throws IOException {
-               int len = s.readInt();
-               String[] ret = new String[len];
-               for (int i = 0; i < len; i++) {
-                       ret[i] = s.readUTF();
-               }
-               return ret;
-       }
-
-       /**
-        * Writes an int array with our encoding
-        */
-       public static void writeStringArray(String[] a, DataOutputStream s) throws IOException {
-               if (a == null) {
-                       s.writeInt(0);
-                       return;
-               }
-               int len = a.length;
-               s.writeInt(len);
-               for (int i = 0; i < len; i++) {
-                       s.writeUTF(a[i]);
-               }
-       }
-
-       /**
-        * Returns the contents of this file as a String
-        */
-       public static String readAsString(File file) throws IOException {
-               BufferedReader r = new BufferedReader(new FileReader(file));
-               StringBuffer b = new StringBuffer();
-               while (true) {
-                       int ch = r.read();
-                       if (ch == -1) {
-                               break;
-                       }
-                       b.append((char) ch);
-               }
-               r.close();
-               return b.toString();
-       }
-
-       // /**
-       // * Returns the contents of this stream as a String
-       // */
-       // public static String readAsString(InputStream in) throws IOException {
-       // BufferedReader r = new BufferedReader(new InputStreamReader(in));
-       // StringBuffer b = new StringBuffer();
-       // while (true) {
-       // int ch = r.read();
-       // if (ch == -1)
-       // break;
-       // b.append((char) ch);
-       // }
-       // in.close();
-       // r.close();
-       // return b.toString();
-       // }
-
-       /**
-        * Returns the contents of this file as a byte[]
-        */
-       public static byte[] readAsByteArray(File file) throws IOException {
-               FileInputStream in = new FileInputStream(file);
-               byte[] ret = FileUtil.readAsByteArray(in);
-               in.close();
-               return ret;
-       }
-
-       /**
-        * Reads this input stream and returns contents as a byte[]
-        */
-       public static byte[] readAsByteArray(InputStream inStream) throws IOException {
-               int size = 1024;
-               byte[] ba = new byte[size];
-               int readSoFar = 0;
-
-               while (true) {
-                       int nRead = inStream.read(ba, readSoFar, size - readSoFar);
-                       if (nRead == -1) {
-                               break;
-                       }
-                       readSoFar += nRead;
-                       if (readSoFar == size) {
-                               int newSize = size * 2;
-                               byte[] newBa = new byte[newSize];
-                               System.arraycopy(ba, 0, newBa, 0, size);
-                               ba = newBa;
-                               size = newSize;
-                       }
-               }
-
-               byte[] newBa = new byte[readSoFar];
-               System.arraycopy(ba, 0, newBa, 0, readSoFar);
-               return newBa;
-       }
-
-       final static String FILECHARS = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-
-       /** @return semi-random String of length 6 usable as filename suffix */
-       static String randomFileString() {
-               final double FILECHARS_length = FILECHARS.length();
-               final int LEN = 6;
-               final char[] result = new char[LEN];
-               int index = (int) (Math.random() * 6d);
-               for (int i = 0; i < LEN; i++) {
-                       if (index >= LEN) {
-                               index = 0;
-                       }
-                       result[index++] = FILECHARS.charAt((int) (Math.random() * FILECHARS_length));
-               }
-               return new String(result);
-       }
-
-       public static InputStream getStreamFromZip(String zipFile, String name) {
-               try {
-                       ZipFile zf = new ZipFile(zipFile);
-                       try {
-                               ZipEntry entry = zf.getEntry(name);
-                               return zf.getInputStream(entry);
-                       } finally {
-                               // ??? is it safe not to close this zf.close();
-                       }
-               } catch (IOException ioe) {
-                       return null;
-               }
-       }
-
-       //
-       // public static void extractJar(String zipFile, String outDir) throws
-       // IOException {
-       // ZipInputStream zs = new ZipInputStream(new FileInputStream(zipFile));
-       // ZipEntry entry;
-       // while ((entry = zs.getNextEntry()) != null) {
-       // if (entry.isDirectory())
-       // continue;
-       // byte[] in = readAsByteArray(zs);
-       //
-       // File outFile = new File(outDir + "/" + entry.getName());
-       // // if (!outFile.getParentFile().exists())
-       // // System.err.println("parent: " + outFile.getParentFile());
-       // // System.err.println("parent: " + outFile.getParentFile());
-       // outFile.getParentFile().mkdirs();
-       // FileOutputStream os = new FileOutputStream(outFile);
-       // os.write(in);
-       // os.close();
-       // zs.closeEntry();
-       // }
-       // zs.close();
-       // }
-
-       /**
-        * Do line-based search for literal text in source files, returning file:line where found.
-        *
-        * @param sought the String text to seek in the file
-        * @param sources the List of String paths to the source files
-        * @param listAll if false, only list first match in file
-        * @param errorSink the PrintStream to print any errors to (one per line) (use null to silently ignore errors)
-        * @return List of String of the form file:line for each found entry (never null, might be empty)
-        */
-       // OPTIMIZE only used by tests? move it out
-       public static List<String> lineSeek(String sought, List<String> sources, boolean listAll, PrintStream errorSink) {
-               if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sources)) {
-                       return Collections.emptyList();
-               }
-               ArrayList<String> result = new ArrayList<String>();
-               for (Iterator<String> iter = sources.iterator(); iter.hasNext();) {
-                       String path = iter.next();
-                       String error = lineSeek(sought, path, listAll, result);
-                       if ((null != error) && (null != errorSink)) {
-                               errorSink.println(error);
-                       }
-               }
-               return result;
-       }
-
-       /**
-        * Do line-based search for literal text in source file, returning line where found as a String in the form
-        * {sourcePath}:line:column submitted to the collecting parameter sink. Any error is rendered to String and returned as the
-        * result.
-        *
-        * @param sought the String text to seek in the file
-        * @param sources the List of String paths to the source files
-        * @param listAll if false, only list first match in file
-        * @param List sink the List for String entries of the form {sourcePath}:line:column
-        * @return String error if any, or add String entries to sink
-        */
-       public static String lineSeek(String sought, String sourcePath, boolean listAll, ArrayList<String> sink) {
-               if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sourcePath)) {
-                       return "nothing sought";
-               }
-               if (LangUtil.isEmpty(sourcePath)) {
-                       return "no sourcePath";
-               }
-               final File file = new File(sourcePath);
-               if (!file.canRead() || !file.isFile()) {
-                       return "sourcePath not a readable file";
-               }
-               int lineNum = 0;
-               FileReader fin = null;
-               try {
-                       fin = new FileReader(file);
-                       BufferedReader reader = new BufferedReader(fin);
-                       String line;
-                       while (null != (line = reader.readLine())) {
-                               lineNum++;
-                               int loc = line.indexOf(sought);
-                               if (-1 != loc) {
-                                       sink.add(sourcePath + ":" + lineNum + ":" + loc);
-                                       if (!listAll) {
-                                               break;
-                                       }
-                               }
-                       }
-               } catch (IOException e) {
-                       return LangUtil.unqualifiedClassName(e) + " reading " + sourcePath + ":" + lineNum;
-               } finally {
-                       try {
-                               if (null != fin) {
-                                       fin.close();
-                               }
-                       } catch (IOException e) {
-                       } // ignore
-               }
-               return null;
-       }
-
-       public static BufferedOutputStream makeOutputStream(File file) throws FileNotFoundException {
-               File parent = file.getParentFile();
-               if (parent != null) {
-                       parent.mkdirs();
-               }
-               return new BufferedOutputStream(new FileOutputStream(file));
-       }
-
-       /**
-        * Sleep until after the last last-modified stamp from the files.
-        *
-        * @param files the File[] of files to inspect for last modified times (this ignores null or empty files array and null or
-        *        non-existing components of files array)
-        * @return true if succeeded without 100 interrupts
-        */
-       public static boolean sleepPastFinalModifiedTime(File[] files) {
-               if ((null == files) || (0 == files.length)) {
-                       return true;
-               }
-               long delayUntil = System.currentTimeMillis();
-               for (int i = 0; i < files.length; i++) {
-                       File file = files[i];
-                       if ((null == file) || !file.exists()) {
-                               continue;
-                       }
-                       long nextModTime = file.lastModified();
-                       if (nextModTime > delayUntil) {
-                               delayUntil = nextModTime;
-                       }
-               }
-               return LangUtil.sleepUntil(++delayUntil);
-       }
-
-       private static void listClassFiles(final File baseDir, ArrayList<File> result) {
-               File[] files = baseDir.listFiles();
-               for (int i = 0; i < files.length; i++) {
-                       File f = files[i];
-                       if (f.isDirectory()) {
-                               listClassFiles(f, result);
-                       } else {
-                               if (f.getName().endsWith(".class")) {
-                                       result.add(f);
-                               }
-                       }
-               }
-       }
-
-       private static void listFiles(final File baseDir, ArrayList<File> result, FileFilter filter) {
-               File[] files = baseDir.listFiles();
-               // hack https://bugs.eclipse.org/bugs/show_bug.cgi?id=48650
-               final boolean skipCVS = (!PERMIT_CVS && (filter == aspectjSourceFileFilter));
-               for (int i = 0; i < files.length; i++) {
-                       File f = files[i];
-                       if (f.isDirectory()) {
-                               if (skipCVS) {
-                                       String name = f.getName().toLowerCase();
-                                       if ("cvs".equals(name) || "sccs".equals(name)) {
-                                               continue;
-                                       }
-                               }
-                               listFiles(f, result, filter);
-                       } else {
-                               if (filter.accept(f)) {
-                                       result.add(f);
-                               }
-                       }
-               }
-       }
-
-       /** @return true if input is not null and contains no path separator */
-       private static boolean isValidFileName(String input) {
-               return ((null != input) && (-1 == input.indexOf(File.pathSeparator)));
-       }
-
-       private static void listFiles(final File baseDir, String dir, ArrayList<String> result) {
-               final String dirPrefix = (null == dir ? "" : dir + "/");
-               final File dirFile = (null == dir ? baseDir : new File(baseDir.getPath() + "/" + dir));
-               final String[] files = dirFile.list();
-               for (int i = 0; i < files.length; i++) {
-                       File f = new File(dirFile, files[i]);
-                       String path = dirPrefix + files[i];
-                       if (f.isDirectory()) {
-                               listFiles(baseDir, path, result);
-                       } else {
-                               result.add(path);
-                       }
-               }
-       }
-
-       private FileUtil() {
-       }
-
-       public static List<String> makeClasspath(URL[] urls) {
-               List<String> ret = new LinkedList<String>();
-               if (urls != null) {
-                       for (int i = 0; i < urls.length; i++) {
-                               ret.add(toPathString(urls[i]));
-                       }
-               }
-               return ret;
-       }
-
-       private static String toPathString(URL url) {
-               try {
-                       return url.toURI().getPath();
-               } catch (URISyntaxException e) {
-                       System.err.println("Warning!! Malformed URL may cause problems: "+url); // TODO: Better way to report this?
-                       // In this case it was likely not using properly escaped
-                       // characters so we just use the 'bad' method that doesn't decode
-                       // special chars
-                       return url.getPath();
-               }
-       }
-
-       /**
-        * A pipe when run reads from an input stream to an output stream, optionally sleeping between reads.
-        *
-        * @see #copyStream(InputStream, OutputStream)
-        */
-       public static class Pipe implements Runnable {
-               private final InputStream in;
-               private final OutputStream out;
-               private final long sleep;
-               private ByteArrayOutputStream snoop;
-               private long totalWritten;
-               private Throwable thrown;
-               private boolean halt;
-               /**
-                * Seem to be unable to detect erroroneous closing of System.out...
-                */
-               private final boolean closeInput;
-               private final boolean closeOutput;
-
-               /**
-                * If true, then continue processing stream until no characters are returned when halting.
-                */
-               private boolean finishStream;
-
-               private boolean done; // true after completing() completes
-
-               /**
-                * alias for <code>Pipe(in, out, 100l, false, false)</code>
-                *
-                * @param in the InputStream source to read
-                * @param out the OutputStream sink to write
-                */
-               Pipe(InputStream in, OutputStream out) {
-                       this(in, out, 100l, false, false);
-               }
-
-               /**
-                * @param in the InputStream source to read
-                * @param out the OutputStream sink to write
-                * @param tryClosingStreams if true, then try closing both streams when done
-                * @param sleep milliseconds to delay between reads (pinned to 0..1 minute)
-                */
-               Pipe(InputStream in, OutputStream out, long sleep, boolean closeInput, boolean closeOutput) {
-                       LangUtil.throwIaxIfNull(in, "in");
-                       LangUtil.throwIaxIfNull(out, "out");
-                       this.in = in;
-                       this.out = out;
-                       this.closeInput = closeInput;
-                       this.closeOutput = closeOutput;
-                       this.sleep = Math.min(0l, Math.max(60l * 1000l, sleep));
-               }
-
-               public void setSnoop(ByteArrayOutputStream snoop) {
-                       this.snoop = snoop;
-               }
-
-               /**
-                * Run the pipe. This halts on the first Throwable thrown or when a read returns -1 (for end-of-file) or on demand.
-                */
-               public void run() {
-                       totalWritten = 0;
-                       if (halt) {
-                               return;
-                       }
-                       try {
-                               final int MAX = 4096;
-                               byte[] buf = new byte[MAX];
-                               // TODO this blocks, hanging the harness
-                               int count = in.read(buf, 0, MAX);
-                               ByteArrayOutputStream mySnoop;
-                               while ((halt && finishStream && (0 < count)) || (!halt && (-1 != count))) {
-                                       out.write(buf, 0, count);
-                                       mySnoop = snoop;
-                                       if (null != mySnoop) {
-                                               mySnoop.write(buf, 0, count);
-                                       }
-                                       totalWritten += count;
-                                       if (halt && !finishStream) {
-                                               break;
-                                       }
-                                       if (!halt && (0 < sleep)) {
-                                               Thread.sleep(sleep);
-                                       }
-                                       if (halt && !finishStream) {
-                                               break;
-                                       }
-                                       count = in.read(buf, 0, MAX);
-                               }
-                       } catch (Throwable e) {
-                               thrown = e;
-                       } finally {
-                               halt = true;
-                               if (closeInput) {
-                                       try {
-                                               in.close();
-                                       } catch (IOException e) {
-                                               // ignore
-                                       }
-                               }
-                               if (closeOutput) {
-                                       try {
-                                               out.close();
-                                       } catch (IOException e) {
-                                               // ignore
-                                       }
-                               }
-                               done = true;
-                               completing(totalWritten, thrown);
-                       }
-
-               }
-
-               /**
-                * Tell the pipe to halt the next time it gains control.
-                *
-                * @param wait if true, this waits synchronously until pipe is done
-                * @param finishStream if true, then continue until a read from the input stream returns no bytes, then halt.
-                * @return true if <code>run()</code> will return the next time it gains control
-                */
-               public boolean halt(boolean wait, boolean finishStream) {
-                       if (!halt) {
-                               halt = true;
-                       }
-                       if (wait) {
-                               while (!done) {
-                                       synchronized (this) {
-                                               notifyAll();
-                                       }
-                                       if (!done) {
-                                               try {
-                                                       Thread.sleep(5l);
-                                               } catch (InterruptedException e) {
-                                                       break;
-                                               }
-                                       }
-                               }
-                       }
-                       return halt;
-               }
-
-               /** @return the total number of bytes written */
-               public long totalWritten() {
-                       return totalWritten;
-               }
-
-               /** @return any exception thrown when reading/writing */
-               public Throwable getThrown() {
-                       return thrown;
-               }
-
-               /**
-                * This is called when the pipe is completing. This implementation does nothing. Subclasses implement this to get notice.
-                * Note that halt(true, true) might or might not have completed before this method is called.
-                */
-               protected void completing(long totalWritten, Throwable thrown) {
-               }
-       }
-
-}
diff --git a/util/src/org/aspectj/util/FuzzyBoolean.java b/util/src/org/aspectj/util/FuzzyBoolean.java
deleted file mode 100644 (file)
index 807d980..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 1999-2001 Xerox Corporation, 
- *               2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *     Xerox/PARC     initial implementation 
- * ******************************************************************/
-package org.aspectj.util;
-
-/** 
- * This class implements boolean that include a "maybe"
- */
-public abstract class FuzzyBoolean {
-    public abstract boolean alwaysTrue();
-    public abstract boolean alwaysFalse();
-    public abstract boolean maybeTrue();
-    public abstract boolean maybeFalse();
-    
-    public abstract FuzzyBoolean and(FuzzyBoolean other);
-    public abstract FuzzyBoolean or(FuzzyBoolean other);
-    public abstract FuzzyBoolean not();
-    
-    private static class YesFuzzyBoolean extends FuzzyBoolean {
-        public boolean alwaysFalse() {
-            return false;
-        }
-
-        public boolean alwaysTrue() {
-            return true;
-        }
-
-        public boolean maybeFalse() {
-            return false;
-        }
-
-        public boolean maybeTrue() {
-            return true;
-        }
-        
-        public FuzzyBoolean and(FuzzyBoolean other) {
-            return other;
-        }
-
-        public FuzzyBoolean not() {
-            return FuzzyBoolean.NO;
-        }
-
-        public FuzzyBoolean or(FuzzyBoolean other) {
-            return this;
-        }
-
-        public String toString() {
-            return "YES";
-        }
-    }    
-    private static class NoFuzzyBoolean extends FuzzyBoolean {
-        public boolean alwaysFalse() {
-            return true;
-        }
-
-        public boolean alwaysTrue() {
-            return false;
-        }
-
-
-        public boolean maybeFalse() {
-            return true;
-        }
-
-        public boolean maybeTrue() {
-            return false;
-        }
-        
-        public FuzzyBoolean and(FuzzyBoolean other) {
-            return this;
-        }
-
-        public FuzzyBoolean not() {
-            return FuzzyBoolean.YES;
-        }
-
-        public FuzzyBoolean or(FuzzyBoolean other) {
-            return other;
-        }
-
-        public String toString() {
-            return "NO";
-        }
-    }
-    private static class NeverFuzzyBoolean extends FuzzyBoolean {
-        public boolean alwaysFalse() {
-            return true;
-        }
-
-        public boolean alwaysTrue() {
-            return false;
-        }
-
-
-        public boolean maybeFalse() {
-            return true;
-        }
-
-        public boolean maybeTrue() {
-            return false;
-        }
-        
-        public FuzzyBoolean and(FuzzyBoolean other) {
-            return this;
-        }
-
-        public FuzzyBoolean not() {
-            return this;
-        }
-
-        public FuzzyBoolean or(FuzzyBoolean other) {
-            return this;
-        }
-
-        public String toString() {
-            return "NEVER";
-        }
-    }
-    
-    private static class MaybeFuzzyBoolean extends FuzzyBoolean {
-        public boolean alwaysFalse() {
-            return false;
-        }
-
-        public boolean alwaysTrue() {
-            return false;
-        }
-
-
-        public boolean maybeFalse() {
-            return true;
-        }
-
-        public boolean maybeTrue() {
-            return true;
-        }
-        
-        public FuzzyBoolean and(FuzzyBoolean other) {
-            return other.alwaysFalse() ? other : this;
-        }
-
-        public FuzzyBoolean not() {
-            return this;
-        }
-
-        public FuzzyBoolean or(FuzzyBoolean other) {
-            return other.alwaysTrue() ? other : this;
-        }
-
-        public String toString() {
-            return "MAYBE";
-        }
-    }
-    
-    public static final FuzzyBoolean YES   = new YesFuzzyBoolean();
-    public static final FuzzyBoolean NO    = new NoFuzzyBoolean();
-    public static final FuzzyBoolean MAYBE = new MaybeFuzzyBoolean();
-    public static final FuzzyBoolean NEVER = new NeverFuzzyBoolean();
-
-       public static final FuzzyBoolean fromBoolean(boolean b) {
-               return b ? YES : NO;
-       }
-
-}
diff --git a/util/src/org/aspectj/util/GenericSignature.java b/util/src/org/aspectj/util/GenericSignature.java
deleted file mode 100644 (file)
index bfefc00..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 1999-2001 Xerox Corporation, 
- *               2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *  
- * ******************************************************************/
-package org.aspectj.util;
-
-/**
- * Encapsulate generic signature parsing
- * 
- * @author Adrian Colyer
- * @author Andy Clement
- */
-public class GenericSignature {
-
-       /**
-        * structure holding a parsed class signature
-        */
-       public static class ClassSignature {
-               public FormalTypeParameter[] formalTypeParameters = FormalTypeParameter.NONE;
-               public ClassTypeSignature superclassSignature;
-               public ClassTypeSignature[] superInterfaceSignatures = ClassTypeSignature.NONE;
-
-               public String toString() {
-                       StringBuffer ret = new StringBuffer();
-                       ret.append(formalTypeParameters.toString());
-                       ret.append(superclassSignature.toString());
-                       for (int i = 0; i < superInterfaceSignatures.length; i++) {
-                               ret.append(superInterfaceSignatures[i].toString());
-                       }
-                       return ret.toString();
-               }
-       }
-
-       public static class MethodTypeSignature {
-               public FormalTypeParameter[] formalTypeParameters = new FormalTypeParameter[0];
-               public TypeSignature[] parameters = new TypeSignature[0];
-               public TypeSignature returnType;
-               public FieldTypeSignature[] throwsSignatures = new FieldTypeSignature[0];
-
-               public MethodTypeSignature(FormalTypeParameter[] aFormalParameterList, TypeSignature[] aParameterList,
-                               TypeSignature aReturnType, FieldTypeSignature[] aThrowsSignatureList) {
-                       this.formalTypeParameters = aFormalParameterList;
-                       this.parameters = aParameterList;
-                       this.returnType = aReturnType;
-                       this.throwsSignatures = aThrowsSignatureList;
-               }
-
-               public String toString() {
-                       StringBuffer sb = new StringBuffer();
-                       if (formalTypeParameters.length > 0) {
-                               sb.append("<");
-                               for (int i = 0; i < formalTypeParameters.length; i++) {
-                                       sb.append(formalTypeParameters[i].toString());
-                               }
-                               sb.append(">");
-                       }
-                       sb.append("(");
-                       for (int i = 0; i < parameters.length; i++) {
-                               sb.append(parameters[i].toString());
-                       }
-                       sb.append(")");
-                       sb.append(returnType.toString());
-                       for (int i = 0; i < throwsSignatures.length; i++) {
-                               sb.append("^");
-                               sb.append(throwsSignatures[i].toString());
-                       }
-                       return sb.toString();
-               }
-       }
-
-       /**
-        * structure capturing a FormalTypeParameter from the Signature grammar
-        */
-       public static class FormalTypeParameter {
-               public static final FormalTypeParameter[] NONE = new FormalTypeParameter[0];
-               public String identifier;
-               public FieldTypeSignature classBound;
-               public FieldTypeSignature[] interfaceBounds;
-
-               public String toString() {
-                       StringBuffer ret = new StringBuffer();
-                       ret.append("T");
-                       ret.append(identifier);
-                       ret.append(":");
-                       ret.append(classBound.toString());
-                       for (int i = 0; i < interfaceBounds.length; i++) {
-                               ret.append(":");
-                               ret.append(interfaceBounds[i].toString());
-                       }
-                       return ret.toString();
-               }
-       }
-
-       public static abstract class TypeSignature {
-               public boolean isBaseType() {
-                       return false;
-               }
-       }
-
-       public static class BaseTypeSignature extends TypeSignature {
-               private final String sig;
-
-               public BaseTypeSignature(String aPrimitiveType) {
-                       sig = aPrimitiveType;
-               }
-
-               public boolean isBaseType() {
-                       return true;
-               }
-
-               public String toString() {
-                       return sig;
-               }
-       }
-
-       public static abstract class FieldTypeSignature extends TypeSignature {
-               public boolean isClassTypeSignature() {
-                       return false;
-               }
-
-               public boolean isTypeVariableSignature() {
-                       return false;
-               }
-
-               public boolean isArrayTypeSignature() {
-                       return false;
-               }
-       }
-
-       public static class ClassTypeSignature extends FieldTypeSignature {
-
-               public static final ClassTypeSignature[] NONE = new ClassTypeSignature[0];
-               public String classSignature;
-               public SimpleClassTypeSignature outerType;
-               public SimpleClassTypeSignature[] nestedTypes;
-
-               public ClassTypeSignature(String sig, String identifier) {
-                       this.classSignature = sig;
-                       this.outerType = new SimpleClassTypeSignature(identifier);
-                       this.nestedTypes = new SimpleClassTypeSignature[0];
-               }
-
-               public ClassTypeSignature(String sig, SimpleClassTypeSignature outer, SimpleClassTypeSignature[] inners) {
-                       this.classSignature = sig;
-                       this.outerType = outer;
-                       this.nestedTypes = inners;
-               }
-
-               public boolean isClassTypeSignature() {
-                       return true;
-               }
-
-               public String toString() {
-                       return classSignature;
-               }
-       }
-
-       public static class TypeVariableSignature extends FieldTypeSignature {
-               public String typeVariableName;
-
-               public TypeVariableSignature(String typeVarToken) {
-                       this.typeVariableName = typeVarToken.substring(1);
-               }
-
-               public boolean isTypeVariableSignature() {
-                       return true;
-               }
-
-               public String toString() {
-                       return "T" + typeVariableName + ";";
-               }
-       }
-
-       public static class ArrayTypeSignature extends FieldTypeSignature {
-               public TypeSignature typeSig;
-
-               public ArrayTypeSignature(TypeSignature aTypeSig) {
-                       this.typeSig = aTypeSig;
-               }
-
-               public boolean isArrayTypeSignature() {
-                       return true;
-               }
-
-               public String toString() {
-                       return "[" + typeSig.toString();
-               }
-       }
-
-       public static class SimpleClassTypeSignature {
-               public String identifier;
-               public TypeArgument[] typeArguments;
-
-               public SimpleClassTypeSignature(String identifier) {
-                       this.identifier = identifier;
-                       this.typeArguments = new TypeArgument[0];
-               }
-
-               public SimpleClassTypeSignature(String identifier, TypeArgument[] args) {
-                       this.identifier = identifier;
-                       this.typeArguments = args;
-               }
-
-               public String toString() {
-                       StringBuffer sb = new StringBuffer();
-                       sb.append(identifier);
-                       if (typeArguments.length > 0) {
-                               sb.append("<");
-                               for (int i = 0; i < typeArguments.length; i++) {
-                                       sb.append(typeArguments[i].toString());
-                               }
-                               sb.append(">");
-                       }
-                       return sb.toString();
-               }
-       }
-
-       public static class TypeArgument {
-               public boolean isWildcard = false;
-               public boolean isPlus = false;
-               public boolean isMinus = false;
-               public FieldTypeSignature signature; // null if isWildcard
-
-               public TypeArgument() {
-                       isWildcard = true;
-               }
-
-               public TypeArgument(boolean plus, boolean minus, FieldTypeSignature aSig) {
-                       this.isPlus = plus;
-                       this.isMinus = minus;
-                       this.signature = aSig;
-               }
-
-               public String toString() {
-                       if (isWildcard)
-                               return "*";
-                       StringBuffer sb = new StringBuffer();
-                       if (isPlus)
-                               sb.append("+");
-                       if (isMinus)
-                               sb.append("-");
-                       sb.append(signature.toString());
-                       return sb.toString();
-               }
-       }
-}
diff --git a/util/src/org/aspectj/util/GenericSignatureParser.java b/util/src/org/aspectj/util/GenericSignatureParser.java
deleted file mode 100644 (file)
index 28c8db8..0000000
+++ /dev/null
@@ -1,426 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 2005-2008 Contributors.
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://eclipse.org/legal/epl-v10.html 
- *  
- * ******************************************************************/
-package org.aspectj.util;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.aspectj.util.GenericSignature.ArrayTypeSignature;
-import org.aspectj.util.GenericSignature.BaseTypeSignature;
-import org.aspectj.util.GenericSignature.ClassTypeSignature;
-import org.aspectj.util.GenericSignature.FieldTypeSignature;
-import org.aspectj.util.GenericSignature.FormalTypeParameter;
-import org.aspectj.util.GenericSignature.MethodTypeSignature;
-import org.aspectj.util.GenericSignature.SimpleClassTypeSignature;
-import org.aspectj.util.GenericSignature.TypeArgument;
-import org.aspectj.util.GenericSignature.TypeSignature;
-import org.aspectj.util.GenericSignature.TypeVariableSignature;
-
-/**
- * Parses the generic signature attribute as defined in the JVM spec.
- * 
- * @author Adrian Colyer
- * @author Andy Clement
- */
-public class GenericSignatureParser {
-
-       private String inputString;
-       private String[] tokenStream; // for parse in flight
-       private int tokenIndex = 0;
-
-       /**
-        * AMC. Parse the signature string interpreting it as a ClassSignature according to the grammar defined in Section 4.4.4 of the
-        * JVM specification.
-        */
-       public GenericSignature.ClassSignature parseAsClassSignature(String sig) {
-               this.inputString = sig;
-               tokenStream = tokenize(sig);
-               tokenIndex = 0;
-               GenericSignature.ClassSignature classSig = new GenericSignature.ClassSignature();
-               // FormalTypeParameters-opt
-               if (maybeEat("<")) {
-                       List<FormalTypeParameter> formalTypeParametersList = new ArrayList<FormalTypeParameter>();
-                       do {
-                               formalTypeParametersList.add(parseFormalTypeParameter());
-                       } while (!maybeEat(">"));
-                       classSig.formalTypeParameters = new FormalTypeParameter[formalTypeParametersList.size()];
-                       formalTypeParametersList.toArray(classSig.formalTypeParameters);
-               }
-               classSig.superclassSignature = parseClassTypeSignature();
-               List<ClassTypeSignature> superIntSigs = new ArrayList<ClassTypeSignature>();
-               while (tokenIndex < tokenStream.length) {
-                       superIntSigs.add(parseClassTypeSignature());
-               }
-               classSig.superInterfaceSignatures = new ClassTypeSignature[superIntSigs.size()];
-               superIntSigs.toArray(classSig.superInterfaceSignatures);
-               return classSig;
-       }
-
-       /**
-        * AMC. Parse the signature string interpreting it as a MethodTypeSignature according to the grammar defined in Section 4.4.4 of
-        * the JVM specification.
-        */
-       public MethodTypeSignature parseAsMethodSignature(String sig) {
-               this.inputString = sig;
-               tokenStream = tokenize(sig);
-               tokenIndex = 0;
-               FormalTypeParameter[] formals = new FormalTypeParameter[0];
-               TypeSignature returnType = null;
-               // FormalTypeParameters-opt
-               if (maybeEat("<")) {
-                       List<FormalTypeParameter> formalTypeParametersList = new ArrayList<FormalTypeParameter>();
-                       do {
-                               formalTypeParametersList.add(parseFormalTypeParameter());
-                       } while (!maybeEat(">"));
-                       formals = new FormalTypeParameter[formalTypeParametersList.size()];
-                       formalTypeParametersList.toArray(formals);
-               }
-               // Parameters
-               eat("(");
-               List<TypeSignature> paramList = new ArrayList<TypeSignature>();
-               while (!maybeEat(")")) {
-                       FieldTypeSignature fsig = parseFieldTypeSignature(true);
-                       if (fsig != null) {
-                               paramList.add(fsig);
-                       } else {
-                               paramList.add(new GenericSignature.BaseTypeSignature(eatIdentifier()));
-                       }
-               }
-               TypeSignature[] params = new TypeSignature[paramList.size()];
-               paramList.toArray(params);
-               // return type
-               returnType = parseFieldTypeSignature(true);
-               if (returnType == null)
-                       returnType = new GenericSignature.BaseTypeSignature(eatIdentifier());
-               // throws
-               List<FieldTypeSignature> throwsList = new ArrayList<FieldTypeSignature>();
-               while (maybeEat("^")) {
-                       FieldTypeSignature fsig = parseFieldTypeSignature(false);
-                       throwsList.add(fsig);
-               }
-               FieldTypeSignature[] throwsSigs = new FieldTypeSignature[throwsList.size()];
-               throwsList.toArray(throwsSigs);
-               return new GenericSignature.MethodTypeSignature(formals, params, returnType, throwsSigs);
-       }
-
-       /**
-        * AMC. Parse the signature string interpreting it as a FieldTypeSignature according to the grammar defined in Section 4.4.4 of
-        * the JVM specification.
-        */
-       public FieldTypeSignature parseAsFieldSignature(String sig) {
-               this.inputString = sig;
-               tokenStream = tokenize(sig);
-               tokenIndex = 0;
-               return parseFieldTypeSignature(false);
-       }
-
-       private FormalTypeParameter parseFormalTypeParameter() {
-               FormalTypeParameter ftp = new FormalTypeParameter();
-               // Identifier
-               ftp.identifier = eatIdentifier();
-               // ClassBound
-               eat(":");
-               ftp.classBound = parseFieldTypeSignature(true);
-               if (ftp.classBound == null) {
-                       ftp.classBound = new ClassTypeSignature("Ljava/lang/Object;", "Ljava/lang/Object");
-               }
-               // Optional InterfaceBounds
-               List<FieldTypeSignature> optionalBounds = new ArrayList<FieldTypeSignature>();
-               while (maybeEat(":")) {
-                       optionalBounds.add(parseFieldTypeSignature(false));
-               }
-               ftp.interfaceBounds = new FieldTypeSignature[optionalBounds.size()];
-               optionalBounds.toArray(ftp.interfaceBounds);
-               return ftp;
-       }
-
-       private FieldTypeSignature parseFieldTypeSignature(boolean isOptional) {
-               if (isOptional) {
-                       // anything other than 'L', 'T' or '[' and we're out of here
-                       if (!tokenStream[tokenIndex].startsWith("L") && !tokenStream[tokenIndex].startsWith("T")
-                                       && !tokenStream[tokenIndex].startsWith("[")) {
-                               return null;
-                       }
-               }
-               if (maybeEat("[")) {
-                       return parseArrayTypeSignature();
-               } else if (tokenStream[tokenIndex].startsWith("L")) {
-                       return parseClassTypeSignature();
-               } else if (tokenStream[tokenIndex].startsWith("T")) {
-                       return parseTypeVariableSignature();
-               } else {
-                       throw new IllegalStateException("Expecting [,L, or T, but found " + tokenStream[tokenIndex] + " while unpacking "
-                                       + inputString);
-               }
-       }
-
-       private ArrayTypeSignature parseArrayTypeSignature() {
-               // opening [ already eaten
-               FieldTypeSignature fieldType = parseFieldTypeSignature(true);
-               if (fieldType != null) {
-                       return new ArrayTypeSignature(fieldType);
-               } else {
-                       // must be BaseType array
-                       return new ArrayTypeSignature(new BaseTypeSignature(eatIdentifier()));
-               }
-       }
-
-       // L PackageSpecifier* SimpleClassTypeSignature ClassTypeSignature* ;
-       private ClassTypeSignature parseClassTypeSignature() {
-               SimpleClassTypeSignature outerType = null;
-               SimpleClassTypeSignature[] nestedTypes = new SimpleClassTypeSignature[0];
-               StringBuffer ret = new StringBuffer();
-               String identifier = eatIdentifier();
-               ret.append(identifier);
-               while (maybeEat("/")) {
-                       ret.append("/"); // dont forget this...
-                       ret.append(eatIdentifier());
-               }
-               identifier = ret.toString();
-               // now we have either a "." indicating the start of a nested type,
-               // or a "<" indication type arguments, or ";" and we are done.
-               while (!maybeEat(";")) {
-                       if (tokenStream[tokenIndex].equals(".")) {
-                               // outer type completed
-                               outerType = new SimpleClassTypeSignature(identifier);
-                               nestedTypes = parseNestedTypesHelper(ret);
-                       } else if (tokenStream[tokenIndex].equals("<")) {
-                               ret.append("<");
-                               TypeArgument[] tArgs = maybeParseTypeArguments();
-                               for (int i = 0; i < tArgs.length; i++) {
-                                       ret.append(tArgs[i].toString());
-                               }
-                               ret.append(">");
-                               outerType = new SimpleClassTypeSignature(identifier, tArgs);
-                               nestedTypes = parseNestedTypesHelper(ret);
-                       } else {
-                               throw new IllegalStateException("Expecting .,<, or ;, but found " + tokenStream[tokenIndex] + " while unpacking "
-                                               + inputString);
-                       }
-               }
-               ret.append(";");
-               if (outerType == null)
-                       outerType = new SimpleClassTypeSignature(ret.toString());
-               return new ClassTypeSignature(ret.toString(), outerType, nestedTypes);
-       }
-
-       /**
-        * Helper method to digest nested types, slightly more complex than necessary to cope with some android related
-        * incorrect classes (see bug 406167)
-        */
-       private SimpleClassTypeSignature[] parseNestedTypesHelper(StringBuffer ret) {
-               boolean brokenSignature = false;
-               SimpleClassTypeSignature[] nestedTypes;
-               List<SimpleClassTypeSignature> nestedTypeList = new ArrayList<SimpleClassTypeSignature>();
-               while (maybeEat(".")) {
-                       ret.append(".");
-                       SimpleClassTypeSignature sig = parseSimpleClassTypeSignature();
-                       if (tokenStream[tokenIndex].equals("/")) {
-                               if (!brokenSignature) {
-                                       System.err.println("[See bug 406167] Bad class file signature encountered, nested types appear package qualified, ignoring those incorrect pieces. Signature: "+inputString);
-                               }
-                               brokenSignature = true;
-                               // hit something like: Lcom/a/a/b/t<TK;TV;>.com/a/a/b/af.com/a/a/b/ag;
-                               // and we are looking at the '/' after the com
-                               tokenIndex++; // pointing at the next identifier
-                               while (tokenStream[tokenIndex+1].equals("/")) {
-                                       tokenIndex+=2; // jump over an 'identifier' '/' pair
-                               }
-                               // now tokenIndex is the final bit of the name (which we'll treat as the inner type name)
-                               sig = parseSimpleClassTypeSignature();
-                       }
-                       ret.append(sig.toString());
-                       nestedTypeList.add(sig);
-               };
-               nestedTypes = new SimpleClassTypeSignature[nestedTypeList.size()];
-               nestedTypeList.toArray(nestedTypes);
-               return nestedTypes;
-       }
-
-       private SimpleClassTypeSignature parseSimpleClassTypeSignature() {
-               String identifier = eatIdentifier();
-               TypeArgument[] tArgs = maybeParseTypeArguments();
-               if (tArgs != null) {
-                       return new SimpleClassTypeSignature(identifier, tArgs);
-               } else {
-                       return new SimpleClassTypeSignature(identifier);
-               }
-       }
-
-       private TypeArgument parseTypeArgument() {
-               boolean isPlus = false;
-               boolean isMinus = false;
-               if (maybeEat("*")) {
-                       return new TypeArgument();
-               } else if (maybeEat("+")) {
-                       isPlus = true;
-               } else if (maybeEat("-")) {
-                       isMinus = true;
-               }
-               FieldTypeSignature sig = parseFieldTypeSignature(false);
-               return new TypeArgument(isPlus, isMinus, sig);
-       }
-
-       private TypeArgument[] maybeParseTypeArguments() {
-               if (maybeEat("<")) {
-                       List<TypeArgument> typeArgs = new ArrayList<TypeArgument>();
-                       do {
-                               TypeArgument arg = parseTypeArgument();
-                               typeArgs.add(arg);
-                       } while (!maybeEat(">"));
-                       TypeArgument[] tArgs = new TypeArgument[typeArgs.size()];
-                       typeArgs.toArray(tArgs);
-                       return tArgs;
-               } else {
-                       return null;
-               }
-       }
-
-       private TypeVariableSignature parseTypeVariableSignature() {
-               TypeVariableSignature tv = new TypeVariableSignature(eatIdentifier());
-               eat(";");
-               return tv;
-       }
-
-       private boolean maybeEat(String token) {
-               if (tokenStream.length <= tokenIndex)
-                       return false;
-               if (tokenStream[tokenIndex].equals(token)) {
-                       tokenIndex++;
-                       return true;
-               }
-               return false;
-       }
-
-       private void eat(String token) {
-               if (!tokenStream[tokenIndex].equals(token)) {
-                       throw new IllegalStateException("Expecting " + token + " but found " + tokenStream[tokenIndex] + " while unpacking "
-                                       + inputString);
-               }
-               tokenIndex++;
-       }
-
-       private String eatIdentifier() {
-               return tokenStream[tokenIndex++];
-       }
-
-       /**
-        * non-private for test visibility Splits a string containing a generic signature into tokens for consumption by the parser.
-        */
-       public String[] tokenize(String signatureString) {
-               char[] chars = signatureString.toCharArray();
-               int index = 0;
-               List<String> tokens = new ArrayList<String>();
-               StringBuffer identifier = new StringBuffer();
-               boolean inParens = false;
-               boolean inArray = false;
-               boolean couldSeePrimitive = false;
-               do {
-                       switch (chars[index]) {
-                       case '<':
-                               if (identifier.length() > 0)
-                                       tokens.add(identifier.toString());
-                               identifier = new StringBuffer();
-                               tokens.add("<");
-                               break;
-                       case '>':
-                               if (identifier.length() > 0)
-                                       tokens.add(identifier.toString());
-                               identifier = new StringBuffer();
-                               tokens.add(">");
-                               break;
-                       case ':':
-                               if (identifier.length() > 0)
-                                       tokens.add(identifier.toString());
-                               identifier = new StringBuffer();
-                               tokens.add(":");
-                               break;
-                       case '/':
-                               if (identifier.length() > 0)
-                                       tokens.add(identifier.toString());
-                               identifier = new StringBuffer();
-                               tokens.add("/");
-                               couldSeePrimitive = false;
-                               break;
-                       case ';':
-                               if (identifier.length() > 0)
-                                       tokens.add(identifier.toString());
-                               identifier = new StringBuffer();
-                               tokens.add(";");
-                               couldSeePrimitive = true;
-                               inArray = false;
-                               break;
-                       case '^':
-                               if (identifier.length() > 0)
-                                       tokens.add(identifier.toString());
-                               identifier = new StringBuffer();
-                               tokens.add("^");
-                               break;
-                       case '+':
-                               tokens.add("+");
-                               break;
-                       case '-':
-                               tokens.add("-");
-                               break;
-                       case '*':
-                               tokens.add("*");
-                               break;
-                       case '.':
-                               if (identifier.length() > 0)
-                                       tokens.add(identifier.toString());
-                               identifier = new StringBuffer();
-                               couldSeePrimitive = false;
-                               tokens.add(".");
-                               break;
-                       case '(':
-                               tokens.add("(");
-                               inParens = true;
-                               couldSeePrimitive = true;
-                               break;
-                       case ')':
-                               tokens.add(")");
-                               inParens = false;
-                               break;
-                       case '[':
-                               tokens.add("[");
-                               couldSeePrimitive = true;
-                               inArray = true;
-                               break;
-                       case 'B':
-                       case 'C':
-                       case 'D':
-                       case 'F':
-                       case 'I':
-                       case 'J':
-                       case 'S':
-                       case 'V':
-                       case 'Z':
-                               if ((inParens || inArray) && couldSeePrimitive && identifier.length() == 0) {
-                                       tokens.add(new String("" + chars[index]));
-                               } else {
-                                       identifier.append(chars[index]);
-                               }
-                               inArray = false;
-                               break;
-                       case 'L':
-                               couldSeePrimitive = false;
-                               // deliberate fall-through
-                       default:
-                               identifier.append(chars[index]);
-                       }
-               } while ((++index) < chars.length);
-               if (identifier.length() > 0)
-                       tokens.add(identifier.toString());
-               String[] tokenArray = new String[tokens.size()];
-               tokens.toArray(tokenArray);
-               return tokenArray;
-       }
-
-}
diff --git a/util/src/org/aspectj/util/IStructureModel.java b/util/src/org/aspectj/util/IStructureModel.java
deleted file mode 100644 (file)
index 9008f08..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 2008 Contributors.
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *   Andy Clement
- * ******************************************************************/
-package org.aspectj.util;
-
-/**
- * Abstraction of a structure model
- * 
- * @author Andy Clement
- */
-public interface IStructureModel {
-
-}
diff --git a/util/src/org/aspectj/util/LangUtil.java b/util/src/org/aspectj/util/LangUtil.java
deleted file mode 100644 (file)
index 82bd942..0000000
+++ /dev/null
@@ -1,1511 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 1999-2001 Xerox Corporation, 
- *               2002 Palo Alto Research Center, Incorporated (PARC).
- *               2018 Contributors
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *     Xerox/PARC     initial implementation 
- * ******************************************************************/
-package org.aspectj.util;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.reflect.Array;
-import java.lang.reflect.InvocationTargetException;
-import java.security.PrivilegedActionException;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.BitSet;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-
-/**
- * 
- */
-public class LangUtil {
-
-       public static final String EOL;
-
-       public static final String JRT_FS = "jrt-fs.jar";
-
-       private static double vmVersion;
-
-       /**
-        * @return the vm version (1.1, 1.2, 1.3, 1.4, etc)
-        */
-       public static String getVmVersionString() {
-               return Double.toString(vmVersion);
-       }
-       
-       public static double getVmVersion() {
-               return vmVersion;
-       }
-       
-       static {
-               StringWriter buf = new StringWriter();
-               PrintWriter writer = new PrintWriter(buf);
-               writer.println("");
-               String eol = "\n";
-               try {
-                       buf.close();
-                       StringBuffer sb = buf.getBuffer();
-                       if (sb != null) {
-                               eol = buf.toString();
-                       }
-               } catch (Throwable t) {
-               }
-               EOL = eol;
-       }
-
-       static {
-               // http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html
-               // http://openjdk.java.net/jeps/223 "New Version-String Scheme"
-               try {
-                       String vm = System.getProperty("java.version"); // JLS 20.18.7
-                       if (vm == null) {
-                               vm = System.getProperty("java.runtime.version");
-                       }
-                       if (vm == null) {
-                               vm = System.getProperty("java.vm.version");
-                       }
-                       if (vm == null) {
-                               new RuntimeException(
-                                               "System properties appear damaged, cannot find: java.version/java.runtime.version/java.vm.version")
-                                               .printStackTrace(System.err);
-                               vmVersion = 1.5;
-                       } else {
-                               // Version: [1-9][0-9]*((\.0)*\.[1-9][0-9]*)* 
-                               // Care about the first set of digits and second set if first digit is 1
-                               try {
-                                       List<Integer> numbers = getFirstNumbers(vm);
-                                       if (numbers.get(0) == 1) {
-                                               // Old school for 1.0 > 1.8
-                                               vmVersion = numbers.get(0)+(numbers.get(1)/10d);
-                                       } else {
-                                               // numbers.get(0) is the major version (9 and above)
-                                               // Note here the number will be 9 (or 10), *not* 1.9 or 1.10
-                                               vmVersion = numbers.get(0);
-                                       }
-                               } catch (Throwable t) {
-                                       // Give up
-                                       vmVersion = 1.5;
-                               }
-                       }
-               } catch (Throwable t) {
-                       new RuntimeException(
-                                       "System properties appear damaged, cannot find: java.version/java.runtime.version/java.vm.version", t)
-                                       .printStackTrace(System.err);
-                       vmVersion = 1.5;
-               }
-       }
-       
-       private static List<Integer> getFirstNumbers(String vm) {
-               List<Integer> result = new ArrayList<Integer>();
-               StringTokenizer st = new StringTokenizer(vm,".-_");
-               try {
-                       result.add(Integer.parseInt(st.nextToken()));
-                       result.add(Integer.parseInt(st.nextToken()));
-               } catch (Exception e) {
-                       // NoSuchElementException if no more tokens
-                       // NumberFormatException if not a number
-               }
-               return result;
-       }
-
-       public static boolean is13VMOrGreater() {
-               return 1.3 <= vmVersion;
-       }
-
-       public static boolean is14VMOrGreater() {
-               return 1.4 <= vmVersion;
-       }
-
-       public static boolean is15VMOrGreater() {
-               return 1.5 <= vmVersion;
-       }
-
-       public static boolean is16VMOrGreater() {
-               return 1.6 <= vmVersion;
-       }
-
-       public static boolean is17VMOrGreater() {
-               return 1.7 <= vmVersion;
-       }
-       
-       public static boolean is18VMOrGreater() {
-               return 1.8 <= vmVersion;
-       }
-       
-       public static boolean is19VMOrGreater() {
-               return 9 <= vmVersion;
-       }
-       
-       public static boolean is10VMOrGreater() {
-               return 10 <= vmVersion;
-       }
-
-       public static boolean is11VMOrGreater() {
-               return 11 <= vmVersion;
-       }
-
-       /**
-        * Shorthand for "if null, throw IllegalArgumentException"
-        * 
-        * @throws IllegalArgumentException "null {name}" if o is null
-        */
-       public static final void throwIaxIfNull(final Object o, final String name) {
-               if (null == o) {
-                       String message = "null " + (null == name ? "input" : name);
-                       throw new IllegalArgumentException(message);
-               }
-       }
-
-       /**
-        * Shorthand for "if not null or not assignable, throw IllegalArgumentException"
-        * 
-        * @param c the Class to check - use null to ignore type check
-        * @throws IllegalArgumentException "null {name}" if o is null
-        */
-       public static final void throwIaxIfNotAssignable(final Object ra[], final Class<?> c, final String name) {
-               throwIaxIfNull(ra, name);
-               String label = (null == name ? "input" : name);
-               for (int i = 0; i < ra.length; i++) {
-                       if (null == ra[i]) {
-                               String m = " null " + label + "[" + i + "]";
-                               throw new IllegalArgumentException(m);
-                       } else if (null != c) {
-                               Class<?> actualClass = ra[i].getClass();
-                               if (!c.isAssignableFrom(actualClass)) {
-                                       String message = label + " not assignable to " + c.getName();
-                                       throw new IllegalArgumentException(message);
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Shorthand for "if not null or not assignable, throw IllegalArgumentException"
-        * 
-        * @throws IllegalArgumentException "null {name}" if o is null
-        */
-       public static final void throwIaxIfNotAssignable(final Object o, final Class<?> c, final String name) {
-               throwIaxIfNull(o, name);
-               if (null != c) {
-                       Class<?> actualClass = o.getClass();
-                       if (!c.isAssignableFrom(actualClass)) {
-                               String message = name + " not assignable to " + c.getName();
-                               throw new IllegalArgumentException(message);
-                       }
-               }
-       }
-
-       // /**
-       // * Shorthand for
-       // "if any not null or not assignable, throw IllegalArgumentException"
-       // * @throws IllegalArgumentException "{name} is not assignable to {c}"
-       // */
-       // public static final void throwIaxIfNotAllAssignable(final Collection
-       // collection,
-       // final Class c, final String name) {
-       // throwIaxIfNull(collection, name);
-       // if (null != c) {
-       // for (Iterator iter = collection.iterator(); iter.hasNext();) {
-       // throwIaxIfNotAssignable(iter.next(), c, name);
-       //                              
-       // }
-       // }
-       // }
-       /**
-        * Shorthand for "if false, throw IllegalArgumentException"
-        * 
-        * @throws IllegalArgumentException "{message}" if test is false
-        */
-       public static final void throwIaxIfFalse(final boolean test, final String message) {
-               if (!test) {
-                       throw new IllegalArgumentException(message);
-               }
-       }
-
-       // /** @return ((null == s) || (0 == s.trim().length())); */
-       // public static boolean isEmptyTrimmed(String s) {
-       // return ((null == s) || (0 == s.length())
-       // || (0 == s.trim().length()));
-       // }
-
-       /** @return ((null == s) || (0 == s.length())); */
-       public static boolean isEmpty(String s) {
-               return ((null == s) || (0 == s.length()));
-       }
-
-       /** @return ((null == ra) || (0 == ra.length)) */
-       public static boolean isEmpty(Object[] ra) {
-               return ((null == ra) || (0 == ra.length));
-       }
-
-       /** @return ((null == ra) || (0 == ra.length)) */
-       public static boolean isEmpty(byte[] ra) {
-               return ((null == ra) || (0 == ra.length));
-       }
-
-       /** @return ((null == collection) || (0 == collection.size())) */
-       public static boolean isEmpty(Collection<?> collection) {
-               return ((null == collection) || (0 == collection.size()));
-       }
-
-       /** @return ((null == map) || (0 == map.size())) */
-       public static boolean isEmpty(Map<?,?> map) {
-               return ((null == map) || (0 == map.size()));
-       }
-
-       /**
-        * Splits <code>text</code> at whitespace.
-        * 
-        * @param text <code>String</code> to split.
-        */
-       public static String[] split(String text) {
-               return strings(text).toArray(new String[0]);
-       }
-
-       /**
-        * Splits <code>input</code> at commas, trimming any white space.
-        * 
-        * @param input <code>String</code> to split.
-        * @return List of String of elements.
-        */
-       public static List<String> commaSplit(String input) {
-               return anySplit(input, ",");
-       }
-
-       /**
-        * Split string as classpath, delimited at File.pathSeparator. Entries are not trimmed, but empty entries are ignored.
-        * 
-        * @param classpath the String to split - may be null or empty
-        * @return String[] of classpath entries
-        */
-       public static String[] splitClasspath(String classpath) {
-               if (LangUtil.isEmpty(classpath)) {
-                       return new String[0];
-               }
-               StringTokenizer st = new StringTokenizer(classpath, File.pathSeparator);
-               ArrayList<String> result = new ArrayList<String>(st.countTokens());
-               while (st.hasMoreTokens()) {
-                       String entry = st.nextToken();
-                       if (!LangUtil.isEmpty(entry)) {
-                               result.add(entry);
-                       }
-               }
-               return result.toArray(new String[0]);
-       }
-
-       /**
-        * Get System property as boolean, but use default value where the system property is not set.
-        * 
-        * @return true if value is set to true, false otherwise
-        */
-       public static boolean getBoolean(String propertyName, boolean defaultValue) {
-               if (null != propertyName) {
-                       try {
-                               String value = System.getProperty(propertyName);
-                               if (null != value) {
-                                       return Boolean.valueOf(value).booleanValue();
-                               }
-                       } catch (Throwable t) {
-                               // default below
-                       }
-               }
-               return defaultValue;
-       }
-
-       /**
-        * Splits <code>input</code>, removing delimiter and trimming any white space. Returns an empty collection if the input is null.
-        * If delimiter is null or empty or if the input contains no delimiters, the input itself is returned after trimming white
-        * space.
-        * 
-        * @param input <code>String</code> to split.
-        * @param delim <code>String</code> separators for input.
-        * @return List of String of elements.
-        */
-       public static List<String> anySplit(String input, String delim) {
-               if (null == input) {
-                       return Collections.emptyList();
-               }
-               ArrayList<String> result = new ArrayList<String>();
-
-               if (LangUtil.isEmpty(delim) || (-1 == input.indexOf(delim))) {
-                       result.add(input.trim());
-               } else {
-                       StringTokenizer st = new StringTokenizer(input, delim);
-                       while (st.hasMoreTokens()) {
-                               result.add(st.nextToken().trim());
-                       }
-               }
-               return result;
-       }
-
-       /**
-        * Splits strings into a <code>List</code> using a <code>StringTokenizer</code>.
-        * 
-        * @param text <code>String</code> to split.
-        */
-       public static List<String> strings(String text) {
-               if (LangUtil.isEmpty(text)) {
-                       return Collections.emptyList();
-               }
-               List<String> strings = new ArrayList<String>();
-               StringTokenizer tok = new StringTokenizer(text);
-               while (tok.hasMoreTokens()) {
-                       strings.add(tok.nextToken());
-               }
-               return strings;
-       }
-
-       /** @return a non-null unmodifiable List */
-       public static <T> List<T> safeList(List<T> list) {
-               return (null == list ? Collections.<T>emptyList() : Collections.unmodifiableList(list));
-       }
-
-       // /**
-       // * Select from input String[] based on suffix-matching
-       // * @param inputs String[] of input - null ignored
-       // * @param suffixes String[] of suffix selectors - null ignored
-       // * @param ignoreCase if true, ignore case
-       // * @return String[] of input that end with any input
-       // */
-       // public static String[] endsWith(String[] inputs, String[] suffixes,
-       // boolean ignoreCase) {
-       // if (LangUtil.isEmpty(inputs) || LangUtil.isEmpty(suffixes)) {
-       // return new String[0];
-       // }
-       // if (ignoreCase) {
-       // String[] temp = new String[suffixes.length];
-       // for (int i = 0; i < temp.length; i++) {
-       // String suff = suffixes[i];
-       // temp[i] = (null == suff ? null : suff.toLowerCase());
-       // }
-       // suffixes = temp;
-       // }
-       // ArrayList result = new ArrayList();
-       // for (int i = 0; i < inputs.length; i++) {
-       // String input = inputs[i];
-       // if (null == input) {
-       // continue;
-       // }
-       // if (!ignoreCase) {
-       // input = input.toLowerCase();
-       // }
-       // for (int j = 0; j < suffixes.length; j++) {
-       // String suffix = suffixes[j];
-       // if (null == suffix) {
-       // continue;
-       // }
-       // if (input.endsWith(suffix)) {
-       // result.add(input);
-       // break;
-       // }
-       // }
-       // }
-       // return (String[]) result.toArray(new String[0]);
-       // }
-       //    
-       // /**
-       // * Select from input String[] if readable directories
-       // * @param inputs String[] of input - null ignored
-       // * @param baseDir the base directory of the input
-       // * @return String[] of input that end with any input
-       // */
-       // public static String[] selectDirectories(String[] inputs, File baseDir) {
-       // if (LangUtil.isEmpty(inputs)) {
-       // return new String[0];
-       // }
-       // ArrayList result = new ArrayList();
-       // for (int i = 0; i < inputs.length; i++) {
-       // String input = inputs[i];
-       // if (null == input) {
-       // continue;
-       // }
-       // File inputFile = new File(baseDir, input);
-       // if (inputFile.canRead() && inputFile.isDirectory()) {
-       // result.add(input);
-       // }
-       // }
-       // return (String[]) result.toArray(new String[0]);
-       // }
-
-       /**
-        * copy non-null two-dimensional String[][]
-        * 
-        * @see extractOptions(String[], String[][])
-        */
-       public static String[][] copyStrings(String[][] in) {
-               String[][] out = new String[in.length][];
-               for (int i = 0; i < out.length; i++) {
-                       out[i] = new String[in[i].length];
-                       System.arraycopy(in[i], 0, out[i], 0, out[i].length);
-               }
-               return out;
-       }
-
-       /**
-        * Extract options and arguments to input option list, returning remainder. The input options will be nullified if not found.
-        * e.g.,
-        * 
-        * <pre>
-        * String[] options = new String[][] { new String[] { &quot;-verbose&quot; }, new String[] { &quot;-classpath&quot;, null } };
-        * String[] args = extractOptions(args, options);
-        * boolean verbose = null != options[0][0];
-        * boolean classpath = options[1][1];
-        * </pre>
-        * 
-        * @param args the String[] input options
-        * @param options the String[][]options to find in the input args - not null for each String[] component the first subcomponent
-        *        is the option itself, and there is one String subcomponent for each additional argument.
-        * @return String[] of args remaining after extracting options to extracted
-        */
-       public static String[] extractOptions(String[] args, String[][] options) {
-               if (LangUtil.isEmpty(args) || LangUtil.isEmpty(options)) {
-                       return args;
-               }
-               BitSet foundSet = new BitSet();
-               String[] result = new String[args.length];
-               int resultIndex = 0;
-               for (int j = 0; j < args.length; j++) {
-                       boolean found = false;
-                       for (int i = 0; !found && (i < options.length); i++) {
-                               String[] option = options[i];
-                               LangUtil.throwIaxIfFalse(!LangUtil.isEmpty(option), "options");
-                               String sought = option[0];
-                               found = sought.equals(args[j]);
-                               if (found) {
-                                       foundSet.set(i);
-                                       int doMore = option.length - 1;
-                                       if (0 < doMore) {
-                                               final int MAX = j + doMore;
-                                               if (MAX >= args.length) {
-                                                       String s = "expecting " + doMore + " args after ";
-                                                       throw new IllegalArgumentException(s + args[j]);
-                                               }
-                                               for (int k = 1; k < option.length; k++) {
-                                                       option[k] = args[++j];
-                                               }
-                                       }
-                               }
-                       }
-                       if (!found) {
-                               result[resultIndex++] = args[j];
-                       }
-               }
-
-               // unset any not found
-               for (int i = 0; i < options.length; i++) {
-                       if (!foundSet.get(i)) {
-                               options[i][0] = null;
-                       }
-               }
-               // fixup remainder
-               if (resultIndex < args.length) {
-                       String[] temp = new String[resultIndex];
-                       System.arraycopy(result, 0, temp, 0, resultIndex);
-                       args = temp;
-               }
-
-               return args;
-       }
-
-       //    
-       // /**
-       // * Extract options and arguments to input parameter list, returning
-       // remainder.
-       // * @param args the String[] input options
-       // * @param validOptions the String[] options to find in the input args -
-       // not null
-       // * @param optionArgs the int[] number of arguments for each option in
-       // validOptions
-       // * (if null, then no arguments for any option)
-       // * @param extracted the List for the matched options
-       // * @return String[] of args remaining after extracting options to
-       // extracted
-       // */
-       // public static String[] extractOptions(String[] args, String[]
-       // validOptions,
-       // int[] optionArgs, List extracted) {
-       // if (LangUtil.isEmpty(args)
-       // || LangUtil.isEmpty(validOptions) ) {
-       // return args;
-       // }
-       // if (null != optionArgs) {
-       // if (optionArgs.length != validOptions.length) {
-       // throw new IllegalArgumentException("args must match options");
-       // }
-       // }
-       // String[] result = new String[args.length];
-       // int resultIndex = 0;
-       // for (int j = 0; j < args.length; j++) {
-       // boolean found = false;
-       // for (int i = 0; !found && (i < validOptions.length); i++) {
-       // String sought = validOptions[i];
-       // int doMore = (null == optionArgs ? 0 : optionArgs[i]);
-       // if (LangUtil.isEmpty(sought)) {
-       // continue;
-       // }
-       // found = sought.equals(args[j]);
-       // if (found) {
-       // if (null != extracted) {
-       // extracted.add(sought);
-       // }
-       // if (0 < doMore) {
-       // final int MAX = j + doMore;
-       // if (MAX >= args.length) {
-       // String s = "expecting " + doMore + " args after ";
-       // throw new IllegalArgumentException(s + args[j]);
-       // }
-       // if (null != extracted) {
-       // while (j < MAX) {
-       // extracted.add(args[++j]);
-       // }
-       // } else {
-       // j = MAX;
-       // }
-       // }
-       // break;
-       // }
-       // }
-       // if (!found) {
-       // result[resultIndex++] = args[j];
-       // }
-       // }
-       // if (resultIndex < args.length) {
-       // String[] temp = new String[resultIndex];
-       // System.arraycopy(result, 0, temp, 0, resultIndex);
-       // args = temp;
-       // }
-       // return args;
-       // }
-
-       // /** @return String[] of entries in validOptions found in args */
-       // public static String[] selectOptions(String[] args, String[]
-       // validOptions) {
-       // if (LangUtil.isEmpty(args) || LangUtil.isEmpty(validOptions)) {
-       // return new String[0];
-       // }
-       // ArrayList result = new ArrayList();
-       // for (int i = 0; i < validOptions.length; i++) {
-       // String sought = validOptions[i];
-       // if (LangUtil.isEmpty(sought)) {
-       // continue;
-       // }
-       // for (int j = 0; j < args.length; j++) {
-       // if (sought.equals(args[j])) {
-       // result.add(sought);
-       // break;
-       // }
-       // }
-       // }
-       // return (String[]) result.toArray(new String[0]);
-       // }
-
-       // /** @return String[] of entries in validOptions found in args */
-       // public static String[] selectOptions(List args, String[] validOptions) {
-       // if (LangUtil.isEmpty(args) || LangUtil.isEmpty(validOptions)) {
-       // return new String[0];
-       // }
-       // ArrayList result = new ArrayList();
-       // for (int i = 0; i < validOptions.length; i++) {
-       // String sought = validOptions[i];
-       // if (LangUtil.isEmpty(sought)) {
-       // continue;
-       // }
-       // for (Iterator iter = args.iterator(); iter.hasNext();) {
-       // String arg = (String) iter.next();
-       // if (sought.equals(arg)) {
-       // result.add(sought);
-       // break;
-       // }
-       // }
-       // }
-       // return (String[]) result.toArray(new String[0]);
-       // }
-
-       // /**
-       // * Generate variants of String[] options by creating an extra set for
-       // * each option that ends with "-". If none end with "-", then an
-       // * array equal to <code>new String[][] { options }</code> is returned;
-       // * if one ends with "-", then two sets are returned,
-       // * three causes eight sets, etc.
-       // * @return String[][] with each option set.
-       // * @throws IllegalArgumentException if any option is null or empty.
-       // */
-       // public static String[][] optionVariants(String[] options) {
-       // if ((null == options) || (0 == options.length)) {
-       // return new String[][] { new String[0]};
-       // }
-       // // be nice, don't stomp input
-       // String[] temp = new String[options.length];
-       // System.arraycopy(options, 0, temp, 0, temp.length);
-       // options = temp;
-       // boolean[] dup = new boolean[options.length];
-       // int numDups = 0;
-       //        
-       // for (int i = 0; i < options.length; i++) {
-       // String option = options[i];
-       // if (LangUtil.isEmpty(option)) {
-       // throw new IllegalArgumentException("empty option at " + i);
-       // }
-       // if (option.endsWith("-")) {
-       // options[i] = option.substring(0, option.length()-1);
-       // dup[i] = true;
-       // numDups++;
-       // }
-       // }
-       // final String[] NONE = new String[0];
-       // final int variants = exp(2, numDups);
-       // final String[][] result = new String[variants][];
-       // // variant is a bitmap wrt doing extra value when dup[k]=true
-       // for (int variant = 0; variant < variants; variant++) {
-       // ArrayList next = new ArrayList();
-       // int nextOption = 0;
-       // for (int k = 0; k < options.length; k++) {
-       // if (!dup[k] || (0 != (variant & (1 << (nextOption++))))) {
-       // next.add(options[k]);
-       // }
-       // }
-       // result[variant] = (String[]) next.toArray(NONE);
-       // }
-       // return result;
-       // }
-       //    
-       // private static int exp(int base, int power) { // not in Math?
-       // if (0 > power) {
-       // throw new IllegalArgumentException("negative power: " + power);
-       // }
-       // int result = 1;
-       // while (0 < power--) {
-       // result *= base;
-       // }
-       // return result;
-       // }
-
-       // /**
-       // * Make a copy of the array.
-       // * @return an array with the same component type as source
-       // * containing same elements, even if null.
-       // * @throws IllegalArgumentException if source is null
-       // */
-       // public static final Object[] copy(Object[] source) {
-       // LangUtil.throwIaxIfNull(source, "source");
-       // final Class c = source.getClass().getComponentType();
-       // Object[] result = (Object[]) Array.newInstance(c, source.length);
-       // System.arraycopy(source, 0, result, 0, result.length);
-       // return result;
-       // }
-
-       /**
-        * Convert arrays safely. The number of elements in the result will be 1 smaller for each element that is null or not
-        * assignable. This will use sink if it has exactly the right size. The result will always have the same component type as sink.
-        * 
-        * @return an array with the same component type as sink containing any assignable elements in source (in the same order).
-        * @throws IllegalArgumentException if either is null
-        */
-       public static Object[] safeCopy(Object[] source, Object[] sink) {
-               final Class<?> sinkType = (null == sink ? Object.class : sink.getClass().getComponentType());
-               final int sourceLength = (null == source ? 0 : source.length);
-               final int sinkLength = (null == sink ? 0 : sink.length);
-
-               final int resultSize;
-               ArrayList<Object> result = null;
-               if (0 == sourceLength) {
-                       resultSize = 0;
-               } else {
-                       result = new ArrayList<Object>(sourceLength);
-                       for (int i = 0; i < sourceLength; i++) {
-                               if ((null != source[i]) && (sinkType.isAssignableFrom(source[i].getClass()))) {
-                                       result.add(source[i]);
-                               }
-                       }
-                       resultSize = result.size();
-               }
-               if (resultSize != sinkLength) {
-                       sink = (Object[]) Array.newInstance(sinkType, result.size());
-               }
-               if (0 < resultSize) {
-                       sink = result.toArray(sink);
-               }
-               return sink;
-       }
-
-       /**
-        * @return a String with the unqualified class name of the class (or "null")
-        */
-       public static String unqualifiedClassName(Class<?> c) {
-               if (null == c) {
-                       return "null";
-               }
-               String name = c.getName();
-               int loc = name.lastIndexOf(".");
-               if (-1 != loc) {
-                       name = name.substring(1 + loc);
-               }
-               return name;
-       }
-
-       /**
-        * @return a String with the unqualified class name of the object (or "null")
-        */
-       public static String unqualifiedClassName(Object o) {
-               return LangUtil.unqualifiedClassName(null == o ? null : o.getClass());
-       }
-
-       /** inefficient way to replace all instances of sought with replace */
-       public static String replace(String in, String sought, String replace) {
-               if (LangUtil.isEmpty(in) || LangUtil.isEmpty(sought)) {
-                       return in;
-               }
-               StringBuffer result = new StringBuffer();
-               final int len = sought.length();
-               int start = 0;
-               int loc;
-               while (-1 != (loc = in.indexOf(sought, start))) {
-                       result.append(in.substring(start, loc));
-                       if (!LangUtil.isEmpty(replace)) {
-                               result.append(replace);
-                       }
-                       start = loc + len;
-               }
-               result.append(in.substring(start));
-               return result.toString();
-       }
-
-       /** render i right-justified with a given width less than about 40 */
-       public static String toSizedString(long i, int width) {
-               String result = "" + i;
-               int size = result.length();
-               if (width > size) {
-                       final String pad = "                                              ";
-                       final int padLength = pad.length();
-                       if (width > padLength) {
-                               width = padLength;
-                       }
-                       int topad = width - size;
-                       result = pad.substring(0, topad) + result;
-               }
-               return result;
-       }
-
-       // /** clip StringBuffer to maximum number of lines */
-       // static String clipBuffer(StringBuffer buffer, int maxLines) {
-       // if ((null == buffer) || (1 > buffer.length())) return "";
-       // StringBuffer result = new StringBuffer();
-       // int j = 0;
-       // final int MAX = maxLines;
-       // final int N = buffer.length();
-       // for (int i = 0, srcBegin = 0; i < MAX; srcBegin += j) {
-       // // todo: replace with String variant if/since getting char?
-       // char[] chars = new char[128];
-       // int srcEnd = srcBegin+chars.length;
-       // if (srcEnd >= N) {
-       // srcEnd = N-1;
-       // }
-       // if (srcBegin == srcEnd) break;
-       // //log("srcBegin:" + srcBegin + ":srcEnd:" + srcEnd);
-       // buffer.getChars(srcBegin, srcEnd, chars, 0);
-       // for (j = 0; j < srcEnd-srcBegin/*chars.length*/; j++) {
-       // char c = chars[j];
-       // if (c == '\n') {
-       // i++;
-       // j++;
-       // break;
-       // }
-       // }
-       // try { result.append(chars, 0, j); }
-       // catch (Throwable t) { }
-       // }
-       // return result.toString();
-       // }
-
-       /**
-        * @return "({UnqualifiedExceptionClass}) {message}"
-        */
-       public static String renderExceptionShort(Throwable e) {
-               if (null == e) {
-                       return "(Throwable) null";
-               }
-               return "(" + LangUtil.unqualifiedClassName(e) + ") " + e.getMessage();
-       }
-
-       /**
-        * Renders exception <code>t</code> after unwrapping and eliding any test packages.
-        * 
-        * @param t <code>Throwable</code> to print.
-        * @see #maxStackTrace
-        */
-       public static String renderException(Throwable t) {
-               return renderException(t, true);
-       }
-
-       /**
-        * Renders exception <code>t</code>, unwrapping, optionally eliding and limiting total number of lines.
-        * 
-        * @param t <code>Throwable</code> to print.
-        * @param elide true to limit to 100 lines and elide test packages
-        * @see StringChecker#TEST_PACKAGES
-        */
-       public static String renderException(Throwable t, boolean elide) {
-               if (null == t) {
-                       return "null throwable";
-               }
-               t = unwrapException(t);
-               StringBuffer stack = stackToString(t, false);
-               if (elide) {
-                       elideEndingLines(StringChecker.TEST_PACKAGES, stack, 100);
-               }
-               return stack.toString();
-       }
-
-       /**
-        * Trim ending lines from a StringBuffer, clipping to maxLines and further removing any number of trailing lines accepted by
-        * checker.
-        * 
-        * @param checker returns true if trailing line should be elided.
-        * @param stack StringBuffer with lines to elide
-        * @param maxLines int for maximum number of resulting lines
-        */
-       static void elideEndingLines(StringChecker checker, StringBuffer stack, int maxLines) {
-               if (null == checker || (null == stack) || (0 == stack.length())) {
-                       return;
-               }
-               final LinkedList<String> lines = new LinkedList<String>();
-               StringTokenizer st = new StringTokenizer(stack.toString(), "\n\r");
-               while (st.hasMoreTokens() && (0 < --maxLines)) {
-                       lines.add(st.nextToken());
-               }
-               st = null;
-
-               String line;
-               int elided = 0;
-               while (!lines.isEmpty()) {
-                       line = lines.getLast();
-                       if (!checker.acceptString(line)) {
-                               break;
-                       } else {
-                               elided++;
-                               lines.removeLast();
-                       }
-               }
-               if ((elided > 0) || (maxLines < 1)) {
-                       final int EOL_LEN = EOL.length();
-                       int totalLength = 0;
-                       while (!lines.isEmpty()) {
-                               totalLength += EOL_LEN + lines.getFirst().length();
-                               lines.removeFirst();
-                       }
-                       if (stack.length() > totalLength) {
-                               stack.setLength(totalLength);
-                               if (elided > 0) {
-                                       stack.append("    (... " + elided + " lines...)");
-                               }
-                       }
-               }
-       }
-
-       /** Dump message and stack to StringBuffer. */
-       public static StringBuffer stackToString(Throwable throwable, boolean skipMessage) {
-               if (null == throwable) {
-                       return new StringBuffer();
-               }
-               StringWriter buf = new StringWriter();
-               PrintWriter writer = new PrintWriter(buf);
-               if (!skipMessage) {
-                       writer.println(throwable.getMessage());
-               }
-               throwable.printStackTrace(writer);
-               try {
-                       buf.close();
-               } catch (IOException ioe) {
-               } // ignored
-               return buf.getBuffer();
-       }
-
-       /** @return Throwable input or tail of any wrapped exception chain */
-       public static Throwable unwrapException(Throwable t) {
-               Throwable current = t;
-               Throwable next = null;
-               while (current != null) {
-                       // Java 1.2 exceptions that carry exceptions
-                       if (current instanceof InvocationTargetException) {
-                               next = ((InvocationTargetException) current).getTargetException();
-                       } else if (current instanceof ClassNotFoundException) {
-                               next = ((ClassNotFoundException) current).getException();
-                       } else if (current instanceof ExceptionInInitializerError) {
-                               next = ((ExceptionInInitializerError) current).getException();
-                       } else if (current instanceof PrivilegedActionException) {
-                               next = ((PrivilegedActionException) current).getException();
-                       } else if (current instanceof SQLException) {
-                               next = ((SQLException) current).getNextException();
-                       }
-                       // ...getException():
-                       // javax.naming.event.NamingExceptionEvent
-                       // javax.naming.ldap.UnsolicitedNotification
-                       // javax.xml.parsers.FactoryConfigurationError
-                       // javax.xml.transform.TransformerFactoryConfigurationError
-                       // javax.xml.transform.TransformerException
-                       // org.xml.sax.SAXException
-                       // 1.4: Throwable.getCause
-                       // java.util.logging.LogRecord.getThrown()
-                       if (null == next) {
-                               break;
-                       } else {
-                               current = next;
-                               next = null;
-                       }
-               }
-               return current;
-       }
-
-       /**
-        * Replacement for Arrays.asList(..) which gacks on null and returns a List in which remove is an unsupported operation.
-        * 
-        * @param array the Object[] to convert (may be null)
-        * @return the List corresponding to array (never null)
-        */
-       public static <T> List<T> arrayAsList(T[] array) {
-               if ((null == array) || (1 > array.length)) {
-                       return Collections.emptyList();
-               }
-               ArrayList<T> list = new ArrayList<T>();
-               list.addAll(Arrays.asList(array));
-               return list;
-       }
-
-       /** check if input contains any packages to elide. */
-       public static class StringChecker {
-               static StringChecker TEST_PACKAGES = new StringChecker(new String[] { "org.aspectj.testing",
-                               "org.eclipse.jdt.internal.junit", "junit.framework.",
-                               "org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" });
-
-               String[] infixes;
-
-               /** @param infixes adopted */
-               StringChecker(String[] infixes) {
-                       this.infixes = infixes;
-               }
-
-               /** @return true if input contains infixes */
-               public boolean acceptString(String input) {
-                       boolean result = false;
-                       if (!LangUtil.isEmpty(input)) {
-                               for (int i = 0; !result && (i < infixes.length); i++) {
-                                       result = (-1 != input.indexOf(infixes[i]));
-                               }
-                       }
-                       return result;
-               }
-       }
-
-       /**
-        * Gen classpath.
-        * 
-        * @param bootclasspath
-        * @param classpath
-        * @param classesDir
-        * @param outputJar
-        * @return String combining classpath elements
-        */
-       public static String makeClasspath( // XXX dumb implementation
-                       String bootclasspath, String classpath, String classesDir, String outputJar) {
-               StringBuffer sb = new StringBuffer();
-               addIfNotEmpty(bootclasspath, sb, File.pathSeparator);
-               addIfNotEmpty(classpath, sb, File.pathSeparator);
-               if (!addIfNotEmpty(classesDir, sb, File.pathSeparator)) {
-                       addIfNotEmpty(outputJar, sb, File.pathSeparator);
-               }
-               return sb.toString();
-       }
-
-       /**
-        * @param input ignored if null
-        * @param sink the StringBuffer to add input to - return false if null
-        * @param delimiter the String to append to input when added - ignored if empty
-        * @return true if input + delimiter added to sink
-        */
-       private static boolean addIfNotEmpty(String input, StringBuffer sink, String delimiter) {
-               if (LangUtil.isEmpty(input) || (null == sink)) {
-                       return false;
-               }
-               sink.append(input);
-               if (!LangUtil.isEmpty(delimiter)) {
-                       sink.append(delimiter);
-               }
-               return true;
-       }
-
-       /**
-        * Create or initialize a process controller to run a process in another VM asynchronously.
-        * 
-        * @param controller the ProcessController to initialize, if not null
-        * @param classpath
-        * @param mainClass
-        * @param args
-        * @return initialized ProcessController
-        */
-       public static ProcessController makeProcess(ProcessController controller, String classpath, String mainClass, String[] args) {
-               File java = LangUtil.getJavaExecutable();
-               ArrayList<String> cmd = new ArrayList<String>();
-               cmd.add(java.getAbsolutePath());
-               cmd.add("-classpath");
-               cmd.add(classpath);
-               cmd.add(mainClass);
-               if (!LangUtil.isEmpty(args)) {
-                       cmd.addAll(Arrays.asList(args));
-               }
-               String[] command = cmd.toArray(new String[0]);
-               if (null == controller) {
-                       controller = new ProcessController();
-               }
-               controller.init(command, mainClass);
-               return controller;
-       }
-
-       // /**
-       // * Create a process to run asynchronously.
-       // * @param controller if not null, initialize this one
-       // * @param command the String[] command to run
-       // * @param controller the ProcessControl for streams and results
-       // */
-       // public static ProcessController makeProcess( // not needed?
-       // ProcessController controller,
-       // String[] command,
-       // String label) {
-       // if (null == controller) {
-       // controller = new ProcessController();
-       // }
-       // controller.init(command, label);
-       // return controller;
-       // }
-
-       /**
-        * Find java executable File path from java.home system property.
-        * 
-        * @return File associated with the java command, or null if not found.
-        */
-       public static File getJavaExecutable() {
-               String javaHome = null;
-               File result = null;
-               // java.home
-               // java.class.path
-               // java.ext.dirs
-               try {
-                       javaHome = System.getProperty("java.home");
-               } catch (Throwable t) {
-                       // ignore
-               }
-               if (null != javaHome) {
-                       File binDir = new File(javaHome, "bin");
-                       if (binDir.isDirectory() && binDir.canRead()) {
-                               String[] execs = new String[] { "java", "java.exe" };
-                               for (int i = 0; i < execs.length; i++) {
-                                       result = new File(binDir, execs[i]);
-                                       if (result.canRead()) {
-                                               break;
-                                       }
-                               }
-                       }
-               }
-               return result;
-       }
-
-       // /**
-       // * Sleep for a particular period (in milliseconds).
-       // *
-       // * @param time the long time in milliseconds to sleep
-       // * @return true if delay succeeded, false if interrupted 100 times
-       // */
-       // public static boolean sleep(long milliseconds) {
-       // if (milliseconds == 0) {
-       // return true;
-       // } else if (milliseconds < 0) {
-       // throw new IllegalArgumentException("negative: " + milliseconds);
-       // }
-       // return sleepUntil(milliseconds + System.currentTimeMillis());
-       // }
-
-       /**
-        * Sleep until a particular time.
-        * 
-        * @param time the long time in milliseconds to sleep until
-        * @return true if delay succeeded, false if interrupted 100 times
-        */
-       public static boolean sleepUntil(long time) {
-               if (time == 0) {
-                       return true;
-               } else if (time < 0) {
-                       throw new IllegalArgumentException("negative: " + time);
-               }
-               // final Thread thread = Thread.currentThread();
-               long curTime = System.currentTimeMillis();
-               for (int i = 0; (i < 100) && (curTime < time); i++) {
-                       try {
-                               Thread.sleep(time - curTime);
-                       } catch (InterruptedException e) {
-                               // ignore
-                       }
-                       curTime = System.currentTimeMillis();
-               }
-               return (curTime >= time);
-       }
-
-       /**
-        * Handle an external process asynchrously. <code>start()</code> launches a main thread to wait for the process and pipes
-        * streams (in child threads) through to the corresponding streams (e.g., the process System.err to this System.err). This can
-        * complete normally, by exception, or on demand by a client. Clients can implement <code>doCompleting(..)</code> to get notice
-        * when the process completes.
-        * <p>
-        * The following sample code creates a process with a completion callback starts it, and some time later retries the process.
-        * 
-        * <pre>
-        * LangUtil.ProcessController controller = new LangUtil.ProcessController() {
-        *      protected void doCompleting(LangUtil.ProcessController.Thrown thrown, int result) {
-        *              // signal result 
-        *      }
-        * };
-        * controller.init(new String[] { &quot;java&quot;, &quot;-version&quot; }, &quot;java version&quot;);
-        * controller.start();
-        * // some time later...
-        * // retry...
-        * if (!controller.completed()) {
-        *      controller.stop();
-        *      controller.reinit();
-        *      controller.start();
-        * }
-        * </pre>
-        * 
-        * <u>warning</u>: Currently this does not close the input or output streams, since doing so prevents their use later.
-        */
-       public static class ProcessController {
-               /*
-                * XXX not verified thread-safe, but should be. Known problems: - user stops (completed = true) then exception thrown from
-                * destroying process (stop() expects !completed) ...
-                */
-               private String[] command;
-               private String[] envp;
-               private String label;
-
-               private boolean init;
-               private boolean started;
-               private boolean completed;
-               /** if true, stopped by user when not completed */
-               private boolean userStopped;
-
-               private Process process;
-               private FileUtil.Pipe errStream;
-               private FileUtil.Pipe outStream;
-               private FileUtil.Pipe inStream;
-               private ByteArrayOutputStream errSnoop;
-               private ByteArrayOutputStream outSnoop;
-
-               private int result;
-               private Thrown thrown;
-
-               public ProcessController() {
-               }
-
-               /**
-                * Permit re-running using the same command if this is not started or if completed. Can also call this when done with
-                * results to release references associated with results (e.g., stack traces).
-                */
-               public final void reinit() {
-                       if (!init) {
-                               throw new IllegalStateException("must init(..) before reinit()");
-                       }
-                       if (started && !completed) {
-                               throw new IllegalStateException("not completed - do stop()");
-                       }
-                       // init everything but command and label
-                       started = false;
-                       completed = false;
-                       result = Integer.MIN_VALUE;
-                       thrown = null;
-                       process = null;
-                       errStream = null;
-                       outStream = null;
-                       inStream = null;
-               }
-
-               public final void init(String classpath, String mainClass, String[] args) {
-                       init(LangUtil.getJavaExecutable(), classpath, mainClass, args);
-               }
-
-               public final void init(File java, String classpath, String mainClass, String[] args) {
-                       LangUtil.throwIaxIfNull(java, "java");
-                       LangUtil.throwIaxIfNull(mainClass, "mainClass");
-                       LangUtil.throwIaxIfNull(args, "args");
-                       ArrayList<String> cmd = new ArrayList<String>();
-                       cmd.add(java.getAbsolutePath());
-                       cmd.add("-classpath");
-                       cmd.add(classpath);
-                       cmd.add(mainClass);
-                       if (!LangUtil.isEmpty(args)) {
-                               cmd.addAll(Arrays.asList(args));
-                       }
-                       init(cmd.toArray(new String[0]), mainClass);
-               }
-
-               public final void init(String[] command, String label) {
-                       this.command = (String[]) LangUtil.safeCopy(command, new String[0]);
-                       if (1 > this.command.length) {
-                               throw new IllegalArgumentException("empty command");
-                       }
-                       this.label = LangUtil.isEmpty(label) ? command[0] : label;
-                       init = true;
-                       reinit();
-               }
-
-               public final void setEnvp(String[] envp) {
-                       this.envp = (String[]) LangUtil.safeCopy(envp, new String[0]);
-                       if (1 > this.envp.length) {
-                               throw new IllegalArgumentException("empty envp");
-                       }
-               }
-
-               public final void setErrSnoop(ByteArrayOutputStream snoop) {
-                       errSnoop = snoop;
-                       if (null != errStream) {
-                               errStream.setSnoop(errSnoop);
-                       }
-               }
-
-               public final void setOutSnoop(ByteArrayOutputStream snoop) {
-                       outSnoop = snoop;
-                       if (null != outStream) {
-                               outStream.setSnoop(outSnoop);
-                       }
-               }
-
-               /**
-                * Start running the process and pipes asynchronously.
-                * 
-                * @return Thread started or null if unable to start thread (results available via <code>getThrown()</code>, etc.)
-                */
-               public final Thread start() {
-                       if (!init) {
-                               throw new IllegalStateException("not initialized");
-                       }
-                       synchronized (this) {
-                               if (started) {
-                                       throw new IllegalStateException("already started");
-                               }
-                               started = true;
-                       }
-                       try {
-                               process = Runtime.getRuntime().exec(command);
-                       } catch (IOException e) {
-                               stop(e, Integer.MIN_VALUE);
-                               return null;
-                       }
-                       errStream = new FileUtil.Pipe(process.getErrorStream(), System.err);
-                       if (null != errSnoop) {
-                               errStream.setSnoop(errSnoop);
-                       }
-                       outStream = new FileUtil.Pipe(process.getInputStream(), System.out);
-                       if (null != outSnoop) {
-                               outStream.setSnoop(outSnoop);
-                       }
-                       inStream = new FileUtil.Pipe(System.in, process.getOutputStream());
-                       // start 4 threads, process & pipes for in, err, out
-                       Runnable processRunner = new Runnable() {
-                               @Override
-                               public void run() {
-                                       Throwable thrown = null;
-                                       int result = Integer.MIN_VALUE;
-                                       try {
-                                               // pipe threads are children
-                                               new Thread(errStream).start();
-                                               new Thread(outStream).start();
-                                               new Thread(inStream).start();
-                                               process.waitFor();
-                                               result = process.exitValue();
-                                       } catch (Throwable e) {
-                                               thrown = e;
-                                       } finally {
-                                               stop(thrown, result);
-                                       }
-                               }
-                       };
-                       Thread result = new Thread(processRunner, label);
-                       result.start();
-                       return result;
-               }
-
-               /**
-                * Destroy any process, stop any pipes. This waits for the pipes to clear (reading until no more input is available), but
-                * does not wait for the input stream for the pipe to close (i.e., not waiting for end-of-file on input stream).
-                */
-               public final synchronized void stop() {
-                       if (completed) {
-                               return;
-                       }
-                       userStopped = true;
-                       stop(null, Integer.MIN_VALUE);
-               }
-
-               public final String[] getCommand() {
-                       String[] toCopy = command;
-                       if (LangUtil.isEmpty(toCopy)) {
-                               return new String[0];
-                       }
-                       String[] result = new String[toCopy.length];
-                       System.arraycopy(toCopy, 0, result, 0, result.length);
-                       return result;
-               }
-
-               public final boolean completed() {
-                       return completed;
-               }
-
-               public final boolean started() {
-                       return started;
-               }
-
-               public final boolean userStopped() {
-                       return userStopped;
-               }
-
-               /**
-                * Get any Throwable thrown. Note that the process can complete normally (with a valid return value), at the same time the
-                * pipes throw exceptions, and that this may return some exceptions even if the process is not complete.
-                * 
-                * @return null if not complete or Thrown containing exceptions thrown by the process and streams.
-                */
-               public final Thrown getThrown() { // cache this
-                       return makeThrown(null);
-               }
-
-               public final int getResult() {
-                       return result;
-               }
-
-               /**
-                * Subclasses implement this to get synchronous notice of completion. All pipes and processes should be complete at this
-                * time. To get the exceptions thrown for the pipes, use <code>getThrown()</code>. If there is an exception, the process
-                * completed abruptly (including side-effects of the user halting the process). If <code>userStopped()</code> is true, then
-                * some client asked that the process be destroyed using <code>stop()</code>. Otherwise, the result code should be the
-                * result value returned by the process.
-                * 
-                * @param thrown same as <code>getThrown().fromProcess</code>.
-                * @param result same as <code>getResult()</code>
-                * @see getThrown()
-                * @see getResult()
-                * @see stop()
-                */
-               protected void doCompleting(Thrown thrown, int result) {
-               }
-
-               /**
-                * Handle termination (on-demand, abrupt, or normal) by destroying and/or halting process and pipes.
-                * 
-                * @param thrown ignored if null
-                * @param result ignored if Integer.MIN_VALUE
-                */
-               private final synchronized void stop(Throwable thrown, int result) {
-                       if (completed) {
-                               throw new IllegalStateException("already completed");
-                       } else if (null != this.thrown) {
-                               throw new IllegalStateException("already set thrown: " + thrown);
-                       }
-                       // assert null == this.thrown
-                       this.thrown = makeThrown(thrown);
-                       if (null != process) {
-                               process.destroy();
-                       }
-                       if (null != inStream) {
-                               inStream.halt(false, true); // this will block if waiting
-                               inStream = null;
-                       }
-                       if (null != outStream) {
-                               outStream.halt(true, true);
-                               outStream = null;
-                       }
-                       if (null != errStream) {
-                               errStream.halt(true, true);
-                               errStream = null;
-                       }
-                       if (Integer.MIN_VALUE != result) {
-                               this.result = result;
-                       }
-                       completed = true;
-                       doCompleting(this.thrown, result);
-               }
-
-               /**
-                * Create snapshot of Throwable's thrown.
-                * 
-                * @param thrown ignored if null or if this.thrown is not null
-                */
-               private final synchronized Thrown makeThrown(Throwable processThrown) {
-                       if (null != thrown) {
-                               return thrown;
-                       }
-                       return new Thrown(processThrown, (null == outStream ? null : outStream.getThrown()), (null == errStream ? null
-                                       : errStream.getThrown()), (null == inStream ? null : inStream.getThrown()));
-               }
-
-               public static class Thrown {
-                       public final Throwable fromProcess;
-                       public final Throwable fromErrPipe;
-                       public final Throwable fromOutPipe;
-                       public final Throwable fromInPipe;
-                       /** true only if some Throwable is not null */
-                       public final boolean thrown;
-
-                       private Thrown(Throwable fromProcess, Throwable fromOutPipe, Throwable fromErrPipe, Throwable fromInPipe) {
-                               this.fromProcess = fromProcess;
-                               this.fromErrPipe = fromErrPipe;
-                               this.fromOutPipe = fromOutPipe;
-                               this.fromInPipe = fromInPipe;
-                               thrown = ((null != fromProcess) || (null != fromInPipe) || (null != fromOutPipe) || (null != fromErrPipe));
-                       }
-
-                       @Override
-                       public String toString() {
-                               StringBuffer sb = new StringBuffer();
-                               append(sb, fromProcess, "process");
-                               append(sb, fromOutPipe, " stdout");
-                               append(sb, fromErrPipe, " stderr");
-                               append(sb, fromInPipe, "  stdin");
-                               if (0 == sb.length()) {
-                                       return "Thrown (none)";
-                               } else {
-                                       return sb.toString();
-                               }
-                       }
-
-                       private void append(StringBuffer sb, Throwable thrown, String label) {
-                               if (null != thrown) {
-                                       sb.append("from " + label + ": ");
-                                       sb.append(LangUtil.renderExceptionShort(thrown));
-                                       sb.append(LangUtil.EOL);
-                               }
-                       }
-               } // class Thrown
-       }
-       
-       public static String getJrtFsFilePath() {
-               return getJavaHome() + File.separator + "lib" + File.separator + JRT_FS;
-       }
-               
-       public static String getJavaHome() {
-           return System.getProperty("java.home");
-       }
-
-}
diff --git a/util/src/org/aspectj/util/PartialOrder.java b/util/src/org/aspectj/util/PartialOrder.java
deleted file mode 100644 (file)
index 8bb9f3b..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 1999-2001 Xerox Corporation, 
- *               2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *     Xerox/PARC     initial implementation 
- * ******************************************************************/
-
-package org.aspectj.util;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * This class implements a partial order
- * 
- * It includes routines for doing a topo-sort
- */
-
-public class PartialOrder {
-
-       /**
-        * All classes that want to be part of a partial order must implement PartialOrder.PartialComparable.
-        */
-       public static interface PartialComparable {
-               /**
-                * @returns <ul>
-                *          <li>+1 if this is greater than other</li>
-                *          <li>-1 if this is less than other</li>
-                *          <li>0 if this is not comparable to other</li>
-                *          </ul>
-                * 
-                *          <b> Note: returning 0 from this method doesn't mean the same thing as returning 0 from
-                *          java.util.Comparable.compareTo()</b>
-                */
-               public int compareTo(Object other);
-
-               /**
-                * This method can provide a deterministic ordering for elements that are strictly not comparable. If you have no need for
-                * this, this method can just return 0 whenever called.
-                */
-               public int fallbackCompareTo(Object other);
-       }
-
-       private static class SortObject<T extends PartialComparable> {
-               T object;
-               List<SortObject<T>> smallerObjects = new LinkedList<SortObject<T>>();
-               List<SortObject<T>> biggerObjects = new LinkedList<SortObject<T>>();
-
-               public SortObject(T o) {
-                       object = o;
-               }
-
-               boolean hasNoSmallerObjects() {
-                       return smallerObjects.size() == 0;
-               }
-
-               boolean removeSmallerObject(SortObject<T> o) {
-                       smallerObjects.remove(o);
-                       return hasNoSmallerObjects();
-               }
-
-               void addDirectedLinks(SortObject<T> other) {
-                       int cmp = object.compareTo(other.object);
-                       if (cmp == 0) {
-                               return;
-                       }
-                       if (cmp > 0) {
-                               this.smallerObjects.add(other);
-                               other.biggerObjects.add(this);
-                       } else {
-                               this.biggerObjects.add(other);
-                               other.smallerObjects.add(this);
-                       }
-               }
-
-               public String toString() {
-                       return object.toString(); // +smallerObjects+biggerObjects;
-               }
-       }
-
-       private static <T extends PartialComparable> void addNewPartialComparable(List<SortObject<T>> graph, T o) {
-               SortObject<T> so = new SortObject<T>(o);
-               for (Iterator<SortObject<T>> i = graph.iterator(); i.hasNext();) {
-                       SortObject<T> other = i.next();
-                       so.addDirectedLinks(other);
-               }
-               graph.add(so);
-       }
-
-       private static <T extends PartialComparable> void removeFromGraph(List<SortObject<T>> graph, SortObject<T> o) {
-               for (Iterator<SortObject<T>> i = graph.iterator(); i.hasNext();) {
-                       SortObject<T> other = i.next();
-
-                       if (o == other) {
-                               i.remove();
-                       }
-                       // ??? could use this to build up a new queue of objects with no
-                       // ??? smaller ones
-                       other.removeSmallerObject(o);
-               }
-       }
-
-       /**
-        * @param objects must all implement PartialComparable
-        * 
-        * @returns the same members as objects, but sorted according to their partial order. returns null if the objects are cyclical
-        * 
-        */
-       public static <T extends PartialComparable> List<T> sort(List<T> objects) {
-               // lists of size 0 or 1 don't need any sorting
-               if (objects.size() < 2) {
-                       return objects;
-               }
-
-               // ??? we might want to optimize a few other cases of small size
-
-               // ??? I don't like creating this data structure, but it does give good
-               // ??? separation of concerns.
-               List<SortObject<T>> sortList = new LinkedList<SortObject<T>>();
-               for (Iterator<T> i = objects.iterator(); i.hasNext();) {
-                       addNewPartialComparable(sortList, i.next());
-               }
-
-               // System.out.println(sortList);
-
-               // now we have built our directed graph
-               // use a simple sort algorithm from here
-               // can increase efficiency later
-               // List ret = new ArrayList(objects.size());
-               final int N = objects.size();
-               for (int index = 0; index < N; index++) {
-                       // System.out.println(sortList);
-                       // System.out.println("-->" + ret);
-
-                       SortObject<T> leastWithNoSmallers = null;
-
-                       for (SortObject<T> so: sortList) {
-                               if (so.hasNoSmallerObjects()) {
-                                       if (leastWithNoSmallers == null || so.object.fallbackCompareTo(leastWithNoSmallers.object) < 0) {
-                                               leastWithNoSmallers = so;
-                                       }
-                               }
-                       }
-
-                       if (leastWithNoSmallers == null) {
-                               return null;
-                       }
-
-                       removeFromGraph(sortList, leastWithNoSmallers);
-                       objects.set(index, leastWithNoSmallers.object);
-               }
-
-               return objects;
-       }
-
-       /***********************************************************************************
-        * /* a minimal testing harness
-        ***********************************************************************************/
-       static class Token implements PartialComparable {
-               private String s;
-
-               Token(String s) {
-                       this.s = s;
-               }
-
-               public int compareTo(Object other) {
-                       Token t = (Token) other;
-
-                       int cmp = s.charAt(0) - t.s.charAt(0);
-                       if (cmp == 1) {
-                               return 1;
-                       }
-                       if (cmp == -1) {
-                               return -1;
-                       }
-                       return 0;
-               }
-
-               public int fallbackCompareTo(Object other) {
-                       return -s.compareTo(((Token) other).s);
-               }
-
-               public String toString() {
-                       return s;
-               }
-       }
-
-       public static void main(String[] args) {
-               List<Token> l = new ArrayList<Token>();
-               l.add(new Token("a1"));
-               l.add(new Token("c2"));
-               l.add(new Token("b3"));
-               l.add(new Token("f4"));
-               l.add(new Token("e5"));
-               l.add(new Token("d6"));
-               l.add(new Token("c7"));
-               l.add(new Token("b8"));
-
-               l.add(new Token("z"));
-               l.add(new Token("x"));
-
-               l.add(new Token("f9"));
-               l.add(new Token("e10"));
-               l.add(new Token("a11"));
-               l.add(new Token("d12"));
-               l.add(new Token("b13"));
-               l.add(new Token("c14"));
-
-               System.out.println(l);
-
-               sort(l);
-
-               System.out.println(l);
-       }
-}
diff --git a/util/src/org/aspectj/util/Reflection.java b/util/src/org/aspectj/util/Reflection.java
deleted file mode 100644 (file)
index 83ceaac..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *     Xerox/PARC     initial implementation 
- * ******************************************************************/
-
-
-package org.aspectj.util;
-
-import java.io.File;
-import java.lang.reflect.*;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.util.ArrayList;
-//import java.util.StringTokenizer;
-
-public class Reflection {
-    public static final Class<?>[] MAIN_PARM_TYPES = new Class[] {String[].class};    
-
-       private Reflection() {
-       }
-       
-       public static Object invokestaticN(Class<?> class_, String name, Object[] args) {
-               return invokeN(class_, name, null, args);
-       }
-
-       public static Object invoke(Class<?> class_, Object target, String name, Object arg1, Object arg2) {
-               return invokeN(class_, name, target, new Object[] { arg1, arg2 });
-       }
-       
-       public static Object invoke(Class<?> class_, Object target, String name, Object arg1, Object arg2, Object arg3) {
-               return invokeN(class_, name, target, new Object[] { arg1, arg2, arg3 });
-       }
-       
-       
-       public static Object invokeN(Class<?> class_, String name, Object target, Object[] args) {
-               Method meth = getMatchingMethod(class_, name, args);
-               try {
-                       return meth.invoke(target, args);
-               } catch (IllegalAccessException e) {
-                       throw new RuntimeException(e.toString());                       
-               } catch (InvocationTargetException e) {
-                       Throwable t = e.getTargetException();
-                       if (t instanceof Error) throw (Error)t;
-                       if (t instanceof RuntimeException) throw (RuntimeException)t;
-                       t.printStackTrace();
-                       throw new RuntimeException(t.toString());
-               }
-       }
-
-
-       public static Method getMatchingMethod(Class<?> class_, String name, Object[] args) {
-               Method[] meths = class_.getMethods();
-               for (int i=0; i < meths.length; i++) {
-                       Method meth = meths[i];
-                       if (meth.getName().equals(name) && isCompatible(meth, args)) {
-                               return meth;
-                       }
-               }
-               return null;
-       }
-
-       private static boolean isCompatible(Method meth, Object[] args) {
-               // ignore methods with overloading other than lengths
-               return meth.getParameterTypes().length == args.length;
-       }
-
-
-       
-
-       public static Object getStaticField(Class<?> class_, String name) {
-               try {
-                       return class_.getField(name).get(null);
-               } catch (IllegalAccessException e) {
-                       throw new RuntimeException("unimplemented");
-               } catch (NoSuchFieldException e) {
-                       throw new RuntimeException("unimplemented");
-               }
-       }
-
-    public static void runMainInSameVM(
-            String classpath,
-            String className, 
-            String[] args) 
-            throws SecurityException, NoSuchMethodException, 
-            IllegalArgumentException, IllegalAccessException, 
-            InvocationTargetException, ClassNotFoundException {
-        LangUtil.throwIaxIfNull(className, "class name");
-        if (LangUtil.isEmpty(classpath)) {
-            Class<?> mainClass = Class.forName(className);
-            runMainInSameVM(mainClass, args);
-            return;            
-        }
-        ArrayList<File> dirs = new ArrayList<File>();
-        ArrayList<File> libs = new ArrayList<File>();
-        ArrayList<URL> urls = new ArrayList<URL>();
-        String[] entries = LangUtil.splitClasspath(classpath);
-        for (int i = 0; i < entries.length; i++) {
-            String entry = entries[i];
-            URL url = makeURL(entry);
-            if (null != url) {
-                urls.add(url);
-            }
-            File file = new File(entries[i]);
-// tolerate bad entries b/c bootclasspath sometimes has them
-//            if (!file.canRead()) {
-//                throw new IllegalArgumentException("cannot read " + file);
-//            }
-            if (FileUtil.isZipFile(file)) {
-                libs.add(file);
-            } else if (file.isDirectory()) {
-                dirs.add(file);
-            } else {
-                // not URL, zip, or dir - unsure what to do
-            }
-        }
-        File[] dirRa = (File[]) dirs.toArray(new File[0]);
-        File[] libRa = (File[]) libs.toArray(new File[0]);
-        URL[] urlRa = (URL[]) urls.toArray(new URL[0]);
-        runMainInSameVM(urlRa, libRa, dirRa, className, args);
-    }
-        
-    public static void runMainInSameVM(
-            URL[] urls,
-            File[] libs, 
-            File[] dirs, 
-            String className, 
-            String[] args) 
-            throws SecurityException, NoSuchMethodException, 
-            IllegalArgumentException, IllegalAccessException, 
-            InvocationTargetException, ClassNotFoundException {
-        LangUtil.throwIaxIfNull(className, "class name");
-        LangUtil.throwIaxIfNotAssignable(libs, File.class, "jars");
-        LangUtil.throwIaxIfNotAssignable(dirs, File.class, "dirs");
-        URL[] libUrls = FileUtil.getFileURLs(libs);
-        if (!LangUtil.isEmpty(libUrls)) {
-            if (!LangUtil.isEmpty(urls)) {
-                URL[] temp = new URL[libUrls.length + urls.length];
-                System.arraycopy(urls, 0, temp, 0, urls.length);
-                System.arraycopy(urls, 0, temp, libUrls.length, urls.length);
-                urls = temp;
-            } else {
-                urls = libUrls;
-            }
-        }
-        UtilClassLoader loader = new UtilClassLoader(urls, dirs);
-        Class<?> targetClass = null;
-        try {
-            targetClass = loader.loadClass(className);
-        } catch (ClassNotFoundException e) {
-            String s = "unable to load class " + className
-                + " using class loader " + loader; 
-            throw new ClassNotFoundException(s);
-        }
-        Method main = targetClass.getMethod("main", MAIN_PARM_TYPES);
-        main.invoke(null, new Object[] { args });
-    }
-    
-    public static void runMainInSameVM(Class<?> mainClass, String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
-        LangUtil.throwIaxIfNull(mainClass, "main class");
-        Method main = mainClass.getMethod("main", MAIN_PARM_TYPES);
-        main.invoke(null, new Object[] { args });
-    }
-
-    /** @return URL if the input is valid as such */
-    private static URL makeURL(String s) {
-        try {
-            return new URL(s);            
-        } catch (Throwable t) {
-            return null;
-        }
-    }
-
-}
diff --git a/util/src/org/aspectj/util/SoftHashMap.java b/util/src/org/aspectj/util/SoftHashMap.java
deleted file mode 100644 (file)
index 94ae834..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 2017 Contributors
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- * ******************************************************************/package org.aspectj.util;
-
-import java.lang.ref.*;
-import java.util.*;
-
-public class SoftHashMap<K,V> extends AbstractMap<K,V> {
-       private Map<K, SpecialValue> map;
-       private ReferenceQueue<? super V> rq = new ReferenceQueue();
-
-       public SoftHashMap() {
-               this.map = new HashMap<K,SpecialValue>();
-       }
-       
-       class SpecialValue extends SoftReference<V> {
-               private final K key;
-
-               SpecialValue(K k, V v) {
-                       super(v, rq);
-                       this.key = k;
-               }
-       }
-
-       @SuppressWarnings("unchecked")
-       private void processQueue() {
-               SpecialValue sv = null;
-               while ((sv = (SpecialValue)rq.poll()) != null) {
-                       map.remove(sv.key);
-               }
-       }
-
-       @Override
-       public V get(Object key) {
-               SpecialValue ref = map.get(key);
-               if (ref == null) {
-                       map.remove(key);
-                       return null;
-               }
-               V value = ref.get();
-               if (value == null) {
-                       map.remove(ref.key);
-                       return null;
-               }
-               return value;
-       }
-
-       @Override
-       public V put(K k, V v) {
-               processQueue();
-               SpecialValue sv = new SpecialValue(k, v);
-               SpecialValue result = map.put(k, sv);
-               return (result == null ? null : result.get());
-       }
-
-       @Override
-       public java.util.Set<Map.Entry<K,V>> entrySet() {
-               if (map.isEmpty()) { return Collections.<K,V>emptyMap().entrySet(); }
-               Map<K,V> currentContents = new HashMap<K,V>();
-               for (Map.Entry<K,SpecialValue> entry: map.entrySet()) {
-                       V currentValueForEntry = entry.getValue().get();
-                       if (currentValueForEntry != null) {
-                               currentContents.put(entry.getKey(), currentValueForEntry);
-                       }
-               }
-               return currentContents.entrySet();
-       }
-
-       @Override
-       public void clear() {
-               processQueue();
-               map.clear();
-       }
-
-       @Override
-       public int size() {
-               processQueue();
-               return map.size();
-       }
-
-       @Override
-       public V remove(Object k) {
-               processQueue();
-               SpecialValue ref = map.remove(k);
-               if (ref == null) {
-                       return null;
-               }
-               return ref.get();
-       }
-}
diff --git a/util/src/org/aspectj/util/TypeSafeEnum.java b/util/src/org/aspectj/util/TypeSafeEnum.java
deleted file mode 100644 (file)
index 99d2231..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *     Xerox/PARC     initial implementation 
- * ******************************************************************/
-
-package org.aspectj.util;
-
-import java.io.*;
-
-public class TypeSafeEnum {
-       private byte key;
-       private String name;
-
-       public TypeSafeEnum(String name, int key) {
-               this.name = name;
-               if (key > Byte.MAX_VALUE || key < Byte.MIN_VALUE) {
-                       throw new IllegalArgumentException("key doesn't fit into a byte: " + key);
-               }
-               this.key = (byte) key;
-       }
-
-       public String toString() {
-               return name;
-       }
-
-       public String getName() {
-               return name;
-       }
-
-       public byte getKey() {
-               return key;
-       }
-
-       public void write(DataOutputStream s) throws IOException {
-               s.writeByte(key);
-       }
-}
diff --git a/util/src/org/aspectj/util/UtilClassLoader.java b/util/src/org/aspectj/util/UtilClassLoader.java
deleted file mode 100644 (file)
index 091e022..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 2003 Contributors.
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *     Isberg        initial implementation 
- * ******************************************************************/
-
-package org.aspectj.util;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Load classes as File from File[] dirs or URL[] jars.
- */
-public class UtilClassLoader extends URLClassLoader {
-
-    /** seek classes in dirs first */
-    List<File> dirs;
-
-    /** save URL[] only for toString */
-    private URL[] urlsForDebugString;
-    
-    public UtilClassLoader(URL[] urls, File[] dirs) {
-        super(urls);
-        LangUtil.throwIaxIfNotAssignable(dirs, File.class, "dirs");
-        this.urlsForDebugString = urls;
-        ArrayList<File> dcopy = new ArrayList<File>();
-        
-        if (!LangUtil.isEmpty(dirs)) {
-            dcopy.addAll(Arrays.asList(dirs));
-        }
-        this.dirs = Collections.unmodifiableList(dcopy);
-    }
-
-    
-    public URL getResource(String name) {
-        return ClassLoader.getSystemResource(name);
-    }
-    
-    public InputStream getResourceAsStream(String name) {
-        return ClassLoader.getSystemResourceAsStream(name);
-    } 
-    
-    public synchronized Class<?> loadClass(String name, boolean resolve)
-        throws ClassNotFoundException {
-        // search the cache, our dirs (if maybe test), 
-        // the system, the superclass (URL[]),
-        // and our dirs again (if not maybe test)
-        ClassNotFoundException thrown = null;
-        Class<?> result =  findLoadedClass(name);
-        if (null != result) {
-            resolve = false;
-        } else {
-            try { 
-                result = findSystemClass(name); 
-            } catch (ClassNotFoundException e) { 
-                thrown = e; 
-            }
-        }
-        if (null == result) {
-            try {
-                result = super.loadClass(name, resolve);
-            } catch (ClassNotFoundException e) {
-                thrown = e;
-            }
-            if (null != result) { // resolved by superclass
-                return result; 
-            }
-        }
-        if (null == result) {
-            byte[] data = readClass(name);
-            if (data != null) {
-                result = defineClass(name, data, 0, data.length);
-            } // handle ClassFormatError?            
-        }
-        
-        if (null == result) {
-            throw (null != thrown ? thrown : new ClassNotFoundException(name));
-        }
-        if (resolve) {
-            resolveClass(result);
-        }
-        return result;
-    }
-    
-    /** @return null if class not found or byte[] of class otherwise */
-    private byte[] readClass(String className) throws ClassNotFoundException {
-        final String fileName = className.replace('.', '/')+".class";
-        for (Iterator<File> iter = dirs.iterator(); iter.hasNext();) {
-            File file = new File(iter.next(), fileName);
-            if (file.canRead()) { 
-                return getClassData(file);
-            }
-        }
-        return null; 
-    }
-        
-    private byte[] getClassData(File f) {
-        try {
-            FileInputStream stream= new FileInputStream(f);
-            ByteArrayOutputStream out= new ByteArrayOutputStream(1000);
-            byte[] b= new byte[4096];
-            int n;
-            while ((n= stream.read(b)) != -1) {
-                out.write(b, 0, n);
-            }
-            stream.close();
-            out.close();
-            return out.toByteArray();
-        } catch (IOException e) {
-        }
-        return null;
-    }
-    
-    /** @return String with debug info: urls and classes used */
-    public String toString() {
-        return "UtilClassLoader(urls=" 
-            + Arrays.asList(urlsForDebugString)
-            + ", dirs="
-            + dirs
-            + ")";
-    }
-}
-
diff --git a/util/src/test/java/org/aspectj/util/FileUtilTest.java b/util/src/test/java/org/aspectj/util/FileUtilTest.java
new file mode 100644 (file)
index 0000000..8ce18c7
--- /dev/null
@@ -0,0 +1,707 @@
+/* *******************************************************************
+ * Copyright (c) 1999-2001 Xerox Corporation, 
+ *               2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/epl-v10.html 
+ *  
+ * Contributors: 
+ *     Xerox/PARC     initial implementation 
+ * ******************************************************************/
+
+package org.aspectj.util;
+
+//import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.StringBufferInputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+import junit.textui.TestRunner;
+
+/**
+ * 
+ */
+public class FileUtilTest extends TestCase {
+       public static final String[] NONE = new String[0];
+       public static boolean log = false;
+
+       public static void main(String[] args) {
+               TestRunner.main(new String[] { "org.aspectj.util.FileUtilTest" });
+       }
+
+       public static void assertSame(String prefix, String[] lhs, String[] rhs) { // XXX cheap diff
+               String srcPaths = LangUtil.arrayAsList(lhs).toString();
+               String destPaths = LangUtil.arrayAsList(rhs).toString();
+               if (!srcPaths.equals(destPaths)) {
+                       log("expected: " + srcPaths);
+                       log("  actual: " + destPaths);
+                       assertTrue(prefix + " expected=" + srcPaths + " != actual=" + destPaths, false);
+               }
+       }
+
+       /**
+        * Verify that dir contains files with names, and return the names of other files in dir.
+        * 
+        * @return the contents of dir after excluding files or NONE if none
+        * @throws AssertionFailedError if any names are not in dir
+        */
+       public static String[] dirContains(File dir, final String[] filenames) {
+               final ArrayList<String> sought = new ArrayList<>(LangUtil.arrayAsList(filenames));
+               FilenameFilter filter = new FilenameFilter() {
+                       public boolean accept(File d, String name) {
+                               return !sought.remove(name);
+                       }
+               };
+               // remove any found from sought and return remainder
+               String[] found = dir.list(filter);
+               if (0 < sought.size()) {
+                       assertTrue("found " + LangUtil.arrayAsList(dir.list()).toString() + " expected " + sought, false);
+               }
+               return (found.length == 0 ? NONE : found);
+       }
+
+       /** @return sorted String[] of all paths to all files/dirs under dir */
+       public static String[] dirPaths(File dir) {
+               return dirPaths(dir, new String[0]);
+       }
+
+       /**
+        * Get a sorted String[] of all paths to all files/dirs under dir. Files with names starting with "." are ignored, as are
+        * directory paths containing "CVS". The directory prefix of the path is stripped. Thus, given directory:
+        * 
+        * <pre>
+        * path/to
+        *   .cvsignore
+        *   CVS/
+        *     Root
+        *     Repository
+        *   Base.java
+        *   com/
+        *     parc/
+        *       messages.properties
+        *   org/
+        *     aspectj/
+        *       Version.java
+        * </pre>
+        * 
+        * a call
+        * 
+        * <pre>
+        * dirPaths(new File(&quot;path/to&quot;), new String[0]);
+        * </pre>
+        * 
+        * returns
+        * 
+        * <pre>
+        * { &quot;Base.java&quot;, &quot;com/parc/messages.properties&quot;, &quot;org/aspectj/Version.java&quot; }
+        * </pre>
+        * 
+        * while the call
+        * 
+        * <pre>
+        * dirPaths(new File(&quot;path/to&quot;), new String[] { &quot;.java&quot; });
+        * </pre>
+        * 
+        * returns
+        * 
+        * <pre>
+        * { &quot;Base.java&quot;, &quot;org/aspectj/Version.java&quot; }
+        * </pre>
+        * 
+        * @param dir the File path to the directory to inspect
+        * @param suffixes if not empty, require files returned to have this suffix
+        * @return sorted String[] of all paths to all files under dir ending with one of the listed suffixes but not starting with "."
+        */
+       public static String[] dirPaths(File dir, String[] suffixes) {
+               ArrayList<String> result = new ArrayList<String>();
+               doDirPaths(dir, result);
+               // if suffixes required, remove those without suffixes
+               if (!LangUtil.isEmpty(suffixes)) {
+                       for (ListIterator<String> iter = result.listIterator(); iter.hasNext();) {
+                               String path = iter.next().toString();
+                               boolean hasSuffix = false;
+                               for (int i = 0; !hasSuffix && (i < suffixes.length); i++) {
+                                       hasSuffix = path.endsWith(suffixes[i]);
+                               }
+                               if (!hasSuffix) {
+                                       iter.remove();
+                               }
+                       }
+               }
+               Collections.sort(result);
+               // trim prefix
+               final String prefix = dir.getPath();
+               final int len = prefix.length() + 1; // plus directory separator
+               String[] ra = (String[]) result.toArray(new String[0]);
+               for (int i = 0; i < ra.length; i++) {
+                       // assertTrue(ra[i].startsWith(prefix));
+                       assertTrue(ra[i], ra[i].length() > len);
+                       ra[i] = ra[i].substring(len);
+               }
+               return ra;
+       }
+
+       /**
+        * @param dir the File to read - ignored if null, not a directory, or has "CVS" in its path
+        * @param useSuffix if true, then use dir as suffix to path
+        */
+       private static void doDirPaths(File dir, ArrayList<String> paths) {
+               if ((null == dir) || !dir.canRead() || (-1 != dir.getPath().indexOf("CVS"))) {
+                       return;
+               }
+               File[] files = dir.listFiles();
+               for (int i = 0; i < files.length; i++) {
+                       String path = files[i].getPath();
+                       if (!files[i].getName().startsWith(".")) {
+                               if (files[i].isFile()) {
+                                       paths.add(path);
+                               } else if (files[i].isDirectory()) {
+                                       doDirPaths(files[i], paths);
+                               } else {
+                                       log("not file or dir: " + dir + "/" + path);
+                               }
+                       }
+               }
+       }
+
+       /** Print s if logging is enabled */
+       private static void log(String s) {
+               if (log) {
+                       System.err.println(s);
+               }
+       }
+
+       /** List of File files or directories to delete when exiting */
+       final ArrayList<File> tempFiles;
+
+       public FileUtilTest(String s) {
+               super(s);
+               tempFiles = new ArrayList<File>();
+       }
+
+       public void tearDown() {
+               for (ListIterator<File> iter = tempFiles.listIterator(); iter.hasNext();) {
+                       File dir = (File) iter.next();
+                       log("removing " + dir);
+                       FileUtil.deleteContents(dir);
+                       dir.delete();
+                       iter.remove();
+               }
+       }
+
+       public void testNotIsFileIsDirectory() {
+               File noSuchFile = new File("foo");
+               assertTrue(!noSuchFile.isFile());
+               assertTrue(!noSuchFile.isDirectory());
+       }
+
+       public void testGetBestFile() {
+               assertNull(FileUtil.getBestFile((String[]) null));
+               assertNull(FileUtil.getBestFile(new String[0]));
+               assertNull(FileUtil.getBestFile(new String[] { "!" }));
+               File f = FileUtil.getBestFile(new String[] { "." });
+               assertNotNull(f);
+               f = FileUtil.getBestFile(new String[] { "!", "." });
+               assertNotNull(f);
+               assertTrue(f.canRead());
+               boolean setProperty = false;
+               try {
+                       System.setProperty("bestfile", ".");
+                       setProperty = true;
+               } catch (Throwable t) {
+                       // ignore Security, etc.
+               }
+               if (setProperty) {
+                       f = FileUtil.getBestFile(new String[] { "sp:bestfile" });
+                       assertNotNull(f);
+                       assertTrue(f.canRead());
+               }
+       }
+
+       public void testCopyFiles() {
+               // bad input
+               Class<?> iaxClass = IllegalArgumentException.class;
+
+               checkCopyFiles(null, null, iaxClass, false);
+
+               File noSuchFile = new File("foo");
+               checkCopyFiles(noSuchFile, null, iaxClass, false);
+               checkCopyFiles(noSuchFile, noSuchFile, iaxClass, false);
+
+               File tempDir = FileUtil.getTempDir("testCopyFiles");
+               tempFiles.add(tempDir);
+               File fromFile = new File(tempDir, "fromFile");
+               String err = FileUtil.writeAsString(fromFile, "contents of from file");
+               assertTrue(err, null == err);
+               checkCopyFiles(fromFile, null, iaxClass, false);
+               checkCopyFiles(fromFile, fromFile, iaxClass, false);
+
+               // file-file
+               File toFile = new File(tempDir, "toFile");
+               checkCopyFiles(fromFile, toFile, null, true);
+
+               // file-dir
+               File toDir = new File(tempDir, "toDir");
+               assertTrue(toDir.mkdirs());
+               checkCopyFiles(fromFile, toDir, null, true);
+
+               // dir-dir
+               File fromDir = new File(tempDir, "fromDir");
+               assertTrue(fromDir.mkdirs());
+               checkCopyFiles(fromFile, fromDir, null, false);
+               File toFile2 = new File(fromDir, "toFile2");
+               checkCopyFiles(fromFile, toFile2, null, false);
+               checkCopyFiles(fromDir, toDir, null, true);
+       }
+
+       void checkCopyFiles(File from, File to, Class exceptionClass, boolean clean) {
+               try {
+                       FileUtil.copyFile(from, to);
+                       assertTrue(null == exceptionClass);
+                       if (to.isFile()) {
+                               assertTrue(from.length() == to.length()); // XXX cheap test
+                       } else if (!from.isDirectory()) {
+                               File toFile = new File(to, from.getName());
+                               assertTrue(from.length() == toFile.length());
+                       } else {
+                               // from is a dir and to is a dir, toDir should be created, and have the
+                               // same contents as fromDir.
+                               assertTrue(to.exists());
+                               assertTrue(from.listFiles().length == to.listFiles().length);
+                       }
+               } catch (Throwable t) {
+                       assertTrue(null != exceptionClass);
+                       assertTrue(exceptionClass.isAssignableFrom(t.getClass()));
+               } finally {
+                       if (clean && (null != to) && (to.exists())) {
+                               if (to.isDirectory()) {
+                                       FileUtil.deleteContents(to);
+                               }
+                               to.delete();
+                       }
+               }
+       }
+
+       public void testDirCopySubdirs() throws IOException {
+               File srcDir = new File("src");
+               File destDir = FileUtil.getTempDir("testDirCopySubdirs");
+               tempFiles.add(destDir);
+               FileUtil.copyDir(srcDir, destDir);
+               assertSame("testDirCopySubdirs", dirPaths(srcDir), dirPaths(destDir));
+       }
+
+       public void testDirCopySubdirsSuffix() throws IOException {
+               File srcDir = new File("src");
+               File destDir = FileUtil.getTempDir("testDirCopySubdirsSuffix");
+               tempFiles.add(destDir);
+               FileUtil.copyDir(srcDir, destDir, ".java", ".aj");
+
+               String[] sources = dirPaths(srcDir, new String[] { ".java" });
+               for (int i = 0; i < sources.length; i++) {
+                       sources[i] = sources[i].substring(0, sources[i].length() - 4);
+               }
+               String[] sinks = dirPaths(destDir, new String[] { ".aj" });
+               for (int i = 0; i < sinks.length; i++) {
+                       sinks[i] = sinks[i].substring(0, sinks[i].length() - 2);
+               }
+               assertSame("testDirCopySubdirs", sources, sinks);
+       }
+
+       public void testGetURL() {
+               String[] args = new String[] { ".", "../util/testdata", "../lib/test/aspectjrt.jar" };
+               for (int i = 0; i < args.length; i++) {
+                       checkGetURL(args[i]);
+               }
+       }
+
+       /**
+        * Method checkGetURL.
+        * 
+        * @param string
+        * @param uRL
+        */
+       private void checkGetURL(String arg) {
+               assertTrue(null != arg);
+               File f = new File(arg);
+               URL url = FileUtil.getFileURL(f);
+               assertTrue(null != url);
+               log("url       " + url);
+               if (!f.exists()) {
+                       log("not exist        " + f);
+               } else if (f.isDirectory()) {
+                       log("directory        " + f);
+               } else {
+                       log("     file        " + f);
+                       InputStream in = null;
+                       try {
+                               in = url.openStream();
+                       } catch (IOException e) {
+                               assertTrue("IOException: " + e, false);
+                       } finally {
+                               if (null != in) {
+                                       try {
+                                               in.close();
+                                       } catch (IOException e) {
+                                       }
+                               }
+                       }
+               }
+       }
+
+       public void testGetTempDir() {
+               boolean pass = true;
+               boolean delete = true;
+               checkGetTempDir("parent", null, pass, delete);
+               checkGetTempDir(null, "child", pass, delete);
+               tempFiles.add(checkGetTempDir("parent", "child", pass, !delete).getParentFile());
+               tempFiles.add(checkGetTempDir("parent", "child", pass, !delete).getParentFile());
+               tempFiles.add(checkGetTempDir("parent", "child", pass, !delete).getParentFile());
+       }
+
+       File checkGetTempDir(String parent, String child, boolean ok, boolean delete) {
+               File parentDir = FileUtil.getTempDir(parent);
+               assertTrue("unable to create " + parent, null != parentDir);
+               File dir = FileUtil.makeNewChildDir(parentDir, child);
+               log("parent=" + parent + " child=" + child + " -> " + dir);
+               assertTrue("dir: " + dir, ok == (dir.canWrite() && dir.isDirectory()));
+               if (delete) {
+                       dir.delete();
+                       parentDir.delete();
+               }
+               return dir;
+       }
+
+       public void testRandomFileString() {
+               ArrayList<String> results = new ArrayList<>();
+               for (int i = 0; i < 1000; i++) {
+                       String s = FileUtil.randomFileString();
+                       if (results.contains(s)) {
+                               log("warning: got duplicate at iteration " + i);
+                       }
+                       results.add(s);
+                       // System.err.print(" " + s);
+                       // if (0 == (i % 5)) {
+                       // System.err.println("");
+                       // }
+               }
+       }
+
+       public void testNormalizedPath() {
+               File tempFile = null;
+               try {
+                       tempFile = File.createTempFile("FileUtilTest_testNormalizedPath", "tmp");
+                       tempFiles.add(tempFile);
+               } catch (IOException e) {
+                       log("aborting test - unable to create temp file");
+                       return;
+               }
+               File parentDir = tempFile.getParentFile();
+               String tempFilePath = FileUtil.normalizedPath(tempFile, parentDir);
+               assertEquals(tempFile.getName(), tempFilePath);
+       }
+
+       public void testFileToClassName() {
+
+               File basedir = new File("/base/dir"); // never created
+               File classFile = new File(basedir, "foo/Bar.class");
+               assertEquals("foo.Bar", FileUtil.fileToClassName(basedir, classFile));
+
+               classFile = new File(basedir, "foo\\Bar.class");
+               assertEquals("foo.Bar", FileUtil.fileToClassName(basedir, classFile));
+
+               assertEquals("Bar", FileUtil.fileToClassName(null, classFile));
+
+               classFile = new File("/home/classes/org/aspectj/lang/JoinPoint.class");
+               assertEquals("org.aspectj.lang.JoinPoint", FileUtil.fileToClassName(null, classFile));
+
+               classFile = new File("/home/classes/com/sun/tools/Javac.class");
+               assertEquals("com.sun.tools.Javac", FileUtil.fileToClassName(null, classFile));
+       }
+
+       public void testDeleteContents() {
+               File tempDir = FileUtil.getTempDir("testDeleteContents");
+               tempFiles.add(tempDir);
+               File f = new File(tempDir, "foo");
+               f.mkdirs();
+               File g = new File(f, "bar");
+               g.mkdirs();
+               File h = new File(g, "bash");
+               h.mkdirs();
+               int d = FileUtil.deleteContents(f);
+               assertTrue(0 == d);
+               assertTrue(0 == f.list().length);
+               f.delete();
+               assertTrue(!f.exists());
+       }
+
+       public void testLineSeek() {
+               File tempDir = FileUtil.getTempDir("testLineSeek");
+               tempFiles.add(tempDir);
+               File file = new File(tempDir, "testLineSeek");
+               String path = file.getPath();
+               String contents = "0123456789" + LangUtil.EOL;
+               contents += contents;
+               FileUtil.writeAsString(file, contents);
+               tempFiles.add(file);
+               List<String> sourceList = new ArrayList<String>();
+               sourceList.add(file.getPath());
+
+               final ArrayList<String> errors = new ArrayList<String>();
+               final PrintStream errorSink = new PrintStream(System.err, true) {
+                       public void println(String error) {
+                               errors.add(error);
+                       }
+               };
+               for (int i = 0; i < 10; i++) {
+                       List<String> result = FileUtil.lineSeek("" + i, sourceList, true, errorSink);
+                       assertEquals(2, result.size());
+                       assertEquals(path + ":1:" + i, result.get(0));
+                       assertEquals(path + ":2:" + i, result.get(1));
+                       if (!LangUtil.isEmpty(errors)) { // XXX prefer fast-fail?
+                               assertTrue("errors: " + errors, false);
+                       }
+               }
+
+       }
+
+       public void testLineSeekMore() {
+               final int MAX = 3; // 1..10
+               File tempDir = FileUtil.getTempDir("testLineSeekMore");
+               tempFiles.add(tempDir);
+               final String prefix = new File(tempDir, "testLineSeek").getPath();
+               // setup files 0..MAX with 2*MAX lines
+               String[] sources = new String[MAX];
+               StringBuffer sb = new StringBuffer();
+               for (int i = 0; i < sources.length; i++) {
+                       sources[i] = new File(prefix + i).getPath();
+                       sb.append("not matched");
+                       sb.append(LangUtil.EOL);
+                       sb.append("0123456789");
+                       sb.append(LangUtil.EOL);
+               }
+               final String contents = sb.toString();
+               for (int i = 0; i < sources.length; i++) {
+                       File file = new File(sources[i]);
+                       FileUtil.writeAsString(file, contents);
+                       tempFiles.add(file);
+               }
+               // now test
+               final ArrayList<String> errors = new ArrayList<>();
+               final PrintStream errorSink = new PrintStream(System.err, true) {
+                       public void println(String error) {
+                               errors.add(error);
+                       }
+               };
+               List<String> sourceList = new ArrayList<>();
+               sourceList.addAll(Arrays.asList(sources));
+               sourceList = Collections.unmodifiableList(sourceList);
+               for (int k = 0; k < sources.length; k++) {
+                       List<String> result = FileUtil.lineSeek("" + k, sourceList, true, errorSink);
+                       // number k found in every other line of every file at index k
+                       Iterator<String> iter = result.iterator();
+                       for (int i = 0; i < MAX; i++) { // for each file
+                               for (int j = 1; j < (MAX + 1); j++) { // for every other line
+                                       assertTrue(iter.hasNext());
+                                       assertEquals(prefix + i + ":" + 2 * j + ":" + k, iter.next());
+                               }
+                       }
+                       if (!LangUtil.isEmpty(errors)) { // XXX prefer fast-fail?
+                               assertTrue("errors: " + errors, false);
+                       }
+               }
+       }
+
+       public void testDirCopyNoSubdirs() throws IOException {
+               String[] srcFiles = new String[] { "one.java", "two.java", "three.java" };
+               String[] destFiles = new String[] { "three.java", "four.java", "five.java" };
+               String[] allFiles = new String[] { "one.java", "two.java", "three.java", "four.java", "five.java" };
+               File srcDir = makeTempDir("FileUtilUT_srcDir", srcFiles);
+               File destDir = makeTempDir("FileUtilUT_destDir", destFiles);
+               assertTrue(null != srcDir);
+               assertTrue(null != destDir);
+               assertTrue(NONE == dirContains(srcDir, srcFiles));
+               assertTrue(NONE == dirContains(destDir, destFiles));
+
+               FileUtil.copyDir(srcDir, destDir);
+               String[] resultOne = dirContains(destDir, allFiles);
+               FileUtil.copyDir(srcDir, destDir);
+               String[] resultTwo = dirContains(destDir, allFiles);
+
+               assertTrue(NONE == resultOne);
+               assertTrue(NONE == resultTwo);
+       }
+
+       public void testDirCopyNoSubdirsWithSuffixes() throws IOException {
+               String[] srcFiles = new String[] { "one.java", "two.java", "three.java" };
+               String[] destFiles = new String[] { "three.java", "four.java", "five.java" };
+               String[] allFiles = new String[] { "one.aj", "two.aj", "three.aj", "three.java", "four.java", "five.java" };
+               File srcDir = makeTempDir("FileUtilUT_srcDir", srcFiles);
+               File destDir = makeTempDir("FileUtilUT_destDir", destFiles);
+               assertTrue(null != srcDir);
+               assertTrue(null != destDir);
+               assertTrue(NONE == dirContains(srcDir, srcFiles));
+               assertTrue(NONE == dirContains(destDir, destFiles));
+
+               FileUtil.copyDir(srcDir, destDir, ".java", ".aj");
+               FileUtil.copyDir(srcDir, destDir, ".java", ".aj");
+
+               assertTrue(NONE == dirContains(destDir, allFiles));
+               assertTrue(NONE == dirContains(destDir, allFiles));
+       }
+
+       public void testDirCopySubdirsSuffixRoundTrip() throws IOException {
+               final File srcDir = new File("src");
+               final File one = FileUtil.getTempDir("testDirCopySubdirsSuffixRoundTrip_1");
+               final File two = FileUtil.getTempDir("testDirCopySubdirsSuffixRoundTrip_2");
+               FileUtil.copyDir(srcDir, one); // no selection
+               FileUtil.copyDir(two, one, ".java", ".aj"); // only .java files
+               FileUtil.copyDir(one, two, ".aj", ".java");
+
+               FileUtil.deleteContents(one);
+               one.delete();
+               FileUtil.deleteContents(two);
+               two.delete();
+       }
+
+       /**
+        * Create temp dir at loc containing temp files files. Result is registered for deletion on cleanup.
+        */
+       File makeTempDir(String loc, String[] filenames) throws IOException {
+               File d = new File(loc);
+               d.mkdirs();
+               assertTrue(d.exists());
+               tempFiles.add(d);
+               assertTrue(d.canWrite());
+               for (int i = 0; i < filenames.length; i++) {
+                       File f = new File(d, filenames[i]);
+                       assertTrue(filenames[i], f.createNewFile());
+               }
+               return d;
+       }
+
+       public void testPipeEmpty() {
+               checkPipe("");
+       }
+
+       public void testPipeMin() {
+               checkPipe("0");
+       }
+
+       public void testPipe() {
+               String str = "The quick brown fox jumped over the lazy dog";
+               StringBuffer sb = new StringBuffer();
+               for (int i = 0; i < 4096; i++) {
+                       sb.append(str);
+               }
+               checkPipe(sb.toString());
+       }
+
+       void checkPipe(String data) {
+               StringBufferInputStream in = new StringBufferInputStream(data);
+               ByteArrayOutputStream out = new ByteArrayOutputStream();
+               FileUtil.Pipe pipe = new FileUtil.Pipe(in, out, 100l, true, true);
+               pipe.run();
+               assertTrue(data.equals(out.toString()));
+               assertTrue(null == pipe.getThrown());
+               assertEquals("totalWritten", data.length(), pipe.totalWritten());
+       }
+
+       public void testPipeThrown() {
+               final String data = "The quick brown fox jumped over the lazy dog";
+               final IOException thrown = new IOException("test");
+               StringBufferInputStream in = new StringBufferInputStream(data);
+               OutputStream out = new OutputStream() {
+                       public void write(int b) throws IOException {
+                               throw thrown;
+                       }
+               };
+
+               FileUtil.Pipe pipe = new FileUtil.Pipe(in, out, 100l, true, true);
+               pipe.run();
+               assertEquals("totalWritten", 0, pipe.totalWritten());
+               assertTrue(thrown == pipe.getThrown());
+       }
+
+       public void xtestPipeHalt() { // this test periodically fails on the build machine -
+                                                                       // disabling till we have time to figure out why
+               final long MAX = 1000000;
+               InputStream in = new InputStream() {
+                       long max = 0;
+
+                       public int read() throws IOException {
+                               if (max++ > MAX) {
+                                       throw new IOException("test failed");
+                               }
+                               return 1;
+                       }
+
+               };
+               final int minWritten = 20;
+               class Flag {
+                       boolean hit;
+               }
+               final Flag flag = new Flag();
+               OutputStream out = new OutputStream() {
+                       long max = 0;
+
+                       public void write(int b) throws IOException {
+                               if (max++ > MAX) {
+                                       throw new IOException("test failed");
+                               } else if (max > minWritten) {
+                                       if (!flag.hit) {
+                                               flag.hit = true;
+                                       }
+                               }
+                       }
+               };
+               class Result {
+                       long totalWritten;
+                       Throwable thrown;
+                       boolean set;
+               }
+               final Result result = new Result();
+               FileUtil.Pipe pipe = new FileUtil.Pipe(in, out, 100l, true, true) {
+                       protected void completing(long totalWritten, Throwable thrown) {
+                               result.totalWritten = totalWritten;
+                               result.thrown = thrown;
+                               result.set = true;
+                       }
+               };
+               // start it up
+               new Thread(pipe).start();
+               // wait for minWritten input
+               while (!flag.hit) {
+                       try {
+                               Thread.sleep(5l);
+                       } catch (InterruptedException e) {
+                               // ignore
+                       }
+               }
+               // halt
+               assertTrue(pipe.halt(true, true));
+               assertTrue(result.set);
+               assertTrue("Expected null but result.thrown = " + result.thrown, null == result.thrown);
+               assertTrue(null == pipe.getThrown());
+               assertEquals("total written", result.totalWritten, pipe.totalWritten());
+               if (minWritten > pipe.totalWritten()) {
+                       assertTrue("written: " + pipe.totalWritten(), false);
+               }
+       }
+
+}
diff --git a/util/src/test/java/org/aspectj/util/GenericSignatureParserTest.java b/util/src/test/java/org/aspectj/util/GenericSignatureParserTest.java
new file mode 100644 (file)
index 0000000..5a9e083
--- /dev/null
@@ -0,0 +1,233 @@
+/* *******************************************************************
+ * Copyright (c) 2005-2008 Contributors
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/epl-v10.html 
+ *
+ * ******************************************************************/
+
+package org.aspectj.util;
+
+import junit.framework.TestCase;
+
+import org.aspectj.util.GenericSignature.ClassSignature;
+import org.aspectj.util.GenericSignature.ClassTypeSignature;
+import org.aspectj.util.GenericSignature.FieldTypeSignature;
+import org.aspectj.util.GenericSignature.SimpleClassTypeSignature;
+
+/**
+ * @author Adrian Colyer
+ * @author Andy Clement
+ */
+public class GenericSignatureParserTest extends TestCase {
+
+       GenericSignatureParser parser;
+
+       public void testSimpleTokenize() {
+               String[] tokens = parser.tokenize("Ljava/lang/String;");
+               assertEquals(new String[] { "Ljava", "/", "lang", "/", "String", ";" }, tokens);
+       }
+
+       public void testTokenizeWithWildTypeArguments() {
+               String[] tokens = parser.tokenize("Ljava/lang/String<*>;");
+               assertEquals(new String[] { "Ljava", "/", "lang", "/", "String", "<", "*", ">", ";" }, tokens);
+       }
+
+       public void testTokenizeWithExtendsTypeArguments() {
+               String[] tokens = parser.tokenize("Ljava/util/List<+TE>;");
+               assertEquals(new String[] { "Ljava", "/", "util", "/", "List", "<", "+", "TE", ">", ";" }, tokens);
+       }
+
+       public void testTokenizeWithSuperTypeArguments() {
+               String[] tokens = parser.tokenize("Ljava/util/List<-TE>;");
+               assertEquals(new String[] { "Ljava", "/", "util", "/", "List", "<", "-", "TE", ">", ";" }, tokens);
+       }
+
+       public void testTokenizeArrayType() {
+               String[] tokens = parser.tokenize("[Ljava/lang/String;");
+               assertEquals(new String[] { "[", "Ljava", "/", "lang", "/", "String", ";" }, tokens);
+       }
+
+       public void testTokenizeFormalTypeParameters() {
+               String[] tokens = parser.tokenize("<T:Ljava/lang/String;:Ljava/util/Comparable;>");
+               assertEquals(new String[] { "<", "T", ":", "Ljava", "/", "lang", "/", "String", ";", ":", "Ljava", "/", "util", "/",
+                               "Comparable", ";", ">" }, tokens);
+       }
+
+       public void testParseClassSignatureSimple() {
+               ClassSignature sig = parser.parseAsClassSignature("Ljava/lang/String;");
+               assertEquals("No type parameters", 0, sig.formalTypeParameters.length);
+               assertEquals("No superinterfaces", 0, sig.superInterfaceSignatures.length);
+               assertEquals("Ljava/lang/String;", sig.superclassSignature.classSignature);
+               SimpleClassTypeSignature outerType = sig.superclassSignature.outerType;
+               assertEquals("Ljava/lang/String;", outerType.identifier);
+               assertEquals("No type args", 0, outerType.typeArguments.length);
+       }
+
+       public void testParseClassSignatureTypeArgs() {
+               ClassSignature sig = parser.parseAsClassSignature("Ljava/util/List<+Ljava/lang/String;>;");
+               assertEquals("No type parameters", 0, sig.formalTypeParameters.length);
+               assertEquals("No superinterfaces", 0, sig.superInterfaceSignatures.length);
+               assertEquals("Ljava/util/List<+Ljava/lang/String;>;", sig.superclassSignature.classSignature);
+               SimpleClassTypeSignature outerType = sig.superclassSignature.outerType;
+               assertEquals("Ljava/util/List", outerType.identifier);
+               assertEquals("One type arg", 1, outerType.typeArguments.length);
+               assertTrue(outerType.typeArguments[0].isPlus);
+               assertEquals("+Ljava/lang/String;", outerType.typeArguments[0].toString());
+       }
+
+       public void testParseClassSignatureTheFullMonty() {
+               ClassSignature sig = parser
+                               .parseAsClassSignature("<E:Ljava/lang/String;:Ljava/lang/Number<TE;>;>Ljava/util/List<TE;>;Ljava/util/Comparable<-TE;>;");
+               assertEquals("1 formal parameter", 1, sig.formalTypeParameters.length);
+               assertEquals("E", sig.formalTypeParameters[0].identifier);
+               ClassTypeSignature fsig = (ClassTypeSignature) sig.formalTypeParameters[0].classBound;
+               assertEquals("Ljava/lang/String;", fsig.classSignature);
+               assertEquals("1 interface bound", 1, sig.formalTypeParameters[0].interfaceBounds.length);
+               ClassTypeSignature isig = (ClassTypeSignature) sig.formalTypeParameters[0].interfaceBounds[0];
+               assertEquals("Ljava/lang/Number<TE;>;", isig.classSignature);
+               assertEquals("Ljava/util/List<TE;>;", sig.superclassSignature.classSignature);
+               assertEquals("1 type argument", 1, sig.superclassSignature.outerType.typeArguments.length);
+               assertEquals("TE;", sig.superclassSignature.outerType.typeArguments[0].toString());
+               assertEquals("1 super interface", 1, sig.superInterfaceSignatures.length);
+               assertEquals("Ljava/util/Comparable<-TE;>;", sig.superInterfaceSignatures[0].toString());
+       }
+
+       public void testFunky_406167() {
+               ClassSignature sig = parser
+                               .parseAsClassSignature("Lcom/google/android/gms/internal/hb<TT;>.com/google/android/gms/internal/hb$b<Ljava/lang/Boolean;>;");
+               // Note the package prefix on the nested types has been dropped
+               assertEquals("Lcom/google/android/gms/internal/hb<TT;>.hb$b<Ljava/lang/Boolean;>;",sig.superclassSignature.toString());
+               sig = parser
+                               .parseAsClassSignature("Lcom/a/a/b/t<TK;TV;>.com/a/a/b/af.com/a/a/b/ag;Ljava/util/ListIterator<TV;>;");
+               // Note the package prefix on the nested types has been dropped
+               assertEquals("Lcom/a/a/b/t<TK;TV;>.af.ag;",sig.superclassSignature.toString());
+               assertEquals("Ljava/util/ListIterator<TV;>;",sig.superInterfaceSignatures[0].toString());
+               sig = parser.parseAsClassSignature("Lcom/google/android/gms/internal/hb.com/google/android/gms/internal/hb$b<Ljava/lang/Boolean;>;");
+               // Note the package prefix on the nested types has been dropped
+               assertEquals("Lcom/google/android/gms/internal/hb.hb$b<Ljava/lang/Boolean;>;",sig.superclassSignature.toString());
+               sig = parser
+                               .parseAsClassSignature("Lcom/a/a/b/t.com/a/a/b/af.com/a/a/b/ag;Ljava/util/ListIterator<TV;>;");
+               // Note the package prefix on the nested types has been dropped
+               assertEquals("Lcom/a/a/b/t.af.ag;",sig.superclassSignature.toString());
+               assertEquals("Ljava/util/ListIterator<TV;>;",sig.superInterfaceSignatures[0].toString());
+       }
+
+
+       public void testFieldSignatureParsingClassType() {
+               FieldTypeSignature fsig = parser.parseAsFieldSignature("Ljava/lang/String;");
+               assertTrue("ClassTypeSignature", fsig instanceof ClassTypeSignature);
+               assertEquals("Ljava/lang/String;", fsig.toString());
+       }
+
+       public void testFieldSignatureParsingArrayType() {
+               FieldTypeSignature fsig = parser.parseAsFieldSignature("[Ljava/lang/String;");
+               assertTrue("ArrayTypeSignature", fsig instanceof GenericSignature.ArrayTypeSignature);
+               assertEquals("[Ljava/lang/String;", fsig.toString());
+       }
+
+       public void testFieldSignatureParsingTypeVariable() {
+               FieldTypeSignature fsig = parser.parseAsFieldSignature("TT;");
+               assertTrue("TypeVariableSignature", fsig instanceof GenericSignature.TypeVariableSignature);
+               assertEquals("TT;", fsig.toString());
+       }
+
+       public void testSimpleMethodSignatureParsing() {
+               GenericSignature.MethodTypeSignature mSig = parser.parseAsMethodSignature("()V");
+               assertEquals("No type parameters", 0, mSig.formalTypeParameters.length);
+               assertEquals("No parameters", 0, mSig.parameters.length);
+               assertEquals("Void return type", "V", mSig.returnType.toString());
+               assertEquals("No throws", 0, mSig.throwsSignatures.length);
+       }
+
+       public void testMethodSignatureTypeParams() {
+               GenericSignature.MethodTypeSignature mSig = parser.parseAsMethodSignature("<T:>(TT;)V");
+               assertEquals("One type parameter", 1, mSig.formalTypeParameters.length);
+               assertEquals("T", mSig.formalTypeParameters[0].identifier);
+               assertEquals("Ljava/lang/Object;", mSig.formalTypeParameters[0].classBound.toString());
+               assertEquals("One parameter", 1, mSig.parameters.length);
+               assertEquals("TT;", mSig.parameters[0].toString());
+               assertEquals("Void return type", "V", mSig.returnType.toString());
+               assertEquals("No throws", 0, mSig.throwsSignatures.length);
+       }
+
+       public void testMethodSignatureGenericReturn() {
+               GenericSignature.MethodTypeSignature mSig = parser.parseAsMethodSignature("<T:>()TT;");
+               assertEquals("One type parameter", 1, mSig.formalTypeParameters.length);
+               assertEquals("T", mSig.formalTypeParameters[0].identifier);
+               assertEquals("Ljava/lang/Object;", mSig.formalTypeParameters[0].classBound.toString());
+               assertEquals("No parameters", 0, mSig.parameters.length);
+               assertEquals("'T' return type", "TT;", mSig.returnType.toString());
+               assertEquals("No throws", 0, mSig.throwsSignatures.length);
+       }
+
+       public void testMethodSignatureThrows() {
+               GenericSignature.MethodTypeSignature mSig = parser
+                               .parseAsMethodSignature("<T:>(TT;)V^Ljava/lang/Exception;^Ljava/lang/RuntimeException;");
+               assertEquals("One type parameter", 1, mSig.formalTypeParameters.length);
+               assertEquals("T", mSig.formalTypeParameters[0].identifier);
+               assertEquals("Ljava/lang/Object;", mSig.formalTypeParameters[0].classBound.toString());
+               assertEquals("One parameter", 1, mSig.parameters.length);
+               assertEquals("TT;", mSig.parameters[0].toString());
+               assertEquals("Void return type", "V", mSig.returnType.toString());
+               assertEquals("2 throws", 2, mSig.throwsSignatures.length);
+               assertEquals("Ljava/lang/Exception;", mSig.throwsSignatures[0].toString());
+               assertEquals("Ljava/lang/RuntimeException;", mSig.throwsSignatures[1].toString());
+       }
+       
+       public void testMethodSignaturePrimitiveParams() {
+               GenericSignature.MethodTypeSignature mSig = parser.parseAsMethodSignature("(ILjava/lang/Object;)V");
+               assertEquals("2 parameters", 2, mSig.parameters.length);
+               assertEquals("I", mSig.parameters[0].toString());
+               assertEquals("Ljava/lang/Object;", mSig.parameters[1].toString());
+       }
+
+       public void testFullyQualifiedSuperclassAfterTypeParams() {
+               try {
+                       GenericSignature.FieldTypeSignature cSig = parser.parseAsFieldSignature("Ljava/util/List</;");
+                       fail("Expected IllegalStateException");
+               } catch (IllegalStateException ex) {
+                       assertTrue(ex.getMessage().indexOf("Ljava/util/List</;") != -1);
+               }
+       }
+
+       public void testPr107784() {
+               parser
+                               .parseAsMethodSignature("(Lcom/cibc/responders/mapping/CommonDataBeanScenario;Ljava/lang/Object;)Lcom/cibc/responders/response/Formatter<[BLjava/lang/Object;>;");
+               parser.parseAsClassSignature("<Parent:Ljava/lang/Object;Child:Ljava/lang/Object;>Ljava/lang/Object;");
+       }
+
+       private void assertEquals(String[] expected, String[] actual) {
+               if (actual.length != expected.length) {
+                       int shorter = Math.min(expected.length, actual.length);
+                       for (int i = 0; i < shorter; i++) {
+                               if (!actual[i].equals(expected[i])) {
+                                       fail("Expected " + expected[i] + " at position " + i + " but found " + actual[i]);
+                               }
+                       }
+                       fail("Expected " + expected.length + " tokens but got " + actual.length + tokensToString(actual));
+               }
+               for (int i = 0; i < actual.length; i++) {
+                       if (!actual[i].equals(expected[i])) {
+                               fail("Expected " + expected[i] + " at position " + i + " but found " + actual[i]);
+                       }
+               }
+       }
+
+       private String tokensToString(String[] tokens) {
+               StringBuffer sb = new StringBuffer();
+               sb.append(tokens[0]);
+               for (int i = 1; i < tokens.length; i++) {
+                       sb.append(",");
+                       sb.append(tokens[i]);
+               }
+               return sb.toString();
+       }
+
+       protected void setUp() throws Exception {
+               super.setUp();
+               parser = new GenericSignatureParser();
+       }
+}
diff --git a/util/src/test/java/org/aspectj/util/LangUtilTest.java b/util/src/test/java/org/aspectj/util/LangUtilTest.java
new file mode 100644 (file)
index 0000000..4cb4479
--- /dev/null
@@ -0,0 +1,270 @@
+/* *******************************************************************
+ * Copyright (c) 1999-2001 Xerox Corporation, 
+ *               2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved. 
+ * This program and the accompanying materials are made available 
+ * under the terms of the Eclipse Public License v1.0 
+ * which accompanies this distribution and is available at 
+ * http://www.eclipse.org/legal/epl-v10.html 
+ *  
+ * Contributors: 
+ *     Xerox/PARC     initial implementation 
+ * ******************************************************************/
+
+package org.aspectj.util;
+
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * 
+ */
+public class LangUtilTest extends TestCase {
+
+       public LangUtilTest(String name) {
+               super(name);
+       }
+
+       // /** @see LangUtil.extractOptions(String[], String[], int[], List) */
+       // public void testExtractOptions() {
+       // ArrayList extracted = new ArrayList();
+       // String[] args = new String[] { "-d", "classes", "-classpath", "foo.jar", "-verbose", "Bar.java" };
+       // String[] validOptions = new String[] { "-classpath", "-d", "-verbose", "-help" };
+       // int[] optionArgs = new int[] { 1, 1, 0, 0 };
+       // String[] result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted);
+       // String resultString = "" + Arrays.asList(result);
+       // String EXP = "[Bar.java]";
+       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+       // EXP = "[-d, classes, -classpath, foo.jar, -verbose]";
+       // resultString = "" + extracted;
+       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+       //        
+       // // no input, no output
+       // extracted.clear();
+       // args = new String[] {};
+       // result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted);
+       // resultString = "" + Arrays.asList(result);
+       // EXP = "[]";
+       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+       // resultString = "" + extracted;
+       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+       //        
+       // // one input, nothing extracted
+       // extracted.clear();
+       // args = new String[] {"Bar.java"};
+       // result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted);
+       // resultString = "" + Arrays.asList(result);
+       // EXP = "[Bar.java]";
+       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+       // EXP = "[]";
+       // resultString = "" + extracted;
+       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+       //        
+       // // one input, extracted
+       // extracted.clear();
+       // args = new String[] {"-verbose"};
+       // result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted);
+       // resultString = "" + Arrays.asList(result);
+       // EXP = "[]";
+       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+       // EXP = "[-verbose]";
+       // resultString = "" + extracted;
+       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+       //
+       // // ------- booleans
+       // validOptions = new String[] { "-help", "-verbose" };
+       // optionArgs = null;
+       //
+       // // one input, extracted
+       // extracted.clear();
+       // args = new String[] {"-verbose"};
+       // result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted);
+       // resultString = "" + Arrays.asList(result);
+       // EXP = "[]";
+       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+       // EXP = "[-verbose]";
+       // resultString = "" + extracted;
+       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+       //        
+       // // one input, not extracted
+       // extracted.clear();
+       // args = new String[] {"Bar.java"};
+       // result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted);
+       // resultString = "" + Arrays.asList(result);
+       // EXP = "[Bar.java]";
+       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+       // EXP = "[]";
+       // resultString = "" + extracted;
+       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+       // }
+
+       public void testVersion() {
+               assertTrue(LangUtil.is13VMOrGreater()); // min vm now - floor may change
+               if (LangUtil.is15VMOrGreater()) {
+                       assertTrue(LangUtil.is14VMOrGreater());
+               }
+       }
+
+       /** @see LangUtil.extractOptions(String[], String[][]) */
+       public void testExtractOptionsArrayCollector() {
+               String[] args = new String[] { "-d", "classes", "-classpath", "foo.jar", "-verbose", "Bar.java" };
+               String[][] OPTIONS = new String[][] { new String[] { "-classpath", null }, new String[] { "-d", null },
+                               new String[] { "-verbose" }, new String[] { "-help" } };
+
+               String[][] options = LangUtil.copyStrings(OPTIONS);
+
+               String[] result = LangUtil.extractOptions(args, options);
+               String resultString = "" + Arrays.asList(result);
+               String EXP = "[Bar.java]";
+               assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+               assertTrue("-verbose".equals(options[2][0]));
+               assertTrue("foo.jar".equals(options[0][1]));
+               assertTrue("classes".equals(options[1][1]));
+               assertTrue("-classpath".equals(options[0][0]));
+               assertTrue("-d".equals(options[1][0]));
+               assertTrue(null == options[3][0]);
+
+               // get args back, no options set
+               args = new String[] { "Bar.java" };
+               options = LangUtil.copyStrings(OPTIONS);
+
+               result = LangUtil.extractOptions(args, options);
+               resultString = "" + Arrays.asList(result);
+               EXP = "[Bar.java]";
+               assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
+               assertTrue(null == options[0][0]);
+               assertTrue(null == options[1][0]);
+               assertTrue(null == options[2][0]);
+               assertTrue(null == options[3][0]);
+       }
+
+       // public void testOptionVariants() {
+       // String[] NONE = new String[0];
+       // String[] one = new String[] {"-1"};
+       // String[] two = new String[] {"-2"};
+       // String[] three= new String[] {"-3"};
+       // String[] both = new String[] {"-1", "-2" };
+       // String[] oneB = new String[] {"-1-"};
+       // String[] bothB = new String[] {"-1-", "-2-" };
+       // String[] onetwoB = new String[] {"-1", "-2-" };
+       // String[] oneBtwo = new String[] {"-1-", "-2" };
+       // String[] threeB = new String[] {"-1-", "-2-", "-3-"};
+       // String[] athreeB = new String[] {"a", "-1-", "-2-", "-3-"};
+       // String[] threeaB = new String[] {"-1-", "a", "-2-", "-3-"};
+       //        
+       // checkOptionVariants(NONE, new String[][] { NONE });
+       // checkOptionVariants(one, new String[][] { one });
+       // checkOptionVariants(both, new String[][] { both });
+       // checkOptionVariants(oneB, new String[][] { NONE, one });
+       // checkOptionVariants(bothB, new String[][] { NONE, one, new String[] {"-2"}, both });
+       // checkOptionVariants(onetwoB, new String[][] { one, new String[] {"-1", "-2"}});
+       // checkOptionVariants(oneBtwo, new String[][] { two, new String[] {"-1", "-2"}});
+       // checkOptionVariants(threeB, new String[][]
+       // {
+       // NONE,
+       // one,
+       // two,
+       // new String[] {"-1", "-2"},
+       // three,
+       // new String[] {"-1", "-3"},
+       // new String[] {"-2", "-3"},
+       // new String[] {"-1", "-2", "-3"}
+       // });
+       // checkOptionVariants(athreeB, new String[][]
+       // {
+       // new String[] {"a"},
+       // new String[] {"a", "-1"},
+       // new String[] {"a", "-2"},
+       // new String[] {"a", "-1", "-2"},
+       // new String[] {"a", "-3"},
+       // new String[] {"a", "-1", "-3"},
+       // new String[] {"a", "-2", "-3"},
+       // new String[] {"a", "-1", "-2", "-3"}
+       // });
+       // checkOptionVariants(threeaB, new String[][]
+       // {
+       // new String[] {"a"},
+       // new String[] {"-1", "a"},
+       // new String[] {"a", "-2"},
+       // new String[] {"-1", "a", "-2"},
+       // new String[] {"a", "-3"},
+       // new String[] {"-1", "a", "-3"},
+       // new String[] {"a", "-2", "-3"},
+       // new String[] {"-1", "a", "-2", "-3"}
+       // });
+       // }
+
+       // void checkOptionVariants(String[] options, String[][] expected) {
+       // String[][] result = LangUtil.optionVariants(options);
+       // if (expected.length != result.length) {
+       // assertTrue("exp=" + expected.length + " actual=" + result.length, false);
+       // }
+       // for (int i = 0; i < expected.length; i++) {
+       // assertEquals(""+i,
+       // "" + Arrays.asList(expected[i]),
+       // "" + Arrays.asList(result[i]));
+       // }
+       // }
+
+       /** @see XMLWriterTest#testUnflattenList() */
+       public void testCommaSplit() {
+               checkCommaSplit("", new String[] { "" });
+               checkCommaSplit("1", new String[] { "1" });
+               checkCommaSplit(" 1 2 ", new String[] { "1 2" });
+               checkCommaSplit(" 1 , 2 ", new String[] { "1", "2" });
+               checkCommaSplit("1,2,3,4", new String[] { "1", "2", "3", "4" });
+       }
+
+       void checkCommaSplit(String input, String[] expected) {
+               List actual = LangUtil.commaSplit(input);
+               String a = "" + actual;
+               String e = "" + Arrays.asList(expected);
+               assertTrue(e + "==" + a, e.equals(a));
+       }
+
+       public void testElideEndingLines() {
+               StringBuffer stackBuffer = LangUtil.stackToString(new RuntimeException(""), true);
+               LangUtil.elideEndingLines(LangUtil.StringChecker.TEST_PACKAGES, stackBuffer, 10);
+               String result = stackBuffer.toString();
+
+               if (-1 == result.indexOf("(... ")) {
+                       // brittle - will fail under different top-level drivers
+                       String m = "when running under eclipse or Ant, expecting (... in trace: ";
+                       assertTrue(m + result, false);
+               }
+
+               stackBuffer = new StringBuffer(
+                               "java.lang.RuntimeException: unimplemented"
+                                               + "\n at org.aspectj.ajdt.internal.core.builder.EclipseUnwovenClassFile.writeWovenBytes(EclipseUnwovenClassFile.java:59)"
+                                               + "\n at org.aspectj.weaver.bcel.BcelWeaver.dump(BcelWeaver.java:271)"
+                                               + "\n at org.aspectj.weaver.bcel.BcelWeaver.weave(BcelWeaver.java:233)"
+                                               + "\n at org.aspectj.weaver.bcel.BcelWeaver.weave(BcelWeaver.java:198)"
+                                               + "\n at org.aspectj.ajdt.internal.core.builder.AjBuildManager.weaveAndGenerateClassFiles(AjBuildanager.java:230)"
+                                               + "\n at org.aspectj.ajdt.internal.core.builder.AjBuildManager.batchBuild(AjBuildManager.java:50)"
+                                               + "\n at org.aspectj.ajdt.ajc.AjdtCommand.runCommand(AjdtCommand.java:42)"
+                                               + "\n at org.aspectj.testing.harness.bridge.CompilerRun.run(CompilerRun.java:222)"
+                                               + "\n at org.aspectj.testing.run.Runner.runPrivate(Runner.java:363)"
+                                               + "\n at org.aspectj.testing.run.Runner.runChild(Runner.java:167)"
+                                               + "\n at org.aspectj.testing.run.Runner.runChild(Runner.java:126)"
+                                               + "\n at org.aspectj.testing.run.Runner$IteratorWrapper.run(Runner.java:441)"
+                                               + "\n at org.aspectj.testing.run.Runner.runPrivate(Runner.java:363)"
+                                               + "\n at org.aspectj.testing.run.Runner.runChild(Runner.java:167)"
+                                               + "\n at org.aspectj.testing.run.Runner.runChild(Runner.java:126)"
+                                               + "\n at org.aspectj.testing.run.Runner$IteratorWrapper.run(Runner.java:441)"
+                                               + "\n at org.aspectj.testing.run.Runner.runPrivate(Runner.java:363)"
+                                               + "\n at org.aspectj.testing.run.Runner.run(Runner.java:114)"
+                                               + "\n at org.aspectj.testing.run.Runner.run(Runner.java:105)"
+                                               + "\n at org.aspectj.testing.run.Runner.runIterator(Runner.java:228)"
+                                               + "\n at org.aspectj.testing.drivers.Harness.run(Harness.java:254)"
+                                               + "\n at org.aspectj.testing.drivers.Harness.runMain(Harness.java:217)"
+                                               + "\n at org.aspectj.testing.drivers.Harness.main(Harness.java:99)"
+                                               + "\n at org.aspectj.testing.Harness.main(Harness.java:37)" + "\n clip me");
+
+               LangUtil.elideEndingLines(LangUtil.StringChecker.TEST_PACKAGES, stackBuffer, 25);
+               result = stackBuffer.toString();
+               assertTrue(result, -1 != result.indexOf("(... "));
+               assertTrue(result, -1 == result.indexOf("org.aspectj.testing"));
+       }
+}
diff --git a/util/testsrc/org/aspectj/util/FileUtilTest.java b/util/testsrc/org/aspectj/util/FileUtilTest.java
deleted file mode 100644 (file)
index 8ce18c7..0000000
+++ /dev/null
@@ -1,707 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 1999-2001 Xerox Corporation, 
- *               2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *     Xerox/PARC     initial implementation 
- * ******************************************************************/
-
-package org.aspectj.util;
-
-//import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.io.StringBufferInputStream;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.TestCase;
-import junit.textui.TestRunner;
-
-/**
- * 
- */
-public class FileUtilTest extends TestCase {
-       public static final String[] NONE = new String[0];
-       public static boolean log = false;
-
-       public static void main(String[] args) {
-               TestRunner.main(new String[] { "org.aspectj.util.FileUtilTest" });
-       }
-
-       public static void assertSame(String prefix, String[] lhs, String[] rhs) { // XXX cheap diff
-               String srcPaths = LangUtil.arrayAsList(lhs).toString();
-               String destPaths = LangUtil.arrayAsList(rhs).toString();
-               if (!srcPaths.equals(destPaths)) {
-                       log("expected: " + srcPaths);
-                       log("  actual: " + destPaths);
-                       assertTrue(prefix + " expected=" + srcPaths + " != actual=" + destPaths, false);
-               }
-       }
-
-       /**
-        * Verify that dir contains files with names, and return the names of other files in dir.
-        * 
-        * @return the contents of dir after excluding files or NONE if none
-        * @throws AssertionFailedError if any names are not in dir
-        */
-       public static String[] dirContains(File dir, final String[] filenames) {
-               final ArrayList<String> sought = new ArrayList<>(LangUtil.arrayAsList(filenames));
-               FilenameFilter filter = new FilenameFilter() {
-                       public boolean accept(File d, String name) {
-                               return !sought.remove(name);
-                       }
-               };
-               // remove any found from sought and return remainder
-               String[] found = dir.list(filter);
-               if (0 < sought.size()) {
-                       assertTrue("found " + LangUtil.arrayAsList(dir.list()).toString() + " expected " + sought, false);
-               }
-               return (found.length == 0 ? NONE : found);
-       }
-
-       /** @return sorted String[] of all paths to all files/dirs under dir */
-       public static String[] dirPaths(File dir) {
-               return dirPaths(dir, new String[0]);
-       }
-
-       /**
-        * Get a sorted String[] of all paths to all files/dirs under dir. Files with names starting with "." are ignored, as are
-        * directory paths containing "CVS". The directory prefix of the path is stripped. Thus, given directory:
-        * 
-        * <pre>
-        * path/to
-        *   .cvsignore
-        *   CVS/
-        *     Root
-        *     Repository
-        *   Base.java
-        *   com/
-        *     parc/
-        *       messages.properties
-        *   org/
-        *     aspectj/
-        *       Version.java
-        * </pre>
-        * 
-        * a call
-        * 
-        * <pre>
-        * dirPaths(new File(&quot;path/to&quot;), new String[0]);
-        * </pre>
-        * 
-        * returns
-        * 
-        * <pre>
-        * { &quot;Base.java&quot;, &quot;com/parc/messages.properties&quot;, &quot;org/aspectj/Version.java&quot; }
-        * </pre>
-        * 
-        * while the call
-        * 
-        * <pre>
-        * dirPaths(new File(&quot;path/to&quot;), new String[] { &quot;.java&quot; });
-        * </pre>
-        * 
-        * returns
-        * 
-        * <pre>
-        * { &quot;Base.java&quot;, &quot;org/aspectj/Version.java&quot; }
-        * </pre>
-        * 
-        * @param dir the File path to the directory to inspect
-        * @param suffixes if not empty, require files returned to have this suffix
-        * @return sorted String[] of all paths to all files under dir ending with one of the listed suffixes but not starting with "."
-        */
-       public static String[] dirPaths(File dir, String[] suffixes) {
-               ArrayList<String> result = new ArrayList<String>();
-               doDirPaths(dir, result);
-               // if suffixes required, remove those without suffixes
-               if (!LangUtil.isEmpty(suffixes)) {
-                       for (ListIterator<String> iter = result.listIterator(); iter.hasNext();) {
-                               String path = iter.next().toString();
-                               boolean hasSuffix = false;
-                               for (int i = 0; !hasSuffix && (i < suffixes.length); i++) {
-                                       hasSuffix = path.endsWith(suffixes[i]);
-                               }
-                               if (!hasSuffix) {
-                                       iter.remove();
-                               }
-                       }
-               }
-               Collections.sort(result);
-               // trim prefix
-               final String prefix = dir.getPath();
-               final int len = prefix.length() + 1; // plus directory separator
-               String[] ra = (String[]) result.toArray(new String[0]);
-               for (int i = 0; i < ra.length; i++) {
-                       // assertTrue(ra[i].startsWith(prefix));
-                       assertTrue(ra[i], ra[i].length() > len);
-                       ra[i] = ra[i].substring(len);
-               }
-               return ra;
-       }
-
-       /**
-        * @param dir the File to read - ignored if null, not a directory, or has "CVS" in its path
-        * @param useSuffix if true, then use dir as suffix to path
-        */
-       private static void doDirPaths(File dir, ArrayList<String> paths) {
-               if ((null == dir) || !dir.canRead() || (-1 != dir.getPath().indexOf("CVS"))) {
-                       return;
-               }
-               File[] files = dir.listFiles();
-               for (int i = 0; i < files.length; i++) {
-                       String path = files[i].getPath();
-                       if (!files[i].getName().startsWith(".")) {
-                               if (files[i].isFile()) {
-                                       paths.add(path);
-                               } else if (files[i].isDirectory()) {
-                                       doDirPaths(files[i], paths);
-                               } else {
-                                       log("not file or dir: " + dir + "/" + path);
-                               }
-                       }
-               }
-       }
-
-       /** Print s if logging is enabled */
-       private static void log(String s) {
-               if (log) {
-                       System.err.println(s);
-               }
-       }
-
-       /** List of File files or directories to delete when exiting */
-       final ArrayList<File> tempFiles;
-
-       public FileUtilTest(String s) {
-               super(s);
-               tempFiles = new ArrayList<File>();
-       }
-
-       public void tearDown() {
-               for (ListIterator<File> iter = tempFiles.listIterator(); iter.hasNext();) {
-                       File dir = (File) iter.next();
-                       log("removing " + dir);
-                       FileUtil.deleteContents(dir);
-                       dir.delete();
-                       iter.remove();
-               }
-       }
-
-       public void testNotIsFileIsDirectory() {
-               File noSuchFile = new File("foo");
-               assertTrue(!noSuchFile.isFile());
-               assertTrue(!noSuchFile.isDirectory());
-       }
-
-       public void testGetBestFile() {
-               assertNull(FileUtil.getBestFile((String[]) null));
-               assertNull(FileUtil.getBestFile(new String[0]));
-               assertNull(FileUtil.getBestFile(new String[] { "!" }));
-               File f = FileUtil.getBestFile(new String[] { "." });
-               assertNotNull(f);
-               f = FileUtil.getBestFile(new String[] { "!", "." });
-               assertNotNull(f);
-               assertTrue(f.canRead());
-               boolean setProperty = false;
-               try {
-                       System.setProperty("bestfile", ".");
-                       setProperty = true;
-               } catch (Throwable t) {
-                       // ignore Security, etc.
-               }
-               if (setProperty) {
-                       f = FileUtil.getBestFile(new String[] { "sp:bestfile" });
-                       assertNotNull(f);
-                       assertTrue(f.canRead());
-               }
-       }
-
-       public void testCopyFiles() {
-               // bad input
-               Class<?> iaxClass = IllegalArgumentException.class;
-
-               checkCopyFiles(null, null, iaxClass, false);
-
-               File noSuchFile = new File("foo");
-               checkCopyFiles(noSuchFile, null, iaxClass, false);
-               checkCopyFiles(noSuchFile, noSuchFile, iaxClass, false);
-
-               File tempDir = FileUtil.getTempDir("testCopyFiles");
-               tempFiles.add(tempDir);
-               File fromFile = new File(tempDir, "fromFile");
-               String err = FileUtil.writeAsString(fromFile, "contents of from file");
-               assertTrue(err, null == err);
-               checkCopyFiles(fromFile, null, iaxClass, false);
-               checkCopyFiles(fromFile, fromFile, iaxClass, false);
-
-               // file-file
-               File toFile = new File(tempDir, "toFile");
-               checkCopyFiles(fromFile, toFile, null, true);
-
-               // file-dir
-               File toDir = new File(tempDir, "toDir");
-               assertTrue(toDir.mkdirs());
-               checkCopyFiles(fromFile, toDir, null, true);
-
-               // dir-dir
-               File fromDir = new File(tempDir, "fromDir");
-               assertTrue(fromDir.mkdirs());
-               checkCopyFiles(fromFile, fromDir, null, false);
-               File toFile2 = new File(fromDir, "toFile2");
-               checkCopyFiles(fromFile, toFile2, null, false);
-               checkCopyFiles(fromDir, toDir, null, true);
-       }
-
-       void checkCopyFiles(File from, File to, Class exceptionClass, boolean clean) {
-               try {
-                       FileUtil.copyFile(from, to);
-                       assertTrue(null == exceptionClass);
-                       if (to.isFile()) {
-                               assertTrue(from.length() == to.length()); // XXX cheap test
-                       } else if (!from.isDirectory()) {
-                               File toFile = new File(to, from.getName());
-                               assertTrue(from.length() == toFile.length());
-                       } else {
-                               // from is a dir and to is a dir, toDir should be created, and have the
-                               // same contents as fromDir.
-                               assertTrue(to.exists());
-                               assertTrue(from.listFiles().length == to.listFiles().length);
-                       }
-               } catch (Throwable t) {
-                       assertTrue(null != exceptionClass);
-                       assertTrue(exceptionClass.isAssignableFrom(t.getClass()));
-               } finally {
-                       if (clean && (null != to) && (to.exists())) {
-                               if (to.isDirectory()) {
-                                       FileUtil.deleteContents(to);
-                               }
-                               to.delete();
-                       }
-               }
-       }
-
-       public void testDirCopySubdirs() throws IOException {
-               File srcDir = new File("src");
-               File destDir = FileUtil.getTempDir("testDirCopySubdirs");
-               tempFiles.add(destDir);
-               FileUtil.copyDir(srcDir, destDir);
-               assertSame("testDirCopySubdirs", dirPaths(srcDir), dirPaths(destDir));
-       }
-
-       public void testDirCopySubdirsSuffix() throws IOException {
-               File srcDir = new File("src");
-               File destDir = FileUtil.getTempDir("testDirCopySubdirsSuffix");
-               tempFiles.add(destDir);
-               FileUtil.copyDir(srcDir, destDir, ".java", ".aj");
-
-               String[] sources = dirPaths(srcDir, new String[] { ".java" });
-               for (int i = 0; i < sources.length; i++) {
-                       sources[i] = sources[i].substring(0, sources[i].length() - 4);
-               }
-               String[] sinks = dirPaths(destDir, new String[] { ".aj" });
-               for (int i = 0; i < sinks.length; i++) {
-                       sinks[i] = sinks[i].substring(0, sinks[i].length() - 2);
-               }
-               assertSame("testDirCopySubdirs", sources, sinks);
-       }
-
-       public void testGetURL() {
-               String[] args = new String[] { ".", "../util/testdata", "../lib/test/aspectjrt.jar" };
-               for (int i = 0; i < args.length; i++) {
-                       checkGetURL(args[i]);
-               }
-       }
-
-       /**
-        * Method checkGetURL.
-        * 
-        * @param string
-        * @param uRL
-        */
-       private void checkGetURL(String arg) {
-               assertTrue(null != arg);
-               File f = new File(arg);
-               URL url = FileUtil.getFileURL(f);
-               assertTrue(null != url);
-               log("url       " + url);
-               if (!f.exists()) {
-                       log("not exist        " + f);
-               } else if (f.isDirectory()) {
-                       log("directory        " + f);
-               } else {
-                       log("     file        " + f);
-                       InputStream in = null;
-                       try {
-                               in = url.openStream();
-                       } catch (IOException e) {
-                               assertTrue("IOException: " + e, false);
-                       } finally {
-                               if (null != in) {
-                                       try {
-                                               in.close();
-                                       } catch (IOException e) {
-                                       }
-                               }
-                       }
-               }
-       }
-
-       public void testGetTempDir() {
-               boolean pass = true;
-               boolean delete = true;
-               checkGetTempDir("parent", null, pass, delete);
-               checkGetTempDir(null, "child", pass, delete);
-               tempFiles.add(checkGetTempDir("parent", "child", pass, !delete).getParentFile());
-               tempFiles.add(checkGetTempDir("parent", "child", pass, !delete).getParentFile());
-               tempFiles.add(checkGetTempDir("parent", "child", pass, !delete).getParentFile());
-       }
-
-       File checkGetTempDir(String parent, String child, boolean ok, boolean delete) {
-               File parentDir = FileUtil.getTempDir(parent);
-               assertTrue("unable to create " + parent, null != parentDir);
-               File dir = FileUtil.makeNewChildDir(parentDir, child);
-               log("parent=" + parent + " child=" + child + " -> " + dir);
-               assertTrue("dir: " + dir, ok == (dir.canWrite() && dir.isDirectory()));
-               if (delete) {
-                       dir.delete();
-                       parentDir.delete();
-               }
-               return dir;
-       }
-
-       public void testRandomFileString() {
-               ArrayList<String> results = new ArrayList<>();
-               for (int i = 0; i < 1000; i++) {
-                       String s = FileUtil.randomFileString();
-                       if (results.contains(s)) {
-                               log("warning: got duplicate at iteration " + i);
-                       }
-                       results.add(s);
-                       // System.err.print(" " + s);
-                       // if (0 == (i % 5)) {
-                       // System.err.println("");
-                       // }
-               }
-       }
-
-       public void testNormalizedPath() {
-               File tempFile = null;
-               try {
-                       tempFile = File.createTempFile("FileUtilTest_testNormalizedPath", "tmp");
-                       tempFiles.add(tempFile);
-               } catch (IOException e) {
-                       log("aborting test - unable to create temp file");
-                       return;
-               }
-               File parentDir = tempFile.getParentFile();
-               String tempFilePath = FileUtil.normalizedPath(tempFile, parentDir);
-               assertEquals(tempFile.getName(), tempFilePath);
-       }
-
-       public void testFileToClassName() {
-
-               File basedir = new File("/base/dir"); // never created
-               File classFile = new File(basedir, "foo/Bar.class");
-               assertEquals("foo.Bar", FileUtil.fileToClassName(basedir, classFile));
-
-               classFile = new File(basedir, "foo\\Bar.class");
-               assertEquals("foo.Bar", FileUtil.fileToClassName(basedir, classFile));
-
-               assertEquals("Bar", FileUtil.fileToClassName(null, classFile));
-
-               classFile = new File("/home/classes/org/aspectj/lang/JoinPoint.class");
-               assertEquals("org.aspectj.lang.JoinPoint", FileUtil.fileToClassName(null, classFile));
-
-               classFile = new File("/home/classes/com/sun/tools/Javac.class");
-               assertEquals("com.sun.tools.Javac", FileUtil.fileToClassName(null, classFile));
-       }
-
-       public void testDeleteContents() {
-               File tempDir = FileUtil.getTempDir("testDeleteContents");
-               tempFiles.add(tempDir);
-               File f = new File(tempDir, "foo");
-               f.mkdirs();
-               File g = new File(f, "bar");
-               g.mkdirs();
-               File h = new File(g, "bash");
-               h.mkdirs();
-               int d = FileUtil.deleteContents(f);
-               assertTrue(0 == d);
-               assertTrue(0 == f.list().length);
-               f.delete();
-               assertTrue(!f.exists());
-       }
-
-       public void testLineSeek() {
-               File tempDir = FileUtil.getTempDir("testLineSeek");
-               tempFiles.add(tempDir);
-               File file = new File(tempDir, "testLineSeek");
-               String path = file.getPath();
-               String contents = "0123456789" + LangUtil.EOL;
-               contents += contents;
-               FileUtil.writeAsString(file, contents);
-               tempFiles.add(file);
-               List<String> sourceList = new ArrayList<String>();
-               sourceList.add(file.getPath());
-
-               final ArrayList<String> errors = new ArrayList<String>();
-               final PrintStream errorSink = new PrintStream(System.err, true) {
-                       public void println(String error) {
-                               errors.add(error);
-                       }
-               };
-               for (int i = 0; i < 10; i++) {
-                       List<String> result = FileUtil.lineSeek("" + i, sourceList, true, errorSink);
-                       assertEquals(2, result.size());
-                       assertEquals(path + ":1:" + i, result.get(0));
-                       assertEquals(path + ":2:" + i, result.get(1));
-                       if (!LangUtil.isEmpty(errors)) { // XXX prefer fast-fail?
-                               assertTrue("errors: " + errors, false);
-                       }
-               }
-
-       }
-
-       public void testLineSeekMore() {
-               final int MAX = 3; // 1..10
-               File tempDir = FileUtil.getTempDir("testLineSeekMore");
-               tempFiles.add(tempDir);
-               final String prefix = new File(tempDir, "testLineSeek").getPath();
-               // setup files 0..MAX with 2*MAX lines
-               String[] sources = new String[MAX];
-               StringBuffer sb = new StringBuffer();
-               for (int i = 0; i < sources.length; i++) {
-                       sources[i] = new File(prefix + i).getPath();
-                       sb.append("not matched");
-                       sb.append(LangUtil.EOL);
-                       sb.append("0123456789");
-                       sb.append(LangUtil.EOL);
-               }
-               final String contents = sb.toString();
-               for (int i = 0; i < sources.length; i++) {
-                       File file = new File(sources[i]);
-                       FileUtil.writeAsString(file, contents);
-                       tempFiles.add(file);
-               }
-               // now test
-               final ArrayList<String> errors = new ArrayList<>();
-               final PrintStream errorSink = new PrintStream(System.err, true) {
-                       public void println(String error) {
-                               errors.add(error);
-                       }
-               };
-               List<String> sourceList = new ArrayList<>();
-               sourceList.addAll(Arrays.asList(sources));
-               sourceList = Collections.unmodifiableList(sourceList);
-               for (int k = 0; k < sources.length; k++) {
-                       List<String> result = FileUtil.lineSeek("" + k, sourceList, true, errorSink);
-                       // number k found in every other line of every file at index k
-                       Iterator<String> iter = result.iterator();
-                       for (int i = 0; i < MAX; i++) { // for each file
-                               for (int j = 1; j < (MAX + 1); j++) { // for every other line
-                                       assertTrue(iter.hasNext());
-                                       assertEquals(prefix + i + ":" + 2 * j + ":" + k, iter.next());
-                               }
-                       }
-                       if (!LangUtil.isEmpty(errors)) { // XXX prefer fast-fail?
-                               assertTrue("errors: " + errors, false);
-                       }
-               }
-       }
-
-       public void testDirCopyNoSubdirs() throws IOException {
-               String[] srcFiles = new String[] { "one.java", "two.java", "three.java" };
-               String[] destFiles = new String[] { "three.java", "four.java", "five.java" };
-               String[] allFiles = new String[] { "one.java", "two.java", "three.java", "four.java", "five.java" };
-               File srcDir = makeTempDir("FileUtilUT_srcDir", srcFiles);
-               File destDir = makeTempDir("FileUtilUT_destDir", destFiles);
-               assertTrue(null != srcDir);
-               assertTrue(null != destDir);
-               assertTrue(NONE == dirContains(srcDir, srcFiles));
-               assertTrue(NONE == dirContains(destDir, destFiles));
-
-               FileUtil.copyDir(srcDir, destDir);
-               String[] resultOne = dirContains(destDir, allFiles);
-               FileUtil.copyDir(srcDir, destDir);
-               String[] resultTwo = dirContains(destDir, allFiles);
-
-               assertTrue(NONE == resultOne);
-               assertTrue(NONE == resultTwo);
-       }
-
-       public void testDirCopyNoSubdirsWithSuffixes() throws IOException {
-               String[] srcFiles = new String[] { "one.java", "two.java", "three.java" };
-               String[] destFiles = new String[] { "three.java", "four.java", "five.java" };
-               String[] allFiles = new String[] { "one.aj", "two.aj", "three.aj", "three.java", "four.java", "five.java" };
-               File srcDir = makeTempDir("FileUtilUT_srcDir", srcFiles);
-               File destDir = makeTempDir("FileUtilUT_destDir", destFiles);
-               assertTrue(null != srcDir);
-               assertTrue(null != destDir);
-               assertTrue(NONE == dirContains(srcDir, srcFiles));
-               assertTrue(NONE == dirContains(destDir, destFiles));
-
-               FileUtil.copyDir(srcDir, destDir, ".java", ".aj");
-               FileUtil.copyDir(srcDir, destDir, ".java", ".aj");
-
-               assertTrue(NONE == dirContains(destDir, allFiles));
-               assertTrue(NONE == dirContains(destDir, allFiles));
-       }
-
-       public void testDirCopySubdirsSuffixRoundTrip() throws IOException {
-               final File srcDir = new File("src");
-               final File one = FileUtil.getTempDir("testDirCopySubdirsSuffixRoundTrip_1");
-               final File two = FileUtil.getTempDir("testDirCopySubdirsSuffixRoundTrip_2");
-               FileUtil.copyDir(srcDir, one); // no selection
-               FileUtil.copyDir(two, one, ".java", ".aj"); // only .java files
-               FileUtil.copyDir(one, two, ".aj", ".java");
-
-               FileUtil.deleteContents(one);
-               one.delete();
-               FileUtil.deleteContents(two);
-               two.delete();
-       }
-
-       /**
-        * Create temp dir at loc containing temp files files. Result is registered for deletion on cleanup.
-        */
-       File makeTempDir(String loc, String[] filenames) throws IOException {
-               File d = new File(loc);
-               d.mkdirs();
-               assertTrue(d.exists());
-               tempFiles.add(d);
-               assertTrue(d.canWrite());
-               for (int i = 0; i < filenames.length; i++) {
-                       File f = new File(d, filenames[i]);
-                       assertTrue(filenames[i], f.createNewFile());
-               }
-               return d;
-       }
-
-       public void testPipeEmpty() {
-               checkPipe("");
-       }
-
-       public void testPipeMin() {
-               checkPipe("0");
-       }
-
-       public void testPipe() {
-               String str = "The quick brown fox jumped over the lazy dog";
-               StringBuffer sb = new StringBuffer();
-               for (int i = 0; i < 4096; i++) {
-                       sb.append(str);
-               }
-               checkPipe(sb.toString());
-       }
-
-       void checkPipe(String data) {
-               StringBufferInputStream in = new StringBufferInputStream(data);
-               ByteArrayOutputStream out = new ByteArrayOutputStream();
-               FileUtil.Pipe pipe = new FileUtil.Pipe(in, out, 100l, true, true);
-               pipe.run();
-               assertTrue(data.equals(out.toString()));
-               assertTrue(null == pipe.getThrown());
-               assertEquals("totalWritten", data.length(), pipe.totalWritten());
-       }
-
-       public void testPipeThrown() {
-               final String data = "The quick brown fox jumped over the lazy dog";
-               final IOException thrown = new IOException("test");
-               StringBufferInputStream in = new StringBufferInputStream(data);
-               OutputStream out = new OutputStream() {
-                       public void write(int b) throws IOException {
-                               throw thrown;
-                       }
-               };
-
-               FileUtil.Pipe pipe = new FileUtil.Pipe(in, out, 100l, true, true);
-               pipe.run();
-               assertEquals("totalWritten", 0, pipe.totalWritten());
-               assertTrue(thrown == pipe.getThrown());
-       }
-
-       public void xtestPipeHalt() { // this test periodically fails on the build machine -
-                                                                       // disabling till we have time to figure out why
-               final long MAX = 1000000;
-               InputStream in = new InputStream() {
-                       long max = 0;
-
-                       public int read() throws IOException {
-                               if (max++ > MAX) {
-                                       throw new IOException("test failed");
-                               }
-                               return 1;
-                       }
-
-               };
-               final int minWritten = 20;
-               class Flag {
-                       boolean hit;
-               }
-               final Flag flag = new Flag();
-               OutputStream out = new OutputStream() {
-                       long max = 0;
-
-                       public void write(int b) throws IOException {
-                               if (max++ > MAX) {
-                                       throw new IOException("test failed");
-                               } else if (max > minWritten) {
-                                       if (!flag.hit) {
-                                               flag.hit = true;
-                                       }
-                               }
-                       }
-               };
-               class Result {
-                       long totalWritten;
-                       Throwable thrown;
-                       boolean set;
-               }
-               final Result result = new Result();
-               FileUtil.Pipe pipe = new FileUtil.Pipe(in, out, 100l, true, true) {
-                       protected void completing(long totalWritten, Throwable thrown) {
-                               result.totalWritten = totalWritten;
-                               result.thrown = thrown;
-                               result.set = true;
-                       }
-               };
-               // start it up
-               new Thread(pipe).start();
-               // wait for minWritten input
-               while (!flag.hit) {
-                       try {
-                               Thread.sleep(5l);
-                       } catch (InterruptedException e) {
-                               // ignore
-                       }
-               }
-               // halt
-               assertTrue(pipe.halt(true, true));
-               assertTrue(result.set);
-               assertTrue("Expected null but result.thrown = " + result.thrown, null == result.thrown);
-               assertTrue(null == pipe.getThrown());
-               assertEquals("total written", result.totalWritten, pipe.totalWritten());
-               if (minWritten > pipe.totalWritten()) {
-                       assertTrue("written: " + pipe.totalWritten(), false);
-               }
-       }
-
-}
diff --git a/util/testsrc/org/aspectj/util/GenericSignatureParserTest.java b/util/testsrc/org/aspectj/util/GenericSignatureParserTest.java
deleted file mode 100644 (file)
index 5a9e083..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 2005-2008 Contributors
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *
- * ******************************************************************/
-
-package org.aspectj.util;
-
-import junit.framework.TestCase;
-
-import org.aspectj.util.GenericSignature.ClassSignature;
-import org.aspectj.util.GenericSignature.ClassTypeSignature;
-import org.aspectj.util.GenericSignature.FieldTypeSignature;
-import org.aspectj.util.GenericSignature.SimpleClassTypeSignature;
-
-/**
- * @author Adrian Colyer
- * @author Andy Clement
- */
-public class GenericSignatureParserTest extends TestCase {
-
-       GenericSignatureParser parser;
-
-       public void testSimpleTokenize() {
-               String[] tokens = parser.tokenize("Ljava/lang/String;");
-               assertEquals(new String[] { "Ljava", "/", "lang", "/", "String", ";" }, tokens);
-       }
-
-       public void testTokenizeWithWildTypeArguments() {
-               String[] tokens = parser.tokenize("Ljava/lang/String<*>;");
-               assertEquals(new String[] { "Ljava", "/", "lang", "/", "String", "<", "*", ">", ";" }, tokens);
-       }
-
-       public void testTokenizeWithExtendsTypeArguments() {
-               String[] tokens = parser.tokenize("Ljava/util/List<+TE>;");
-               assertEquals(new String[] { "Ljava", "/", "util", "/", "List", "<", "+", "TE", ">", ";" }, tokens);
-       }
-
-       public void testTokenizeWithSuperTypeArguments() {
-               String[] tokens = parser.tokenize("Ljava/util/List<-TE>;");
-               assertEquals(new String[] { "Ljava", "/", "util", "/", "List", "<", "-", "TE", ">", ";" }, tokens);
-       }
-
-       public void testTokenizeArrayType() {
-               String[] tokens = parser.tokenize("[Ljava/lang/String;");
-               assertEquals(new String[] { "[", "Ljava", "/", "lang", "/", "String", ";" }, tokens);
-       }
-
-       public void testTokenizeFormalTypeParameters() {
-               String[] tokens = parser.tokenize("<T:Ljava/lang/String;:Ljava/util/Comparable;>");
-               assertEquals(new String[] { "<", "T", ":", "Ljava", "/", "lang", "/", "String", ";", ":", "Ljava", "/", "util", "/",
-                               "Comparable", ";", ">" }, tokens);
-       }
-
-       public void testParseClassSignatureSimple() {
-               ClassSignature sig = parser.parseAsClassSignature("Ljava/lang/String;");
-               assertEquals("No type parameters", 0, sig.formalTypeParameters.length);
-               assertEquals("No superinterfaces", 0, sig.superInterfaceSignatures.length);
-               assertEquals("Ljava/lang/String;", sig.superclassSignature.classSignature);
-               SimpleClassTypeSignature outerType = sig.superclassSignature.outerType;
-               assertEquals("Ljava/lang/String;", outerType.identifier);
-               assertEquals("No type args", 0, outerType.typeArguments.length);
-       }
-
-       public void testParseClassSignatureTypeArgs() {
-               ClassSignature sig = parser.parseAsClassSignature("Ljava/util/List<+Ljava/lang/String;>;");
-               assertEquals("No type parameters", 0, sig.formalTypeParameters.length);
-               assertEquals("No superinterfaces", 0, sig.superInterfaceSignatures.length);
-               assertEquals("Ljava/util/List<+Ljava/lang/String;>;", sig.superclassSignature.classSignature);
-               SimpleClassTypeSignature outerType = sig.superclassSignature.outerType;
-               assertEquals("Ljava/util/List", outerType.identifier);
-               assertEquals("One type arg", 1, outerType.typeArguments.length);
-               assertTrue(outerType.typeArguments[0].isPlus);
-               assertEquals("+Ljava/lang/String;", outerType.typeArguments[0].toString());
-       }
-
-       public void testParseClassSignatureTheFullMonty() {
-               ClassSignature sig = parser
-                               .parseAsClassSignature("<E:Ljava/lang/String;:Ljava/lang/Number<TE;>;>Ljava/util/List<TE;>;Ljava/util/Comparable<-TE;>;");
-               assertEquals("1 formal parameter", 1, sig.formalTypeParameters.length);
-               assertEquals("E", sig.formalTypeParameters[0].identifier);
-               ClassTypeSignature fsig = (ClassTypeSignature) sig.formalTypeParameters[0].classBound;
-               assertEquals("Ljava/lang/String;", fsig.classSignature);
-               assertEquals("1 interface bound", 1, sig.formalTypeParameters[0].interfaceBounds.length);
-               ClassTypeSignature isig = (ClassTypeSignature) sig.formalTypeParameters[0].interfaceBounds[0];
-               assertEquals("Ljava/lang/Number<TE;>;", isig.classSignature);
-               assertEquals("Ljava/util/List<TE;>;", sig.superclassSignature.classSignature);
-               assertEquals("1 type argument", 1, sig.superclassSignature.outerType.typeArguments.length);
-               assertEquals("TE;", sig.superclassSignature.outerType.typeArguments[0].toString());
-               assertEquals("1 super interface", 1, sig.superInterfaceSignatures.length);
-               assertEquals("Ljava/util/Comparable<-TE;>;", sig.superInterfaceSignatures[0].toString());
-       }
-
-       public void testFunky_406167() {
-               ClassSignature sig = parser
-                               .parseAsClassSignature("Lcom/google/android/gms/internal/hb<TT;>.com/google/android/gms/internal/hb$b<Ljava/lang/Boolean;>;");
-               // Note the package prefix on the nested types has been dropped
-               assertEquals("Lcom/google/android/gms/internal/hb<TT;>.hb$b<Ljava/lang/Boolean;>;",sig.superclassSignature.toString());
-               sig = parser
-                               .parseAsClassSignature("Lcom/a/a/b/t<TK;TV;>.com/a/a/b/af.com/a/a/b/ag;Ljava/util/ListIterator<TV;>;");
-               // Note the package prefix on the nested types has been dropped
-               assertEquals("Lcom/a/a/b/t<TK;TV;>.af.ag;",sig.superclassSignature.toString());
-               assertEquals("Ljava/util/ListIterator<TV;>;",sig.superInterfaceSignatures[0].toString());
-               sig = parser.parseAsClassSignature("Lcom/google/android/gms/internal/hb.com/google/android/gms/internal/hb$b<Ljava/lang/Boolean;>;");
-               // Note the package prefix on the nested types has been dropped
-               assertEquals("Lcom/google/android/gms/internal/hb.hb$b<Ljava/lang/Boolean;>;",sig.superclassSignature.toString());
-               sig = parser
-                               .parseAsClassSignature("Lcom/a/a/b/t.com/a/a/b/af.com/a/a/b/ag;Ljava/util/ListIterator<TV;>;");
-               // Note the package prefix on the nested types has been dropped
-               assertEquals("Lcom/a/a/b/t.af.ag;",sig.superclassSignature.toString());
-               assertEquals("Ljava/util/ListIterator<TV;>;",sig.superInterfaceSignatures[0].toString());
-       }
-
-
-       public void testFieldSignatureParsingClassType() {
-               FieldTypeSignature fsig = parser.parseAsFieldSignature("Ljava/lang/String;");
-               assertTrue("ClassTypeSignature", fsig instanceof ClassTypeSignature);
-               assertEquals("Ljava/lang/String;", fsig.toString());
-       }
-
-       public void testFieldSignatureParsingArrayType() {
-               FieldTypeSignature fsig = parser.parseAsFieldSignature("[Ljava/lang/String;");
-               assertTrue("ArrayTypeSignature", fsig instanceof GenericSignature.ArrayTypeSignature);
-               assertEquals("[Ljava/lang/String;", fsig.toString());
-       }
-
-       public void testFieldSignatureParsingTypeVariable() {
-               FieldTypeSignature fsig = parser.parseAsFieldSignature("TT;");
-               assertTrue("TypeVariableSignature", fsig instanceof GenericSignature.TypeVariableSignature);
-               assertEquals("TT;", fsig.toString());
-       }
-
-       public void testSimpleMethodSignatureParsing() {
-               GenericSignature.MethodTypeSignature mSig = parser.parseAsMethodSignature("()V");
-               assertEquals("No type parameters", 0, mSig.formalTypeParameters.length);
-               assertEquals("No parameters", 0, mSig.parameters.length);
-               assertEquals("Void return type", "V", mSig.returnType.toString());
-               assertEquals("No throws", 0, mSig.throwsSignatures.length);
-       }
-
-       public void testMethodSignatureTypeParams() {
-               GenericSignature.MethodTypeSignature mSig = parser.parseAsMethodSignature("<T:>(TT;)V");
-               assertEquals("One type parameter", 1, mSig.formalTypeParameters.length);
-               assertEquals("T", mSig.formalTypeParameters[0].identifier);
-               assertEquals("Ljava/lang/Object;", mSig.formalTypeParameters[0].classBound.toString());
-               assertEquals("One parameter", 1, mSig.parameters.length);
-               assertEquals("TT;", mSig.parameters[0].toString());
-               assertEquals("Void return type", "V", mSig.returnType.toString());
-               assertEquals("No throws", 0, mSig.throwsSignatures.length);
-       }
-
-       public void testMethodSignatureGenericReturn() {
-               GenericSignature.MethodTypeSignature mSig = parser.parseAsMethodSignature("<T:>()TT;");
-               assertEquals("One type parameter", 1, mSig.formalTypeParameters.length);
-               assertEquals("T", mSig.formalTypeParameters[0].identifier);
-               assertEquals("Ljava/lang/Object;", mSig.formalTypeParameters[0].classBound.toString());
-               assertEquals("No parameters", 0, mSig.parameters.length);
-               assertEquals("'T' return type", "TT;", mSig.returnType.toString());
-               assertEquals("No throws", 0, mSig.throwsSignatures.length);
-       }
-
-       public void testMethodSignatureThrows() {
-               GenericSignature.MethodTypeSignature mSig = parser
-                               .parseAsMethodSignature("<T:>(TT;)V^Ljava/lang/Exception;^Ljava/lang/RuntimeException;");
-               assertEquals("One type parameter", 1, mSig.formalTypeParameters.length);
-               assertEquals("T", mSig.formalTypeParameters[0].identifier);
-               assertEquals("Ljava/lang/Object;", mSig.formalTypeParameters[0].classBound.toString());
-               assertEquals("One parameter", 1, mSig.parameters.length);
-               assertEquals("TT;", mSig.parameters[0].toString());
-               assertEquals("Void return type", "V", mSig.returnType.toString());
-               assertEquals("2 throws", 2, mSig.throwsSignatures.length);
-               assertEquals("Ljava/lang/Exception;", mSig.throwsSignatures[0].toString());
-               assertEquals("Ljava/lang/RuntimeException;", mSig.throwsSignatures[1].toString());
-       }
-       
-       public void testMethodSignaturePrimitiveParams() {
-               GenericSignature.MethodTypeSignature mSig = parser.parseAsMethodSignature("(ILjava/lang/Object;)V");
-               assertEquals("2 parameters", 2, mSig.parameters.length);
-               assertEquals("I", mSig.parameters[0].toString());
-               assertEquals("Ljava/lang/Object;", mSig.parameters[1].toString());
-       }
-
-       public void testFullyQualifiedSuperclassAfterTypeParams() {
-               try {
-                       GenericSignature.FieldTypeSignature cSig = parser.parseAsFieldSignature("Ljava/util/List</;");
-                       fail("Expected IllegalStateException");
-               } catch (IllegalStateException ex) {
-                       assertTrue(ex.getMessage().indexOf("Ljava/util/List</;") != -1);
-               }
-       }
-
-       public void testPr107784() {
-               parser
-                               .parseAsMethodSignature("(Lcom/cibc/responders/mapping/CommonDataBeanScenario;Ljava/lang/Object;)Lcom/cibc/responders/response/Formatter<[BLjava/lang/Object;>;");
-               parser.parseAsClassSignature("<Parent:Ljava/lang/Object;Child:Ljava/lang/Object;>Ljava/lang/Object;");
-       }
-
-       private void assertEquals(String[] expected, String[] actual) {
-               if (actual.length != expected.length) {
-                       int shorter = Math.min(expected.length, actual.length);
-                       for (int i = 0; i < shorter; i++) {
-                               if (!actual[i].equals(expected[i])) {
-                                       fail("Expected " + expected[i] + " at position " + i + " but found " + actual[i]);
-                               }
-                       }
-                       fail("Expected " + expected.length + " tokens but got " + actual.length + tokensToString(actual));
-               }
-               for (int i = 0; i < actual.length; i++) {
-                       if (!actual[i].equals(expected[i])) {
-                               fail("Expected " + expected[i] + " at position " + i + " but found " + actual[i]);
-                       }
-               }
-       }
-
-       private String tokensToString(String[] tokens) {
-               StringBuffer sb = new StringBuffer();
-               sb.append(tokens[0]);
-               for (int i = 1; i < tokens.length; i++) {
-                       sb.append(",");
-                       sb.append(tokens[i]);
-               }
-               return sb.toString();
-       }
-
-       protected void setUp() throws Exception {
-               super.setUp();
-               parser = new GenericSignatureParser();
-       }
-}
diff --git a/util/testsrc/org/aspectj/util/LangUtilTest.java b/util/testsrc/org/aspectj/util/LangUtilTest.java
deleted file mode 100644 (file)
index 4cb4479..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 1999-2001 Xerox Corporation, 
- *               2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *     Xerox/PARC     initial implementation 
- * ******************************************************************/
-
-package org.aspectj.util;
-
-import java.util.Arrays;
-import java.util.List;
-
-import junit.framework.TestCase;
-
-/**
- * 
- */
-public class LangUtilTest extends TestCase {
-
-       public LangUtilTest(String name) {
-               super(name);
-       }
-
-       // /** @see LangUtil.extractOptions(String[], String[], int[], List) */
-       // public void testExtractOptions() {
-       // ArrayList extracted = new ArrayList();
-       // String[] args = new String[] { "-d", "classes", "-classpath", "foo.jar", "-verbose", "Bar.java" };
-       // String[] validOptions = new String[] { "-classpath", "-d", "-verbose", "-help" };
-       // int[] optionArgs = new int[] { 1, 1, 0, 0 };
-       // String[] result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted);
-       // String resultString = "" + Arrays.asList(result);
-       // String EXP = "[Bar.java]";
-       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-       // EXP = "[-d, classes, -classpath, foo.jar, -verbose]";
-       // resultString = "" + extracted;
-       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-       //        
-       // // no input, no output
-       // extracted.clear();
-       // args = new String[] {};
-       // result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted);
-       // resultString = "" + Arrays.asList(result);
-       // EXP = "[]";
-       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-       // resultString = "" + extracted;
-       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-       //        
-       // // one input, nothing extracted
-       // extracted.clear();
-       // args = new String[] {"Bar.java"};
-       // result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted);
-       // resultString = "" + Arrays.asList(result);
-       // EXP = "[Bar.java]";
-       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-       // EXP = "[]";
-       // resultString = "" + extracted;
-       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-       //        
-       // // one input, extracted
-       // extracted.clear();
-       // args = new String[] {"-verbose"};
-       // result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted);
-       // resultString = "" + Arrays.asList(result);
-       // EXP = "[]";
-       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-       // EXP = "[-verbose]";
-       // resultString = "" + extracted;
-       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-       //
-       // // ------- booleans
-       // validOptions = new String[] { "-help", "-verbose" };
-       // optionArgs = null;
-       //
-       // // one input, extracted
-       // extracted.clear();
-       // args = new String[] {"-verbose"};
-       // result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted);
-       // resultString = "" + Arrays.asList(result);
-       // EXP = "[]";
-       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-       // EXP = "[-verbose]";
-       // resultString = "" + extracted;
-       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-       //        
-       // // one input, not extracted
-       // extracted.clear();
-       // args = new String[] {"Bar.java"};
-       // result = LangUtil.extractOptions(args, validOptions, optionArgs, extracted);
-       // resultString = "" + Arrays.asList(result);
-       // EXP = "[Bar.java]";
-       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-       // EXP = "[]";
-       // resultString = "" + extracted;
-       // assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-       // }
-
-       public void testVersion() {
-               assertTrue(LangUtil.is13VMOrGreater()); // min vm now - floor may change
-               if (LangUtil.is15VMOrGreater()) {
-                       assertTrue(LangUtil.is14VMOrGreater());
-               }
-       }
-
-       /** @see LangUtil.extractOptions(String[], String[][]) */
-       public void testExtractOptionsArrayCollector() {
-               String[] args = new String[] { "-d", "classes", "-classpath", "foo.jar", "-verbose", "Bar.java" };
-               String[][] OPTIONS = new String[][] { new String[] { "-classpath", null }, new String[] { "-d", null },
-                               new String[] { "-verbose" }, new String[] { "-help" } };
-
-               String[][] options = LangUtil.copyStrings(OPTIONS);
-
-               String[] result = LangUtil.extractOptions(args, options);
-               String resultString = "" + Arrays.asList(result);
-               String EXP = "[Bar.java]";
-               assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-               assertTrue("-verbose".equals(options[2][0]));
-               assertTrue("foo.jar".equals(options[0][1]));
-               assertTrue("classes".equals(options[1][1]));
-               assertTrue("-classpath".equals(options[0][0]));
-               assertTrue("-d".equals(options[1][0]));
-               assertTrue(null == options[3][0]);
-
-               // get args back, no options set
-               args = new String[] { "Bar.java" };
-               options = LangUtil.copyStrings(OPTIONS);
-
-               result = LangUtil.extractOptions(args, options);
-               resultString = "" + Arrays.asList(result);
-               EXP = "[Bar.java]";
-               assertTrue(resultString + " != " + EXP, resultString.equals(EXP));
-               assertTrue(null == options[0][0]);
-               assertTrue(null == options[1][0]);
-               assertTrue(null == options[2][0]);
-               assertTrue(null == options[3][0]);
-       }
-
-       // public void testOptionVariants() {
-       // String[] NONE = new String[0];
-       // String[] one = new String[] {"-1"};
-       // String[] two = new String[] {"-2"};
-       // String[] three= new String[] {"-3"};
-       // String[] both = new String[] {"-1", "-2" };
-       // String[] oneB = new String[] {"-1-"};
-       // String[] bothB = new String[] {"-1-", "-2-" };
-       // String[] onetwoB = new String[] {"-1", "-2-" };
-       // String[] oneBtwo = new String[] {"-1-", "-2" };
-       // String[] threeB = new String[] {"-1-", "-2-", "-3-"};
-       // String[] athreeB = new String[] {"a", "-1-", "-2-", "-3-"};
-       // String[] threeaB = new String[] {"-1-", "a", "-2-", "-3-"};
-       //        
-       // checkOptionVariants(NONE, new String[][] { NONE });
-       // checkOptionVariants(one, new String[][] { one });
-       // checkOptionVariants(both, new String[][] { both });
-       // checkOptionVariants(oneB, new String[][] { NONE, one });
-       // checkOptionVariants(bothB, new String[][] { NONE, one, new String[] {"-2"}, both });
-       // checkOptionVariants(onetwoB, new String[][] { one, new String[] {"-1", "-2"}});
-       // checkOptionVariants(oneBtwo, new String[][] { two, new String[] {"-1", "-2"}});
-       // checkOptionVariants(threeB, new String[][]
-       // {
-       // NONE,
-       // one,
-       // two,
-       // new String[] {"-1", "-2"},
-       // three,
-       // new String[] {"-1", "-3"},
-       // new String[] {"-2", "-3"},
-       // new String[] {"-1", "-2", "-3"}
-       // });
-       // checkOptionVariants(athreeB, new String[][]
-       // {
-       // new String[] {"a"},
-       // new String[] {"a", "-1"},
-       // new String[] {"a", "-2"},
-       // new String[] {"a", "-1", "-2"},
-       // new String[] {"a", "-3"},
-       // new String[] {"a", "-1", "-3"},
-       // new String[] {"a", "-2", "-3"},
-       // new String[] {"a", "-1", "-2", "-3"}
-       // });
-       // checkOptionVariants(threeaB, new String[][]
-       // {
-       // new String[] {"a"},
-       // new String[] {"-1", "a"},
-       // new String[] {"a", "-2"},
-       // new String[] {"-1", "a", "-2"},
-       // new String[] {"a", "-3"},
-       // new String[] {"-1", "a", "-3"},
-       // new String[] {"a", "-2", "-3"},
-       // new String[] {"-1", "a", "-2", "-3"}
-       // });
-       // }
-
-       // void checkOptionVariants(String[] options, String[][] expected) {
-       // String[][] result = LangUtil.optionVariants(options);
-       // if (expected.length != result.length) {
-       // assertTrue("exp=" + expected.length + " actual=" + result.length, false);
-       // }
-       // for (int i = 0; i < expected.length; i++) {
-       // assertEquals(""+i,
-       // "" + Arrays.asList(expected[i]),
-       // "" + Arrays.asList(result[i]));
-       // }
-       // }
-
-       /** @see XMLWriterTest#testUnflattenList() */
-       public void testCommaSplit() {
-               checkCommaSplit("", new String[] { "" });
-               checkCommaSplit("1", new String[] { "1" });
-               checkCommaSplit(" 1 2 ", new String[] { "1 2" });
-               checkCommaSplit(" 1 , 2 ", new String[] { "1", "2" });
-               checkCommaSplit("1,2,3,4", new String[] { "1", "2", "3", "4" });
-       }
-
-       void checkCommaSplit(String input, String[] expected) {
-               List actual = LangUtil.commaSplit(input);
-               String a = "" + actual;
-               String e = "" + Arrays.asList(expected);
-               assertTrue(e + "==" + a, e.equals(a));
-       }
-
-       public void testElideEndingLines() {
-               StringBuffer stackBuffer = LangUtil.stackToString(new RuntimeException(""), true);
-               LangUtil.elideEndingLines(LangUtil.StringChecker.TEST_PACKAGES, stackBuffer, 10);
-               String result = stackBuffer.toString();
-
-               if (-1 == result.indexOf("(... ")) {
-                       // brittle - will fail under different top-level drivers
-                       String m = "when running under eclipse or Ant, expecting (... in trace: ";
-                       assertTrue(m + result, false);
-               }
-
-               stackBuffer = new StringBuffer(
-                               "java.lang.RuntimeException: unimplemented"
-                                               + "\n at org.aspectj.ajdt.internal.core.builder.EclipseUnwovenClassFile.writeWovenBytes(EclipseUnwovenClassFile.java:59)"
-                                               + "\n at org.aspectj.weaver.bcel.BcelWeaver.dump(BcelWeaver.java:271)"
-                                               + "\n at org.aspectj.weaver.bcel.BcelWeaver.weave(BcelWeaver.java:233)"
-                                               + "\n at org.aspectj.weaver.bcel.BcelWeaver.weave(BcelWeaver.java:198)"
-                                               + "\n at org.aspectj.ajdt.internal.core.builder.AjBuildManager.weaveAndGenerateClassFiles(AjBuildanager.java:230)"
-                                               + "\n at org.aspectj.ajdt.internal.core.builder.AjBuildManager.batchBuild(AjBuildManager.java:50)"
-                                               + "\n at org.aspectj.ajdt.ajc.AjdtCommand.runCommand(AjdtCommand.java:42)"
-                                               + "\n at org.aspectj.testing.harness.bridge.CompilerRun.run(CompilerRun.java:222)"
-                                               + "\n at org.aspectj.testing.run.Runner.runPrivate(Runner.java:363)"
-                                               + "\n at org.aspectj.testing.run.Runner.runChild(Runner.java:167)"
-                                               + "\n at org.aspectj.testing.run.Runner.runChild(Runner.java:126)"
-                                               + "\n at org.aspectj.testing.run.Runner$IteratorWrapper.run(Runner.java:441)"
-                                               + "\n at org.aspectj.testing.run.Runner.runPrivate(Runner.java:363)"
-                                               + "\n at org.aspectj.testing.run.Runner.runChild(Runner.java:167)"
-                                               + "\n at org.aspectj.testing.run.Runner.runChild(Runner.java:126)"
-                                               + "\n at org.aspectj.testing.run.Runner$IteratorWrapper.run(Runner.java:441)"
-                                               + "\n at org.aspectj.testing.run.Runner.runPrivate(Runner.java:363)"
-                                               + "\n at org.aspectj.testing.run.Runner.run(Runner.java:114)"
-                                               + "\n at org.aspectj.testing.run.Runner.run(Runner.java:105)"
-                                               + "\n at org.aspectj.testing.run.Runner.runIterator(Runner.java:228)"
-                                               + "\n at org.aspectj.testing.drivers.Harness.run(Harness.java:254)"
-                                               + "\n at org.aspectj.testing.drivers.Harness.runMain(Harness.java:217)"
-                                               + "\n at org.aspectj.testing.drivers.Harness.main(Harness.java:99)"
-                                               + "\n at org.aspectj.testing.Harness.main(Harness.java:37)" + "\n clip me");
-
-               LangUtil.elideEndingLines(LangUtil.StringChecker.TEST_PACKAGES, stackBuffer, 25);
-               result = stackBuffer.toString();
-               assertTrue(result, -1 != result.indexOf("(... "));
-               assertTrue(result, -1 == result.indexOf("org.aspectj.testing"));
-       }
-}
diff --git a/util/testsrc/org/aspectj/util/UtilModuleTests.java b/util/testsrc/org/aspectj/util/UtilModuleTests.java
deleted file mode 100644 (file)
index 463fdce..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.aspectj.util;
-/* *******************************************************************
- * Copyright (c) 1999-2001 Xerox Corporation, 
- *               2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *     Xerox/PARC     initial implementation 
- * ******************************************************************/
-
-
-// default package
-
-import junit.framework.*;
-import junit.framework.Test;
-
-public class UtilModuleTests extends TestCase {
-
-    public static Test suite() { 
-        TestSuite suite = new TestSuite(UtilModuleTests.class.getName());
-        suite.addTest(org.aspectj.util.UtilTests.suite()); 
-        return suite;
-    }
-
-    public UtilModuleTests(String name) { super(name); }
-
-}  
diff --git a/util/testsrc/org/aspectj/util/UtilTests.java b/util/testsrc/org/aspectj/util/UtilTests.java
deleted file mode 100644 (file)
index 9d459aa..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/* *******************************************************************
- * Copyright (c) 1999-2001 Xerox Corporation, 
- *               2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved. 
- * This program and the accompanying materials are made available 
- * under the terms of the Eclipse Public License v1.0 
- * which accompanies this distribution and is available at 
- * http://www.eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *     Xerox/PARC     initial implementation 
- * ******************************************************************/
-
-package org.aspectj.util;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-public class UtilTests extends TestCase {
-
-       public static Test suite() {
-               TestSuite suite = new TestSuite(UtilTests.class.getName());
-               // $JUnit-BEGIN$
-               suite.addTestSuite(FileUtilTest.class);
-               suite.addTestSuite(LangUtilTest.class);
-               suite.addTestSuite(GenericSignatureParserTest.class);
-               // $JUnit-END$
-               return suite;
-       }
-
-       public UtilTests(String name) {
-               super(name);
-       }
-
-}