From: Andy Clement Date: Wed, 23 Jan 2019 20:28:35 +0000 (-0800) Subject: mavenized util module X-Git-Tag: V1_9_3RC1~101 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=6245346ab0fe0f19db977c86389e31517ed068ec;p=aspectj.git mavenized util module --- diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..35ca85e24 --- /dev/null +++ b/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + org.aspectj + aspectj-parent + pom + ${revision} + AspectJ Parent Project + + + 1.9.3.BUILD-SNAPSHOT + UTF-8 + + + + util + bcel + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + + + + + junit + junit + 3.8.1 + test + + + + diff --git a/util/.classpath b/util/.classpath index f33c50284..f0257c5a5 100644 --- a/util/.classpath +++ b/util/.classpath @@ -1,8 +1,44 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/util/.cvsignore b/util/.cvsignore deleted file mode 100644 index dbb81434b..000000000 --- a/util/.cvsignore +++ /dev/null @@ -1,3 +0,0 @@ -bin - -bintest diff --git a/util/.isJava5 b/util/.isJava5 deleted file mode 100644 index 136d06384..000000000 --- a/util/.isJava5 +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/util/.project b/util/.project deleted file mode 100644 index ee97704dd..000000000 --- a/util/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - util - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/util/.settings/org.eclipse.core.resources.prefs b/util/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..f9fe34593 --- /dev/null +++ b/util/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/test/java=UTF-8 +encoding/=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 index 000000000..d4313d4b2 --- /dev/null +++ b/util/.settings/org.eclipse.jdt.apt.core.prefs @@ -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 index 000000000..5592a0a1c --- /dev/null +++ b/util/.settings/org.eclipse.jdt.core.prefs @@ -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 index 05bdade88..000000000 --- a/util/build.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/util/pom.xml b/util/pom.xml new file mode 100644 index 000000000..d53319f8f --- /dev/null +++ b/util/pom.xml @@ -0,0 +1,17 @@ + + 4.0.0 + + util + jar + util + + + org.aspectj + aspectj-parent + ${revision} + .. + + + diff --git a/util/pr107313-SourceLocation.patch b/util/pr107313-SourceLocation.patch deleted file mode 100644 index 63fd61f61..000000000 --- a/util/pr107313-SourceLocation.patch +++ /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 index 000000000..f3db2bda7 --- /dev/null +++ b/util/src/main/java/org/aspectj/util/FileUtil.java @@ -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 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 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 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 + * flatten(getAbsolutePaths(files), infix) + * @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 result = new ArrayList(); + 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 result = new ArrayList(); + 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 listClassFiles(File dir) { + ArrayList result = new ArrayList(); + 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 list = new ArrayList(); + 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 lineSeek(String sought, List sources, boolean listAll, PrintStream errorSink) { + if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sources)) { + return Collections.emptyList(); + } + ArrayList result = new ArrayList(); + for (Iterator 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 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 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 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 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 makeClasspath(URL[] urls) { + List ret = new LinkedList(); + 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 Pipe(in, out, 100l, false, false) + * + * @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 run() 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 index 000000000..807d98087 --- /dev/null +++ b/util/src/main/java/org/aspectj/util/FuzzyBoolean.java @@ -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 index 000000000..bfefc007e --- /dev/null +++ b/util/src/main/java/org/aspectj/util/GenericSignature.java @@ -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 index 000000000..28c8db83d --- /dev/null +++ b/util/src/main/java/org/aspectj/util/GenericSignatureParser.java @@ -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 formalTypeParametersList = new ArrayList(); + do { + formalTypeParametersList.add(parseFormalTypeParameter()); + } while (!maybeEat(">")); + classSig.formalTypeParameters = new FormalTypeParameter[formalTypeParametersList.size()]; + formalTypeParametersList.toArray(classSig.formalTypeParameters); + } + classSig.superclassSignature = parseClassTypeSignature(); + List superIntSigs = new ArrayList(); + 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 formalTypeParametersList = new ArrayList(); + do { + formalTypeParametersList.add(parseFormalTypeParameter()); + } while (!maybeEat(">")); + formals = new FormalTypeParameter[formalTypeParametersList.size()]; + formalTypeParametersList.toArray(formals); + } + // Parameters + eat("("); + List paramList = new ArrayList(); + 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 throwsList = new ArrayList(); + 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 optionalBounds = new ArrayList(); + 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 nestedTypeList = new ArrayList(); + 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.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 typeArgs = new ArrayList(); + 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 tokens = new ArrayList(); + 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 index 000000000..9008f0872 --- /dev/null +++ b/util/src/main/java/org/aspectj/util/IStructureModel.java @@ -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 index 000000000..82bd9426b --- /dev/null +++ b/util/src/main/java/org/aspectj/util/LangUtil.java @@ -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 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 getFirstNumbers(String vm) { + List result = new ArrayList(); + 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 text at whitespace. + * + * @param text String to split. + */ + public static String[] split(String text) { + return strings(text).toArray(new String[0]); + } + + /** + * Splits input at commas, trimming any white space. + * + * @param input String to split. + * @return List of String of elements. + */ + public static List 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 result = new ArrayList(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 input, 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 String to split. + * @param delim String separators for input. + * @return List of String of elements. + */ + public static List anySplit(String input, String delim) { + if (null == input) { + return Collections.emptyList(); + } + ArrayList result = new ArrayList(); + + 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 List using a StringTokenizer. + * + * @param text String to split. + */ + public static List strings(String text) { + if (LangUtil.isEmpty(text)) { + return Collections.emptyList(); + } + List strings = new ArrayList(); + StringTokenizer tok = new StringTokenizer(text); + while (tok.hasMoreTokens()) { + strings.add(tok.nextToken()); + } + return strings; + } + + /** @return a non-null unmodifiable List */ + public static List safeList(List list) { + return (null == list ? Collections.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., + * + *
+	 * String[] options = new String[][] { new String[] { "-verbose" }, new String[] { "-classpath", null } };
+	 * String[] args = extractOptions(args, options);
+	 * boolean verbose = null != options[0][0];
+	 * boolean classpath = options[1][1];
+	 * 
+ * + * @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 new String[][] { options } 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 result = null; + if (0 == sourceLength) { + resultSize = 0; + } else { + result = new ArrayList(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 t after unwrapping and eliding any test packages. + * + * @param t Throwable to print. + * @see #maxStackTrace + */ + public static String renderException(Throwable t) { + return renderException(t, true); + } + + /** + * Renders exception t, unwrapping, optionally eliding and limiting total number of lines. + * + * @param t Throwable 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 lines = new LinkedList(); + 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 List arrayAsList(T[] array) { + if ((null == array) || (1 > array.length)) { + return Collections.emptyList(); + } + ArrayList list = new ArrayList(); + 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 cmd = new ArrayList(); + 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. start() 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 doCompleting(..) to get notice + * when the process completes. + *

+ * The following sample code creates a process with a completion callback starts it, and some time later retries the process. + * + *

+	 * LangUtil.ProcessController controller = new LangUtil.ProcessController() {
+	 * 	protected void doCompleting(LangUtil.ProcessController.Thrown thrown, int result) {
+	 * 		// signal result 
+	 * 	}
+	 * };
+	 * controller.init(new String[] { "java", "-version" }, "java version");
+	 * controller.start();
+	 * // some time later...
+	 * // retry...
+	 * if (!controller.completed()) {
+	 * 	controller.stop();
+	 * 	controller.reinit();
+	 * 	controller.start();
+	 * }
+	 * 
+ * + * warning: 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 cmd = new ArrayList(); + 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 getThrown(), 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 getThrown(). If there is an exception, the process + * completed abruptly (including side-effects of the user halting the process). If userStopped() is true, then + * some client asked that the process be destroyed using stop(). Otherwise, the result code should be the + * result value returned by the process. + * + * @param thrown same as getThrown().fromProcess. + * @param result same as getResult() + * @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 index 000000000..8bb9f3b77 --- /dev/null +++ b/util/src/main/java/org/aspectj/util/PartialOrder.java @@ -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
    + *
  • +1 if this is greater than other
  • + *
  • -1 if this is less than other
  • + *
  • 0 if this is not comparable to other
  • + *
+ * + * Note: returning 0 from this method doesn't mean the same thing as returning 0 from + * java.util.Comparable.compareTo() + */ + 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 object; + List> smallerObjects = new LinkedList>(); + List> biggerObjects = new LinkedList>(); + + public SortObject(T o) { + object = o; + } + + boolean hasNoSmallerObjects() { + return smallerObjects.size() == 0; + } + + boolean removeSmallerObject(SortObject o) { + smallerObjects.remove(o); + return hasNoSmallerObjects(); + } + + void addDirectedLinks(SortObject 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 void addNewPartialComparable(List> graph, T o) { + SortObject so = new SortObject(o); + for (Iterator> i = graph.iterator(); i.hasNext();) { + SortObject other = i.next(); + so.addDirectedLinks(other); + } + graph.add(so); + } + + private static void removeFromGraph(List> graph, SortObject o) { + for (Iterator> i = graph.iterator(); i.hasNext();) { + SortObject 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 List sort(List 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> sortList = new LinkedList>(); + for (Iterator 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 leastWithNoSmallers = null; + + for (SortObject 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 l = new ArrayList(); + 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 index 000000000..83ceaac23 --- /dev/null +++ b/util/src/main/java/org/aspectj/util/Reflection.java @@ -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 dirs = new ArrayList(); + ArrayList libs = new ArrayList(); + ArrayList urls = new ArrayList(); + 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 index 000000000..94ae83441 --- /dev/null +++ b/util/src/main/java/org/aspectj/util/SoftHashMap.java @@ -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 extends AbstractMap { + private Map map; + private ReferenceQueue rq = new ReferenceQueue(); + + public SoftHashMap() { + this.map = new HashMap(); + } + + class SpecialValue extends SoftReference { + 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> entrySet() { + if (map.isEmpty()) { return Collections.emptyMap().entrySet(); } + Map currentContents = new HashMap(); + for (Map.Entry 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 index 000000000..99d223143 --- /dev/null +++ b/util/src/main/java/org/aspectj/util/TypeSafeEnum.java @@ -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 index 000000000..091e022a3 --- /dev/null +++ b/util/src/main/java/org/aspectj/util/UtilClassLoader.java @@ -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 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 dcopy = new ArrayList(); + + 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 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 index f3db2bda7..000000000 --- a/util/src/org/aspectj/util/FileUtil.java +++ /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 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 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 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 - * flatten(getAbsolutePaths(files), infix) - * @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 result = new ArrayList(); - 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 result = new ArrayList(); - 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 listClassFiles(File dir) { - ArrayList result = new ArrayList(); - 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 list = new ArrayList(); - 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 lineSeek(String sought, List sources, boolean listAll, PrintStream errorSink) { - if (LangUtil.isEmpty(sought) || LangUtil.isEmpty(sources)) { - return Collections.emptyList(); - } - ArrayList result = new ArrayList(); - for (Iterator 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 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 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 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 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 makeClasspath(URL[] urls) { - List ret = new LinkedList(); - 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 Pipe(in, out, 100l, false, false) - * - * @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 run() 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 index 807d98087..000000000 --- a/util/src/org/aspectj/util/FuzzyBoolean.java +++ /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 index bfefc007e..000000000 --- a/util/src/org/aspectj/util/GenericSignature.java +++ /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 index 28c8db83d..000000000 --- a/util/src/org/aspectj/util/GenericSignatureParser.java +++ /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 formalTypeParametersList = new ArrayList(); - do { - formalTypeParametersList.add(parseFormalTypeParameter()); - } while (!maybeEat(">")); - classSig.formalTypeParameters = new FormalTypeParameter[formalTypeParametersList.size()]; - formalTypeParametersList.toArray(classSig.formalTypeParameters); - } - classSig.superclassSignature = parseClassTypeSignature(); - List superIntSigs = new ArrayList(); - 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 formalTypeParametersList = new ArrayList(); - do { - formalTypeParametersList.add(parseFormalTypeParameter()); - } while (!maybeEat(">")); - formals = new FormalTypeParameter[formalTypeParametersList.size()]; - formalTypeParametersList.toArray(formals); - } - // Parameters - eat("("); - List paramList = new ArrayList(); - 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 throwsList = new ArrayList(); - 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 optionalBounds = new ArrayList(); - 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 nestedTypeList = new ArrayList(); - 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.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 typeArgs = new ArrayList(); - 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 tokens = new ArrayList(); - 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 index 9008f0872..000000000 --- a/util/src/org/aspectj/util/IStructureModel.java +++ /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 index 82bd9426b..000000000 --- a/util/src/org/aspectj/util/LangUtil.java +++ /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 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 getFirstNumbers(String vm) { - List result = new ArrayList(); - 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 text at whitespace. - * - * @param text String to split. - */ - public static String[] split(String text) { - return strings(text).toArray(new String[0]); - } - - /** - * Splits input at commas, trimming any white space. - * - * @param input String to split. - * @return List of String of elements. - */ - public static List 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 result = new ArrayList(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 input, 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 String to split. - * @param delim String separators for input. - * @return List of String of elements. - */ - public static List anySplit(String input, String delim) { - if (null == input) { - return Collections.emptyList(); - } - ArrayList result = new ArrayList(); - - 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 List using a StringTokenizer. - * - * @param text String to split. - */ - public static List strings(String text) { - if (LangUtil.isEmpty(text)) { - return Collections.emptyList(); - } - List strings = new ArrayList(); - StringTokenizer tok = new StringTokenizer(text); - while (tok.hasMoreTokens()) { - strings.add(tok.nextToken()); - } - return strings; - } - - /** @return a non-null unmodifiable List */ - public static List safeList(List list) { - return (null == list ? Collections.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., - * - *
-	 * String[] options = new String[][] { new String[] { "-verbose" }, new String[] { "-classpath", null } };
-	 * String[] args = extractOptions(args, options);
-	 * boolean verbose = null != options[0][0];
-	 * boolean classpath = options[1][1];
-	 * 
- * - * @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 new String[][] { options } 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 result = null; - if (0 == sourceLength) { - resultSize = 0; - } else { - result = new ArrayList(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 t after unwrapping and eliding any test packages. - * - * @param t Throwable to print. - * @see #maxStackTrace - */ - public static String renderException(Throwable t) { - return renderException(t, true); - } - - /** - * Renders exception t, unwrapping, optionally eliding and limiting total number of lines. - * - * @param t Throwable 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 lines = new LinkedList(); - 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 List arrayAsList(T[] array) { - if ((null == array) || (1 > array.length)) { - return Collections.emptyList(); - } - ArrayList list = new ArrayList(); - 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 cmd = new ArrayList(); - 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. start() 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 doCompleting(..) to get notice - * when the process completes. - *

- * The following sample code creates a process with a completion callback starts it, and some time later retries the process. - * - *

-	 * LangUtil.ProcessController controller = new LangUtil.ProcessController() {
-	 * 	protected void doCompleting(LangUtil.ProcessController.Thrown thrown, int result) {
-	 * 		// signal result 
-	 * 	}
-	 * };
-	 * controller.init(new String[] { "java", "-version" }, "java version");
-	 * controller.start();
-	 * // some time later...
-	 * // retry...
-	 * if (!controller.completed()) {
-	 * 	controller.stop();
-	 * 	controller.reinit();
-	 * 	controller.start();
-	 * }
-	 * 
- * - * warning: 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 cmd = new ArrayList(); - 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 getThrown(), 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 getThrown(). If there is an exception, the process - * completed abruptly (including side-effects of the user halting the process). If userStopped() is true, then - * some client asked that the process be destroyed using stop(). Otherwise, the result code should be the - * result value returned by the process. - * - * @param thrown same as getThrown().fromProcess. - * @param result same as getResult() - * @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 index 8bb9f3b77..000000000 --- a/util/src/org/aspectj/util/PartialOrder.java +++ /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
    - *
  • +1 if this is greater than other
  • - *
  • -1 if this is less than other
  • - *
  • 0 if this is not comparable to other
  • - *
- * - * Note: returning 0 from this method doesn't mean the same thing as returning 0 from - * java.util.Comparable.compareTo() - */ - 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 object; - List> smallerObjects = new LinkedList>(); - List> biggerObjects = new LinkedList>(); - - public SortObject(T o) { - object = o; - } - - boolean hasNoSmallerObjects() { - return smallerObjects.size() == 0; - } - - boolean removeSmallerObject(SortObject o) { - smallerObjects.remove(o); - return hasNoSmallerObjects(); - } - - void addDirectedLinks(SortObject 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 void addNewPartialComparable(List> graph, T o) { - SortObject so = new SortObject(o); - for (Iterator> i = graph.iterator(); i.hasNext();) { - SortObject other = i.next(); - so.addDirectedLinks(other); - } - graph.add(so); - } - - private static void removeFromGraph(List> graph, SortObject o) { - for (Iterator> i = graph.iterator(); i.hasNext();) { - SortObject 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 List sort(List 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> sortList = new LinkedList>(); - for (Iterator 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 leastWithNoSmallers = null; - - for (SortObject 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 l = new ArrayList(); - 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 index 83ceaac23..000000000 --- a/util/src/org/aspectj/util/Reflection.java +++ /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 dirs = new ArrayList(); - ArrayList libs = new ArrayList(); - ArrayList urls = new ArrayList(); - 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 index 94ae83441..000000000 --- a/util/src/org/aspectj/util/SoftHashMap.java +++ /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 extends AbstractMap { - private Map map; - private ReferenceQueue rq = new ReferenceQueue(); - - public SoftHashMap() { - this.map = new HashMap(); - } - - class SpecialValue extends SoftReference { - 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> entrySet() { - if (map.isEmpty()) { return Collections.emptyMap().entrySet(); } - Map currentContents = new HashMap(); - for (Map.Entry 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 index 99d223143..000000000 --- a/util/src/org/aspectj/util/TypeSafeEnum.java +++ /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 index 091e022a3..000000000 --- a/util/src/org/aspectj/util/UtilClassLoader.java +++ /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 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 dcopy = new ArrayList(); - - 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 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 index 000000000..8ce18c700 --- /dev/null +++ b/util/src/test/java/org/aspectj/util/FileUtilTest.java @@ -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 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: + * + *
+	 * path/to
+	 *   .cvsignore
+	 *   CVS/
+	 *     Root
+	 *     Repository
+	 *   Base.java
+	 *   com/
+	 *     parc/
+	 *       messages.properties
+	 *   org/
+	 *     aspectj/
+	 *       Version.java
+	 * 
+ * + * a call + * + *
+	 * dirPaths(new File("path/to"), new String[0]);
+	 * 
+ * + * returns + * + *
+	 * { "Base.java", "com/parc/messages.properties", "org/aspectj/Version.java" }
+	 * 
+ * + * while the call + * + *
+	 * dirPaths(new File("path/to"), new String[] { ".java" });
+	 * 
+ * + * returns + * + *
+	 * { "Base.java", "org/aspectj/Version.java" }
+	 * 
+ * + * @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 result = new ArrayList(); + doDirPaths(dir, result); + // if suffixes required, remove those without suffixes + if (!LangUtil.isEmpty(suffixes)) { + for (ListIterator 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 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 tempFiles; + + public FileUtilTest(String s) { + super(s); + tempFiles = new ArrayList(); + } + + public void tearDown() { + for (ListIterator 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 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 sourceList = new ArrayList(); + sourceList.add(file.getPath()); + + final ArrayList errors = new ArrayList(); + final PrintStream errorSink = new PrintStream(System.err, true) { + public void println(String error) { + errors.add(error); + } + }; + for (int i = 0; i < 10; i++) { + List 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 errors = new ArrayList<>(); + final PrintStream errorSink = new PrintStream(System.err, true) { + public void println(String error) { + errors.add(error); + } + }; + List sourceList = new ArrayList<>(); + sourceList.addAll(Arrays.asList(sources)); + sourceList = Collections.unmodifiableList(sourceList); + for (int k = 0; k < sources.length; k++) { + List result = FileUtil.lineSeek("" + k, sourceList, true, errorSink); + // number k found in every other line of every file at index k + Iterator 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 index 000000000..5a9e083e4 --- /dev/null +++ b/util/src/test/java/org/aspectj/util/GenericSignatureParserTest.java @@ -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(""); + 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(";>Ljava/util/List;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;", isig.classSignature); + assertEquals("Ljava/util/List;", 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.com/google/android/gms/internal/hb$b;"); + // Note the package prefix on the nested types has been dropped + assertEquals("Lcom/google/android/gms/internal/hb.hb$b;",sig.superclassSignature.toString()); + sig = parser + .parseAsClassSignature("Lcom/a/a/b/t.com/a/a/b/af.com/a/a/b/ag;Ljava/util/ListIterator;"); + // 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;",sig.superInterfaceSignatures[0].toString()); + sig = parser.parseAsClassSignature("Lcom/google/android/gms/internal/hb.com/google/android/gms/internal/hb$b;"); + // Note the package prefix on the nested types has been dropped + assertEquals("Lcom/google/android/gms/internal/hb.hb$b;",sig.superclassSignature.toString()); + sig = parser + .parseAsClassSignature("Lcom/a/a/b/t.com/a/a/b/af.com/a/a/b/ag;Ljava/util/ListIterator;"); + // 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;",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("(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("()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("(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;"); + parser.parseAsClassSignature("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 index 000000000..4cb44795d --- /dev/null +++ b/util/src/test/java/org/aspectj/util/LangUtilTest.java @@ -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 index 8ce18c700..000000000 --- a/util/testsrc/org/aspectj/util/FileUtilTest.java +++ /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 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: - * - *
-	 * path/to
-	 *   .cvsignore
-	 *   CVS/
-	 *     Root
-	 *     Repository
-	 *   Base.java
-	 *   com/
-	 *     parc/
-	 *       messages.properties
-	 *   org/
-	 *     aspectj/
-	 *       Version.java
-	 * 
- * - * a call - * - *
-	 * dirPaths(new File("path/to"), new String[0]);
-	 * 
- * - * returns - * - *
-	 * { "Base.java", "com/parc/messages.properties", "org/aspectj/Version.java" }
-	 * 
- * - * while the call - * - *
-	 * dirPaths(new File("path/to"), new String[] { ".java" });
-	 * 
- * - * returns - * - *
-	 * { "Base.java", "org/aspectj/Version.java" }
-	 * 
- * - * @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 result = new ArrayList(); - doDirPaths(dir, result); - // if suffixes required, remove those without suffixes - if (!LangUtil.isEmpty(suffixes)) { - for (ListIterator 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 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 tempFiles; - - public FileUtilTest(String s) { - super(s); - tempFiles = new ArrayList(); - } - - public void tearDown() { - for (ListIterator 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 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 sourceList = new ArrayList(); - sourceList.add(file.getPath()); - - final ArrayList errors = new ArrayList(); - final PrintStream errorSink = new PrintStream(System.err, true) { - public void println(String error) { - errors.add(error); - } - }; - for (int i = 0; i < 10; i++) { - List 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 errors = new ArrayList<>(); - final PrintStream errorSink = new PrintStream(System.err, true) { - public void println(String error) { - errors.add(error); - } - }; - List sourceList = new ArrayList<>(); - sourceList.addAll(Arrays.asList(sources)); - sourceList = Collections.unmodifiableList(sourceList); - for (int k = 0; k < sources.length; k++) { - List result = FileUtil.lineSeek("" + k, sourceList, true, errorSink); - // number k found in every other line of every file at index k - Iterator 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 index 5a9e083e4..000000000 --- a/util/testsrc/org/aspectj/util/GenericSignatureParserTest.java +++ /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(""); - 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(";>Ljava/util/List;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;", isig.classSignature); - assertEquals("Ljava/util/List;", 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.com/google/android/gms/internal/hb$b;"); - // Note the package prefix on the nested types has been dropped - assertEquals("Lcom/google/android/gms/internal/hb.hb$b;",sig.superclassSignature.toString()); - sig = parser - .parseAsClassSignature("Lcom/a/a/b/t.com/a/a/b/af.com/a/a/b/ag;Ljava/util/ListIterator;"); - // 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;",sig.superInterfaceSignatures[0].toString()); - sig = parser.parseAsClassSignature("Lcom/google/android/gms/internal/hb.com/google/android/gms/internal/hb$b;"); - // Note the package prefix on the nested types has been dropped - assertEquals("Lcom/google/android/gms/internal/hb.hb$b;",sig.superclassSignature.toString()); - sig = parser - .parseAsClassSignature("Lcom/a/a/b/t.com/a/a/b/af.com/a/a/b/ag;Ljava/util/ListIterator;"); - // 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;",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("(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("()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("(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;"); - parser.parseAsClassSignature("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 index 4cb44795d..000000000 --- a/util/testsrc/org/aspectj/util/LangUtilTest.java +++ /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 index 463fdce2a..000000000 --- a/util/testsrc/org/aspectj/util/UtilModuleTests.java +++ /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 index 9d459aad7..000000000 --- a/util/testsrc/org/aspectj/util/UtilTests.java +++ /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); - } - -}