diff options
author | wisberg <wisberg> | 2002-12-31 18:25:37 +0000 |
---|---|---|
committer | wisberg <wisberg> | 2002-12-31 18:25:37 +0000 |
commit | 63d88f163be18c14fccd02cc88b691679eb17e93 (patch) | |
tree | 3023d14aea66fec7f3b660218564f4dfec638a66 /aspectj-attic/testing-src/org/aspectj/testing | |
parent | 897e1f63b9790c079f20dd66bd0d15f4b840bcb8 (diff) | |
download | aspectj-63d88f163be18c14fccd02cc88b691679eb17e93.tar.gz aspectj-63d88f163be18c14fccd02cc88b691679eb17e93.zip |
initial version of unused/attic code for later reference
Diffstat (limited to 'aspectj-attic/testing-src/org/aspectj/testing')
19 files changed, 3866 insertions, 0 deletions
diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/CompareFiles.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/CompareFiles.java new file mode 100644 index 000000000..2d16c9e6b --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/CompareFiles.java @@ -0,0 +1,571 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.testing.compare; + +import org.aspectj.util.LangUtil; + +import jdiff.util.Diff; +import jdiff.text.FileLine; +import jdiff.util.DiffNormalOutput; + +import java.util.*; +import java.util.zip.*; +import java.io.*; + +/** + * Compare two files and emit differences. + * Requires the jdiff library in jdiff/jdiff.jar + */ +public class CompareFiles { + protected static String[] NO_STRINGS = new String[]{}; + /** standard rendering of null references */ + public static final String NULL = "null"; + /** filter for the both files */ + protected RegexpFilter filter; + /** ignore case by converting lines to upper case */ + protected boolean ignoreCase = false; + /** collapse internal whitespace by converting to space character */ + protected boolean collapseWhitespace = true; + /** trim leading and trailing whitespace from lines before comparison */ + protected boolean trimWhitespace = false; + /** output to this File - if not set, System.out */ + protected File output = null; + + /** + * Compare two files by lines, emitting output to System.out as a series of edits. + * @param args the String[] containing two files to diff plus any number of + * <li>-i "ignore": ignore case</li> + * <li>-t "trim" : ignore leading and trailing white space</li> + * <li>-b "blanks": ignore differences in all white space</li> + * @param lhs the File on the left-hand-side of the comparison + * @param rhs the File on the left-hand-side of the comparison + * @throws IllegalArgumentException if cannot read both files + */ + public static void main(String[] args) { + new CompareFiles().comparefiles(args); + } + + /** + * Write results of a diff to some output Writer + * @param result the DiffResult containing the results of the diff + * @param output the Writer to output results to - if null, + * defaults to System.out, but will be closed if not null + * @throws IllegalArgumentException if null == result or output + */ + public static void writeDiffResult(DiffResult result, File output) + throws IOException { + if (null == result) throw new IllegalArgumentException("null result"); + Writer writer = (null != output ? new FileWriter(output) + : new OutputStreamWriter(System.out)); + DiffNormalOutput out = new DiffNormalOutput(result.lhsLines, result.rhsLines); + out.setOut(writer); + out.setLineSeparator(LangUtil.EOL); + try { + out.writeScript(result.edits); + } finally { + if (null != output) { + try { writer.close(); } + catch (IOException e) { + e.printStackTrace(System.err); + } + } + } + } // writeDiffResult + + /** + * descend filesystem tree, invoking FileRunnerI.accept() on files. + * E.g., To list files from current directory: + * <code><pre>descendFileTree(new File("."), new FileRunnerI() { + * public boolean accept(File f){ + * System.out.println(f.getAbsolutePath()); + * return true; + * }});</code></pre> + * @param file root/starting point. If a file, the only one visited. + * @param filter supplies accept(File) routine + */ + public static void descendFileTree(File file, FileFilter filter) { + descendFileTree(file, filter, false); + } + + /** + * Descend filesystem tree, invoking FileFilter.accept() on files + * and, if userRecursion, on dirs. If userRecursion, accept() must + * call descendFileTree() again to recurse down directories. + * This calls fileFilter.accept(File) on all files before doing any dirs. + * E.g., To list only files from Unix root: + * <code><pre>descendFileTree(new File("/"), new FileFilter() { + * public boolean run(File f){ + * System.out.println(f.getAbsolutePath()); + * return true; + * }}, false);</code></pre> + * To list files/dir from root using user recursion: + * <code><pre>descendFileTree(new File("/"), new FileFilter() { + * public boolean run(File f){ + * System.out.println(f.getAbsolutePath()); + * if (f.isDirectory() && (-1 == f.getName().indexOf("CVS"))) + * return descendFileTree(f, this, true); + * return true; + * }}, true);</code></pre> + * @param file root/starting point. If a file, the only one visited. + * @param filter supplies boolean accept(File) method + * @param userRecursion - if true, do accept() on dirs; else, recurse + * @return false if any fileFilter.accept(File) did. + * @throws IllegalArgumentException if file or fileFilter is null + */ + public static boolean descendFileTree(File file, FileFilter fileFilter, + boolean userRecursion) { + if (null == file) {throw new IllegalArgumentException("parm File"); } + if (null == fileFilter){throw new IllegalArgumentException("parm FileFilter");} + + if (!file.isDirectory()) { + return fileFilter.accept(file); + } else if (file.canRead()) { + // go through files first + File[] files = file.listFiles(ValidFileFilter.FILE_EXISTS); + if (null != files) { + for (int i = 0; i < files.length; i++) { + if (!fileFilter.accept(files[i])) { + return false; + } + } + } + // now recurse to handle directories + File[] dirs = file.listFiles(ValidFileFilter.DIR_EXISTS); + if (null != dirs) { + for (int i = 0; i < dirs.length; i++) { + if (userRecursion) { + if (!fileFilter.accept(dirs[i])) { + return false; + } + } else { + if (!descendFileTree(dirs[i], fileFilter,userRecursion)) { + return false; + } + } + } + } + } // readable directory (ignore unreadable ones) + return true; + } // descendFiles + + /** + * Render a zip/entry combination to String + */ + private static String renderZipEntry(File zipfile, ZipEntry entry) { + String filename = (null == zipfile ? "null File" : zipfile.getName()); + String entryname = (null == entry ? "null ZipEntry" : entry.getName()); + //return filename + "!" + entryname; + return "" + entryname; + } + + /** + * Initialise the filter. Users must do this before the filter is + * lazily constructed or must set update to true. + * @param args the String with any args valid for RegexpFilter.init(String, RegexpFilter) + * @param update if true, use existing filter settings unless + * overwritten by the new ones; + * if false, create a new filter using args. + * @throws IllegalArgumentException if cannot read both files + * @see RegexpFilter#init(String, RegexpFilter) + */ + public void initFilter(String arg, boolean update) { + filter = RegexpFilter.init(arg, update ? filter : null); + } + + /** + * Initialise the filter. Users must do this before the filter is + * lazily constructed or must set update to true. + * @param args the String[] with any args valid for RegexpFilter + * @param update if true, use existing filter settings unless + * overwritten by the new ones; + * if false, create a new filter using args. + * @throws IllegalArgumentException if cannot read both files + * @see RegexpFilter#init(String[], RegexpFilter) + */ + public void initFilter(String[] args, boolean update) { + filter = RegexpFilter.init(args, update ? filter : null); + } + + /** + * Compare two files by lines, emitting output to System.out as a series of edits. + * @param args the String[] containing two files to diff + * (lhs, rhs) plus any args valid for RegexpFilter + * @throws IllegalArgumentException if cannot read both files + */ + public final void comparefiles(String[] args) { + if (errMessage(null == args, "null args", null)) return; + if (errMessage(args.length < 2, "need more args", null)) return; + File lhs = new File(args[0]); + File rhs = new File(args[1]); + if (errMessage(!lhs.canRead(), "!lhs.canRead()", null)) return; + if (errMessage(!rhs.canRead(), "!rhs.canRead()", null)) return; + int filterArgsLength = args.length - 2; + if (0 >= filterArgsLength) { + initFilter(NO_STRINGS, false); + } else { + String[] filterArgs = new String[filterArgsLength]; + System.arraycopy(args, 0, filterArgs, 0, filterArgsLength); + initFilter(filterArgs, false); + } + + try { + if (errMessage(!diff(lhs, rhs), "diff(lhs,rhs)",null)) return; + } catch (IOException t) { + if (errMessage(false, null, t)) return; + } + } // main + + /** + * Compare two files/dirs, emitting output as a series of edits. + * If both files are directories, then this compares their contents + * (including the contents of any zip or jar files) as a series of paths + * (i.e., it will recognize added or removed or changed filenames, but + * not files whose contents have changed). + * Output will go to the File specifies as "output" or System.out if none specified. + * This is costly, creating an in-memory copy, one String per line, of both files. + * @param lhs the File/dir on the left-hand-side of the comparison + * @param rhs the File/dir on the left-hand-side of the comparison + * @throws IllegalArgumentException if either parm is null or not existing, + * or if one is a dir and the other a file + * @throws IOException if getFileLines or diff utilities do + * @return false if there was some error + */ + public final boolean diff(File lhs, File rhs) throws IOException { + DiffResult result = null; + String err = null; + if ((null == lhs) || (null == rhs) + || (!lhs.exists()) || (!rhs.exists()) + || (!lhs.canRead()) || (!rhs.canRead()) + || (lhs.isDirectory() != rhs.isDirectory())) { + err = "need 2 readable files or dirs or zip files - got lhs=" + lhs + " rhs=" + rhs; + } else if (lhs.isDirectory()) { + result = diffDirUtil(lhs, rhs); + } else { + boolean lhsIsZip = isZipFile(lhs); + if (lhsIsZip != isZipFile(rhs)) { + err = "need 2 readable files or dirs or zip files - got lhs=" + lhs + " rhs=" + rhs; + } else if (lhsIsZip) { + result = diffDirUtil(lhs, rhs); + } else { + result = diffUtil(lhs, rhs); + } + } + if (null != err) throw new IllegalArgumentException(err); + if (errMessage(null == result, null, null)) return false; + writeDiffResult(result, output); + return true; + } + + /** + * Compare two files, returning results for further evaluation or processing + * @param lhs the File on the left-hand-side of the comparison + * @param rhs the File on the left-hand-side of the comparison + * @throws IllegalArgumentException if either parm is null + * @throws IOException if getFileLines or diff utilities do + * @return false if there was some error + */ + public final DiffResult diffUtil(File lhs, File rhs) + throws IOException { + if (errMessage(null == lhs, "null lhs", null)) return null; + if (errMessage(null == rhs, "null rhs", null)) return null; + FileLine[] lhsLines = getFileLines(lhs); + FileLine[] rhsLines = getFileLines(rhs); + Diff.change edits = new Diff(lhsLines, rhsLines).diff_2(false); + return new DiffResult(lhsLines, rhsLines, edits); + } + + /** + * Read all lines of a file into a String[] (not very efficient), + * implementing flag policies. + * @param file the File to read + * @return a FileLine[] with elements for each line in the file + */ + public FileLine[] getFileLines(File file) throws IOException { + if (file.isDirectory()) return getFileLinesForDir(file); + final Vector results = new Vector(); + + BufferedReader in = null; + try { + in = getReader(file); + String line; + while (null != (line = in.readLine())) { + results.add(toFileLine(line)); + } + } finally { + if (null != in) { in.close(); } + } + final FileLine[] lines = new FileLine[results.size()]; + results.copyInto(lines); + return lines; + } + + /** + * Compare two directories or zip files by listing contents (including contents of zip/jar files) + * and differencing the results. This does NOT call diff on each file, but only + * recognizes when files or zip entries have been added or removed. + * @param lhsDir the File for an existing, readable directory (as left-hand-side) + * @param rhsDir the File for an existing, readable directory (as right-hand-side) + * @throws IllegalArgumentException if null == lhs or rhsDir + */ + public DiffResult diffDirUtil(File lhsDir, File rhsDir) { + FileLine[] lhsLines = getFileLinesForDir(lhsDir); + FileLine[] rhsLines = getFileLinesForDir(rhsDir); + // now do the comparison as if they were two files + Diff.change edits = new Diff(lhsLines, rhsLines).diff_2(false); + return new DiffResult(lhsLines, rhsLines, edits); + } + + /** + * Render all sub-elements of a directory as a list of FileLine[], including entries + * in zip and jar files. The directory prefix is not included in the FileLine. + * @param dir the File representing the directory to list + * @throws IllegalArgumentException if null == dir or !dir.isDirectory() + */ + public FileLine[] getFileLinesForDir(File dir) { + if (null == dir) throw new IllegalArgumentException("null dir"); + if (!dir.isDirectory() && ! isZipFile(dir)) throw new IllegalArgumentException("not a dir: " + dir); + Collection items = directoryToString(dir, null); + return toFileLine(items, dir.getPath(), true); + } + + /** @return true if test or if null != error */ + protected boolean errMessage(boolean test, String message, Throwable error) { + if (test && (null != message)) { System.err.println(message); } + if (null != error) { error.printStackTrace(System.err); } + return (test || (null != error)); + } + + /** + * Convert current setting into an initialization list for filter + */ + protected String[] getFilterArgs() { + return new String[] + { (trimWhitespace ? "-t" : "-T") + , (collapseWhitespace ? "-b" : "-B") + , (ignoreCase ? "-i" : "-I") + }; + } + + /** + * Lazy construction of filter + */ + protected RegexpFilter getFilter() { + if (null == filter) { + filter = RegexpFilter.init(getFilterArgs(), null); + } + return filter; + } + + /** + * Factory for reader used by getFileLines(File). + * Default implementation creates a BufferedReader. + * Subclasses may implement pre-processing filters here. + */ + protected BufferedReader getReader(File file) throws IOException { + return new BufferedReader(new FileReader(file)); + } + + /** + * Create a FileLine from the input string, + * applying policies for whitespace, etc. + * @param string the String to wrap as a FileLine + * @return FileLine with string as text and + * canonical as string modified by any canonicalizing policies. + */ + protected FileLine toFileLine(String string) { + String canonical = getFilter().process(string); + return new FileLine(string, canonical); + } + + + protected boolean isZipFile(File f) { + String s = null; + if ((null == f) || (null == (s = f.getPath()))) { + return false; + } else { + return (f.exists() && !f.isDirectory() && (s.endsWith(".jar"))); + } + } + + /** + * Convert to an array of FileLine by optionally removing prefix and/or sorting + * @param collection the Collection of String to process + */ + protected FileLine[] toFileLine(Collection collection, String ignorePrefix, boolean sort) { + if (null == collection) throw new IllegalArgumentException("null collection"); + List list = new ArrayList(); + list.addAll(collection); + if (null != ignorePrefix) { + for (int i = 0; i < list.size(); i++) { + String next = list.get(i).toString(); + if (next.startsWith(ignorePrefix)) { + list.set(i, next.substring(ignorePrefix.length())); + } + } + } + if (sort) { + Collections.sort(list); + } + FileLine[] result = new FileLine[list.size()]; + int i = 0; + for (Iterator it = list.iterator(); it.hasNext();) { + result[i++] = toFileLine(it.next().toString()); + } + if (i < result.length) { + throw new Error("list lost elements? " + (result.length-i)); + } + return result; + } + + /** + * Return the names of all files below a directory. + * If file is a directory, then all files under the directory + * are returned. If file is absolute or relative, all the files are. + * If file is a zip or jar file, then all entries in the zip or jar + * are listed. Entries inside those jarfiles/zipfiles are not listed. + * There are no guarantees about ordering. + * @param dir the File to list for + * @param results the Collection to use for the results (may be null) + * @throws IllegalArgumentException if null == dir + * @return a Collection of String of paths, including paths inside jars + */ + protected Collection directoryToString(File dir, Collection results) { + if (null == dir) throw new IllegalArgumentException("null dir"); + final Collection result = (results != null? results : new Vector()); + if (isZipFile(dir)) { + zipFileToString(dir, result); + } else if (!dir.isDirectory()) { + throw new IllegalArgumentException("not a dir: " + dir); + } else { + AccumulatingFileFilter acFilter = new AccumulatingFileFilter() { + public boolean accumulate(File file) { + String name = file.getPath(); + result.add(name); + if (isZipFile(file)) { + zipFileToString(file, result); + } + return true; + } + }; + descendFileTree(dir, acFilter, false); + } + return result; + } // directoryToString + + /** + * Render as String the entries in a zip or jar file, + * converting each to String beforehand (as jarpath!jarentry) + * applying policies for whitespace, etc. + * @param file the File to enumerate ZipEntry for + * @param results the Colection to use to return the FileLine - may be null + * @return FileLines with string as text and + * canonical as string modified by any canonicalizing policies. + */ + protected Collection zipFileToString(final File zipfile, Collection results) { + Collection result = (results != null ? results : new Vector()); + ZipFile zip = null; + try { + //ZipFile.OPEN_READ | ZipFile.OPEN_DELETE); delete is 1.3 only + zip = new ZipFile(zipfile); + Enumeration enum = zip.entries(); + // now emitting filename only once, so entries match even if filename does not + results.add("ZipFileName: " + zipfile); + while (enum.hasMoreElements()) { + results.add(renderZipEntry(zipfile, (ZipEntry) enum.nextElement())); + } + zip.close(); + zip = null; + } catch (Throwable t) { + String err = "Error opening " + zipfile + " attempting to continue..."; + System.err.println(err); + t.printStackTrace(System.err); + } finally { + if (null != zip) { + try { zip.close(); } + catch (IOException e) { + e.printStackTrace(System.err); + } + } + } + return result; + } + + /** + * return structure for diffUtil + */ + public final class DiffResult { + public final FileLine[] lhsLines; + public final FileLine[] rhsLines; + public final Diff.change edits; + public DiffResult (FileLine[] lhsLines, + FileLine[] rhsLines, + Diff.change edits) { + this.lhsLines = lhsLines; + this.rhsLines = rhsLines; + this.edits = edits; + } + } + +} // class CompareFiles + +class ValidFileFilter implements FileFilter { + public static final FileFilter EXIST = new ValidFileFilter(); + public static final FileFilter FILE_EXISTS = new FilesOnlyFilter(); + public static final FileFilter DIR_EXISTS = new DirsOnlyFilter(); + protected ValidFileFilter(){} + public boolean accept(File f) { + return ((null != f) && (f.exists())); + } + static class FilesOnlyFilter extends ValidFileFilter { + public final boolean accept(File f) { + return (super.accept(f) && (!f.isDirectory())); + } + } + static class DirsOnlyFilter extends ValidFileFilter { + public final boolean accept(File f) { + return (super.accept(f) && (f.isDirectory())); + } + } +} + +/** + * A FileFilter that accumulates the results when called if they exist. + * Subclasses override accumulate to determine whether it should be + * accumulated. + */ +class AccumulatingFileFilter extends ValidFileFilter { + Vector files = new Vector(); + public final boolean accept(File f) { + if (super.accept(f) && (accumulate(f))) { + files.add(f); + } + return true; + } + + /** + * This implementation accumulates everything. + * Subclasses should override to implement filter + * @param file a File guaranteed to exist + * @return true if file should be accumulated. + */ + public boolean accumulate(File f) { + return true; + } + /** + * @return list of files currently accumulated + */ + public File[] getFiles() { + return (File[]) files.toArray(new File[0]); + } +} diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/CompareUtil.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/CompareUtil.java new file mode 100644 index 000000000..8358a177d --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/CompareUtil.java @@ -0,0 +1,143 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.testing.compare; + +import java.util.Iterator; +import java.util.Collection; +import java.util.List; + +/** Minor methods for short-circuiting comparisons */ +public class CompareUtil { + /** + * Callers may abort equality checks with false + * if this passes its misc short-circuit semantics. + * A false result does not mean the arguments + * are equal, but a true result does mean they are not equal. + * <pre>if (notSame(foo,bar,true)) then return false;</pre> + * @param lhs the Object on the left-hand-side to compare + * @param rhs the Object on the right-hand-side to compare + * @param considerType if true, then also return true if the + * right-hand-side cannot be assigned to the left-hand-side + * @return true if lhs and rhs are not the same per considerType. + */ + public static boolean notSame(Object lhs, Object rhs, boolean considerType) { + if (null == lhs) { + return (!(null == rhs)); + } else if (null == rhs) { + return true; + } else if (lhs== rhs) { + return false; // known to be same + } else if (considerType) { + Class lhClass = lhs.getClass(); + Class rhClass = rhs.getClass(); + if (!lhClass.isAssignableFrom(rhClass)) { + return true; + } + } + return false; // unknown whether equal or not + } + + /** + * Return null/equal comparison: + * <li>null considered to be lesser</li> + * <li>reference or Object.equals() considered to be 0</li> + * <li>return Integer.MAX_VALUE for all other cases</li> + * <table> + * <tr><td>result</td><td>input</td></tr> + * <tr><td>-1</td><td>null < rhs</td></tr> + * <tr><td>1</td><td>lhs > null</td></tr> + * <tr><td>0</td><td>null == null</td></tr> + * <tr><td>0</td><td>lhs == rhs</td></tr> + * <tr><td>0</td><td>lhs.equals(rhs)</td></tr> + * <tr><td>Integer.MAX_VALUE</td><td>{all other cases}</td></tr> + * </table> + * @see Comparator + * @return Integer.MAX_VALUE if uncertain, value otherwise + */ + public static int compare(Object lhs, Object rhs) { + if (null == lhs) { + return (null == rhs ? 0 : -1); + } else if (null == rhs) { + return 1; + } else if (lhs == rhs) { + return 0; // known to be same + } else { + return Integer.MAX_VALUE; + } + } + + /** + * Return boolean comparison where true > false. + * (Comparable not defined for Boolean) + * @see Comparator + */ + public static int compare(boolean lhs, boolean rhs) { + return (lhs == rhs ? 0 : (lhs ? 1 : -1)); + } + + /** + * Return String comparison based on {@link compare(Object,Object)} + * and {@link String.compareTo(String)}. + * @see Comparator + */ + public static int compare(String lhs, String rhs) { + int result = compare((Object) lhs, (Object) rhs); + if (Integer.MAX_VALUE == result) { + result = lhs.compareTo(rhs); + } + return result; + } + + /** + * Compare two Collections by reference to a standard List. + * The first Collection to not contain a standard element + * when the other does loses. Order is ignored. + * The left-hand-side acts as the standard if the standard is null. + * @param lhs the List from the left-hand-side + * @param rhs the Collection from the right-hand-side + * @param standard the List to act as the standard (if null, use lhs) + * @param return -1 if lhs is null and rhs is not, 1 if reverse; + * 0 if both have all elements in standard, + * 1 if lhs has a standard element rhs does not, -1 if reverse + * (testing in standard order) + */ + public static int compare(List lhs, Collection rhs, List standard) { + int result = compare(lhs, rhs); + if (result == Integer.MAX_VALUE) { + if (null == standard) { + result = compare(lhs, rhs, lhs); // use lhs as standard + } else { + boolean leftHasThem = lhs.containsAll(standard); + boolean rightHasThem = rhs.containsAll(standard); + if (leftHasThem != rightHasThem) { + result = (leftHasThem ? 1 : -1); + } else if (leftHasThem) { + result = 0; // they both have them + } else { // first to not have an element loses + Iterator standardIterator = standard.iterator(); + while (standardIterator.hasNext()) { + Object standardObject = standardIterator.next(); + boolean leftHasIt = lhs.contains(standardObject); + boolean rightHasIt = rhs.contains(standardObject); + if (leftHasIt != rightHasIt) { + result = (leftHasIt ? 1 : -1); + break; + } + } + } + } + } + return result; + } +} // class Util diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNode.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNode.java new file mode 100644 index 000000000..cdb0b70da --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNode.java @@ -0,0 +1,427 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.testing.compare; + +import java.util.*; + + +/** + * Generic tree and pairwise traversal over it + * for comparison purposes. + * Implement a generic tree by delegation and + * a traversal method for comparing two trees. + * Guarantees after initialization: + * <li>nodeComparator is not null</li> + * <li>node is not null</li> + * <li>children, if any, are assignable to childClass + * (only as of initialization, since list is adopted)</li> + * <li>Obeys GenericTreeNode contract, especially regarding equals</li> + * <p>It will throw Error if used before initialization completes. + */ +public class GenericTreeNode implements Comparable { + /** underlying node - never null */ + Object node; + /** node children - type-safe if present */ + List children; + /** node parent - may be null if this is tree root */ + GenericTreeNode parent; + /** used to compare underlying node */ + Comparator nodeComparator; + /** Class used to verify children - may be null */ + Class childClass; + + /** Track whether we are initialized */ + boolean ready; + /** cache for heavy toString() */ + String toStringCache; + + // --------------------------------------- static + /** Singleton visitor to match trees exactly, stopping at first failure */ + public static final GenericTreeNodesVisitorI EXACT = new MatchExact(); + + /** Singleton visitor to print non-matching nodes to System.err */ + public static final GenericTreeNodesVisitorI PRINTERR = new PrintNonMatches(); + + /** Singleton visitor to print all nodes to System.err */ + public static final GenericTreeNodesVisitorI PRINTALL = new PrintAllPairs(); + + + /** Singleton comparator for all of us */ + public static final Comparator COMPARATOR = new gtnComparator(); + + /** + * Visit two generic trees in order. + * Visit the parents, and recursively visit the children. + * The children are visited in an order determined by + * (any) ordering factory invoked to produce an ordered pair + * of children lists for visiting. + * All children are visited, even if not paired. + * When one list of children is smaller, the + * remainder are visited with null complements. + * This is true for parents as well; this will visit + * all the children of a parent even when the + * other parent is null. + * This will return false without further visits + * as soon as the visitor returns false. That means + * the visitor can decide to stop visiting when null + * nodes (parent or children) are encountered). + * This will return true only after the tree was + * visited without objection from the visitor. + * <p>A simple example of using this to determine if + * two trees are strictly equal is this: + * @return false if the visitor returned false for any visit; + * true otherwise. + * @throws Error if any children are not GenericTreeNode + * @throws IllegalArgumentException if the visitor or both parents are null + */ + + public static boolean traverse(GenericTreeNode lhsParent + , GenericTreeNode rhsParent + , GenericTreeNodeListOrdererFactoryI childrenPrepFactory + , GenericTreeNodesVisitorI visitor) { + if (null == visitor) { + throw new IllegalArgumentException("null visitor"); + } + if ((null == lhsParent) && (null == rhsParent)) { + throw new IllegalArgumentException("null parents"); + } + if (visitor.visit(lhsParent, rhsParent)) { + List lhsList = (null == lhsParent ? null : lhsParent.getChildren()); + List rhsList = (null == rhsParent ? null : rhsParent.getChildren()); + if (null != childrenPrepFactory) { + GenericTreeNodeListOrdererI factory = + childrenPrepFactory.produce(lhsParent, rhsParent, visitor); + if (null != factory) { + List[] prepKids = factory.produceLists(lhsList, rhsList); + if (null != prepKids) { + lhsList = prepKids[0]; + rhsList = prepKids[1]; + } + } + + } + ListIterator lhsIterator = (null == lhsList ? null : lhsList.listIterator()); + ListIterator rhsIterator = (null == rhsList ? null : rhsList.listIterator()); + Object lhs = null; + Object rhs = null; + while (true) { + lhs = null; + rhs = null; + // get the child pair + if ((null != lhsIterator) && (lhsIterator.hasNext())) { + lhs = lhsIterator.next(); + } + if ((null != rhsIterator) && (rhsIterator.hasNext())) { + rhs = rhsIterator.next(); + } + if ((null == rhs) && (null == lhs)) { + break; + } + if ((null != lhs) && (!(lhs instanceof GenericTreeNode))) { + throw new Error("GenericTreeNode expected, got lhs " + lhs); + } + if ((null != rhs) && (!(rhs instanceof GenericTreeNode))) { + throw new Error("GenericTreeNode expected, got rhs " + rhs); + } + // traverse the child pair + if (!traverse((GenericTreeNode) lhs, (GenericTreeNode) rhs, + childrenPrepFactory, visitor)) { + return false; + } + } + } + return true; // see also other return statements above + } // traverse + + // --------------------------------------- constructors + public GenericTreeNode() { + //ready = false; + } + + public void init(GenericTreeNode parent, Comparator nodeComparator, + Object node, List children, Class childClass) { + if (ready) ready = false; // weak sync + if (null == node) { + throw new IllegalArgumentException("null node"); + } + if (null == nodeComparator) { + throw new IllegalArgumentException("null nodeComparator"); + } + this.nodeComparator = nodeComparator; + this.node = node; + this.children = children; + this.parent = parent; + if (null != childClass) { + ListIterator iter = children.listIterator(); + while (iter.hasNext()) { + Object kid = iter.next(); + if (!(childClass.isAssignableFrom(kid.getClass()))) { + String err = "child " + kid + " is not " + childClass; + throw new IllegalArgumentException(err); + } + } + this.childClass = childClass; + } + ready = true; + } + + //-------------------- Object interface + /** + * ambiguous: equal if + * <li>this is the input, or </li> + * <li>the input is a GenericTreeNode, and </li> + * <li>the underlying nodes are equal(), or</li + * <li>the underlying nodes have equal comparators + * which return 0 from compare(...)</li> + * @param input the Object to compare with + * @return true if this equals input + */ + public boolean equals(Object input) { + if (input == this) { + return true; + } else if (!(input instanceof GenericTreeNode)) { + return false; + } else { + GenericTreeNode in = (GenericTreeNode) input; + if (node.equals(in.getNode())) { // assumes nodes are unique, not flyweights? + return true; + } else { + Comparator inComp = in.getNodeComparator(); + if ((this == nodeComparator) + || (this == inComp) + || (nodeComparator == inComp) + || (nodeComparator.equals(inComp))) { + return (0 == nodeComparator.compare(this, in)); + } else { + return false; + } + } + } + } + + /** + * Delegate to the underlying node object + * @return the hashcode of the underlying node object + */ + public int hashCode() { // todo: probably not correct to delegate to node + return node.hashCode(); + } + + /** toString delegates to longString() */ + public String toString() { + if (null == toStringCache) { + toStringCache = longString(); + } + return toStringCache; + } + + /** + * short rendition delegates to thisString, + * prefixing by one tab per parent + */ + public String shortString() { + StringBuffer sb = new StringBuffer(); + // add a tab for each parent + GenericTreeNode par = parent; + while (null != par) { + sb.append("\t"); + par = par.getParent(); + } + sb.append(thisString()); + return sb.toString(); + } + + /** + * long rendition delegates to parentString, thisString, + * and childString as follows: + * <pre>GenericTreeNode [parent={parentString()] [children={size}] [Node={thisString()]</pre> + */ + public String longString() { + StringBuffer sb = new StringBuffer(); + sb.append("GenericTreeNode "); + sb.append("[parent="); + sb.append(parentString()); + sb.append("] [children="); + if (null == children) { + sb.append("0"); + } else { + sb.append("" + children.size()); + } + sb.append("] [Node="); + sb.append(thisString()); + sb.append("]"); + return sb.toString(); + } + + /** render this as "[root] [next] ... [parent]" */ + protected String thisString() { + return node.toString(); + } + + /** render parent hierarchy as "[root] [next] ... [parent]" */ + protected String parentString() { + if (null == parent) { + return "[null]"; + } else { + Vector parents = new Vector(); + GenericTreeNode par = parent; + do { + parents.addElement(par); + par = par.getParent(); + } while (null != par); + int size = parents.size(); + StringBuffer sb = new StringBuffer(32*size); + par = (GenericTreeNode) parents.remove(--size); + sb.append("["); + do { + sb.append(par.toString()); + par = (size<1?null:(GenericTreeNode) parents.remove(--size)); + if (null != par) { + sb.append("] ["); + } + } while (null != par); + sb.append("]"); + return sb.toString(); + } + } // parentString() + + // -------------------------- Comparable interface + /** + * Comparable just delegates to Comparator. + * i.e., <code>return compare(this, rhs)</code> + * @param rhs the Object to compare from the right + * @throws ClassCastException if either is not instanceof GenericTreeNode + * @return 0 if equal, >0 if this is greater than rhs, <0 otherwise. + */ + public int compareTo(Object rhs) { + return COMPARATOR.compare(this, rhs); + } + + + //-------------------- GenericTreeNode interface + /** @return the actual underlying node */ + public Object getNode() { assertReady(); return node ; } + /** @return the actual underlying node */ + public GenericTreeNode getParent() { assertReady(); return parent ; } + /** @return List of children - null if none */ + public List getChildren() { assertReady(); return children ; } + /** @return Comparator used for comparing node (not children) */ + public Comparator getNodeComparator() { assertReady(); return nodeComparator; } + /** @return Class which any children can be assigned to */ + public Class getChildClass() { assertReady(); return childClass; } + + //-------------------- misc + protected void assertReady() { + if (!ready) throw new Error("not ready"); + } + + //-------------------- inner classes + /** + * This relies entirely on the GenericTreeNode implementation of equals() + * which delegates to the underlying node equals() or the comparator + */ + static class MatchExact implements GenericTreeNodesVisitorI { + public boolean visit(GenericTreeNode lhs,GenericTreeNode rhs) { + return (null == lhs ? (null == rhs) : lhs.equals(rhs)); + } + } + + /** + * This prints non-matching pairs to System.err, + * returning true always. + */ + static class PrintNonMatches implements GenericTreeNodesVisitorI { + public boolean visit(GenericTreeNode lhs,GenericTreeNode rhs) { + if (! (null == lhs ? (null == rhs) : lhs.equals(rhs))) { + System.err.println("[lhs=" + lhs + "] [rhs=" + rhs + "]"); + } + return true; + } + } + + /** + * This prints all pairs to System.err, returning true always. + */ + static class PrintAllPairs implements GenericTreeNodesVisitorI { + public boolean visit(GenericTreeNode lhs,GenericTreeNode rhs) { + System.err.println("[lhs=" + lhs + "] [rhs=" + rhs + "]"); + return true; + } + } + + /** + * have to separate to distinguish + * gtn.equals() from gtnComparator.equals + */ + static class gtnComparator implements Comparator { + public boolean equals(Object o) { + if (null == o) return false; + return ( o instanceof gtnComparator ); + } + public int hashCode() { + return gtnComparator.class.hashCode(); + } + // -------------------------- Comparator interface + /** + * Shallow compare of two GenericTreeNodes. + * <li>any null component translates to "less than" another null component<li> + * <li>comparators must be equal according to + * <code>Comparator.equals(Comparator)</code><li> + * <li>underlying nodes equal according to + * <code>Comparator.compare(Object lhs, Object rhs)</code><li> + * <li>if the two comparators are not equal, then + * do arbitrary ordering of the node by toString().compareTo() + * of the underlying objects</li> + * <li>children are ignored. Two nodes may be equal even if + * they do not have the same children.</li> + * @param lhs the Object to compare from the left + * @param rhs the Object to compare from the right + * @throws ClassCastException if either is not instanceof GenericTreeNode + */ + public int compare(Object lhs, Object rhs) { + int result = CompareUtil.compare(lhs, rhs); + if (Integer.MAX_VALUE == result) { + GenericTreeNode lhsNode = (GenericTreeNode) lhs; + GenericTreeNode rhsNode = (GenericTreeNode) rhs; + Object lhObject = lhsNode.getNode(); + Object rhObject = rhsNode.getNode(); + result = CompareUtil.compare(lhObject, rhObject); // + if (Integer.MAX_VALUE == result) { + Comparator lhComp = lhsNode.getNodeComparator(); + Comparator rhComp = rhsNode.getNodeComparator(); + result = CompareUtil.compare(lhComp, rhComp); + if ((Integer.MAX_VALUE == result) + || ((0 == result) && (null == lhComp))) { + // tricky: comparators are not equal or null, - todo + // so unable to determine standard of ordering. + // impose a consistent but arbitrary ordering + // on the underlying objects + System.err.println(" -- result: " + result + " lhComp " + lhComp); + result = lhObject.toString().compareTo(rhObject.toString()); + } else if (0 == result) { // ok to use comparator + result = lhComp.compare(lhsNode.getNode(), rhsNode.getNode()); + } else { + String err = "Program error - result: " + result + + " lhComp: " + lhComp + " rhComp: " + rhComp; + throw new Error(err); + } + } + } + return result; + } // compare + } + +} // class GenericTreeNode + diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNodeListOrdererFactoryI.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNodeListOrdererFactoryI.java new file mode 100644 index 000000000..c98549829 --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNodeListOrdererFactoryI.java @@ -0,0 +1,30 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.testing.compare; + +/** + * Factory to produce orderers per children list pair. + * Using a factory permits generation/selection of the right orderer + * for each set of children to be compared, potentially considering + * the visitor expectations. Note that while orderers may change during + * traversals, visitors do not. + */ +public interface GenericTreeNodeListOrdererFactoryI { + /** + * Produce the correct orderer for the children of the given GenericTreeNodes + * @return GenericTreeNodeListOrdererI for children, or null if none to be used + */ + public GenericTreeNodeListOrdererI produce(GenericTreeNode lhs, GenericTreeNode rhs, + GenericTreeNodesVisitorI visitor); +} diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNodeListOrdererI.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNodeListOrdererI.java new file mode 100644 index 000000000..7f5e2bd07 --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNodeListOrdererI.java @@ -0,0 +1,44 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.testing.compare; + +import java.util.List; + +/** + * Puts two lists of GenericTreeNode children + * in traversal order for comparison purposes. + * Given two lists, produce two lists which + * should be the comparison order of the two. + * In the case of set comparison, this will + * impose an ordering on the produced lists + * such that the equal elements are pairs. + * In the case of list comparison, it may + * impose an ordering if the comparator itself + * does not. + * All Lists must contain only GenericTreeNode. + */ +public interface GenericTreeNodeListOrdererI { + /** + * Produce lists representing the proper transformation of + * the children for a given visitor. + * To use the existing lists, return them in a new array. + * You may return null for one or both Lists. + * @param lhs the List representing the left-hand-side + * which contains only GenericTreeNode + * @param rhs the List representing the right-hand-side + * which contains only GenericTreeNode + * @return two lists List[] (0 => lhs, 1 => rhs) + */ + public List[] produceLists(List lhs, List rhs); +} diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNodesVisitorI.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNodesVisitorI.java new file mode 100644 index 000000000..da1dcf2a5 --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNodesVisitorI.java @@ -0,0 +1,31 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.testing.compare; +/** + * Visit a node-pair in the trees, + * signalling whether to continue traversal. + */ +public interface GenericTreeNodesVisitorI { + /** + * Visit nodes in parallel trees. + * One (but not both) of the input nodes may be null. + * @param lhs the GenericTreeNode on the left-hand-side of a binary operation + * (may be null) + * @param lhs the GenericTreeNode on the right-hand-side of a binary operation + * (may be null) + * @return true if should continue visiting + */ + public boolean visit(GenericTreeNode lhs, GenericTreeNode rhs); +} + diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/Regexp.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/Regexp.java new file mode 100644 index 000000000..c32310cd7 --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/Regexp.java @@ -0,0 +1,40 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.testing.compare; + +import java.util.Vector; + +/** Generalize regular expression interface (to avoid binding to regexp provider)*/ +public interface Regexp { + /** @return the substrings matched in argument by this regular expression */ + public Vector getGroups(String argument); + + /** @return true if argument is matched by this regular expression */ + public boolean matches(String argument); + + /** + * Set pattern used in this regular expression. + * May throw Exception if the pattern can be determined to be illegal + * during initialization. + * @throws Exception if pattern is illegal + */ + public void setPattern(String pattern) throws Exception; + + /** + * @return a string representaion of the pattern + * (may not be legal or the input) + */ + public String getPattern() ; +} + diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/RegexpFactory.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/RegexpFactory.java new file mode 100644 index 000000000..0c7bccc14 --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/RegexpFactory.java @@ -0,0 +1,64 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +// todo: non-distribution license? + +package org.aspectj.testing.compare; + +// currently in aspectj-external-lib/regexp +import org.apache.regexp.RE; + +import java.util.Vector; + + +/** Factory for our Regexp. */ +public class RegexpFactory { + public static Regexp makeRegexp() { + return new RegExpDelegate(); + } +} + +/** Implement Regexp by delegating to org.apache.regexp.RE */ +final class RegExpDelegate implements Regexp { + String pattern; + RE regexp; + public RegExpDelegate() { } + public Vector getGroups(String arg) { + String label = "getGroups(\"" + arg + "\") "; + D.log(label); + Vector result = null; + if ((null != arg) && (matches(arg))) { + int size = regexp.getParenCount(); + D.log(label + " size " + size); + result = new Vector(size); + for (int i = 0; i < size; i++) { + Object item = regexp.getParen(i); + result.addElement(item); + D.log(label + i + ": " + item); + } + } + return result; + } + public boolean matches(String arg) { + return ((null != regexp) && regexp.match(arg)); + } + public void setPattern(String pattern) throws Exception { + this.pattern = pattern; + regexp = new RE(this.pattern); + D.log("RE: " + regexp + " pattern: /" + pattern + "/"); + } + public String getPattern() { + return pattern; + } +} + diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/RegexpFilter.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/RegexpFilter.java new file mode 100644 index 000000000..7ea6c7ecf --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/RegexpFilter.java @@ -0,0 +1,622 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +// todo: non-distribution license? +package org.aspectj.testing.compare; + +import org.aspectj.testing.compare.Regexp; + +import java.util.*; +import java.io.*; + + +/** utility class for logging */ +class D { + public static boolean LOG = false; + public static void log(String s) { if (LOG) System.err.println("## " + s); } + static { + try { + LOG = (null != System.getProperty("DEBUG")); + } catch (Throwable t) { + // ignore + } + } +} + +/** utility class for handling errors */ +class ErrHandler { + public static final ErrHandler DEFAULT = new ErrHandler(); + public static final Action INFO = new Action("info") { + public void invoke(String message) { System.out.println("INFO: " + message); } }; + public static final Action WARN = new Action("warn") { + public void invoke(String message) { System.err.println("WARNING: " + message); } }; + public static final Action HALT = new Action("halt") { + public void invoke(String message) { throw new RuntimeException(message); } }; + public static final Action ABORT = new Action("abort") { + public void invoke(String message) { throw new Error(message); } }; + public static final Action EXIT = new Action("exit") { + public void invoke(String message) { System.err.println(message); System.exit(1); } }; + abstract static class Action { + protected final String name; + private Action(String name) { + this.name = name.toLowerCase().trim(); + } + abstract public void invoke(String message); + public String toString() { return name; } + } + public static final void handleErr(String message, Throwable t) { + DEFAULT.handle(message, t); + } + public static final void handleErr(String message) { + DEFAULT.handle(message); + } + public static final void handleErr(String message, Action suggestion) { + DEFAULT.handle(message, suggestion); + } + public void handle(String message) { handle(message, INFO); } + public void handle(String message, Throwable t) { + String eMessage = (null == t ? "" : + t.getClass().getName() + ": " + t.getMessage()); + handle(message + eMessage, HALT); + } + /** + * The default implementation just takes the suggested action + * @param message the String to pass to any Action + * @param suggestion the Action proposed by the caller + */ + public void handle(String message, Action suggestion) { + suggestion.invoke(message); + } +} + +/* old comments, not correct: + * <li>test line against all registered select statements + * to get all the matching (replace) operations (unsupported)</li> + * The algorithm is greedy in that if the user requests a line + * and the default is no-output, it will read lines from the input + * until one is matched (or EOF). + */ + +/** + * Process files in a minimal version of sed: + * <li>read line using superclass LineNumberReader.readLine()</li> + * <li>Preprocess with case and white space operations</li> + * <li>run all the replace operations on the input, in order</li> + * <li>return the line.</li> + * Using anything but the <code>readLine()</code> method will circumvent + * the regular expression replacement processing. + */ +public class RegexpFilter { + protected static final String[] NO_STRINGS = new String[]{}; + + // ---------------------------------------------- static methods + /** + * Process file (or System.in) like sed. + * This only calls <code>RegexpFilterReader.main(args)</code>. + * @param args same as for init(String[], RegexpFilter) + */ + public static void main(String[] args) throws IOException { + RegexpFilterReader.main(args); + } + + // todo: move String -> String[] (commas) out into utility + /** + * Initialize a RegexpFilter based on command-line style arguments + * in a single String. (Otherwise, same as + * <code>init(String[], RegexpFilter)</code>) + * The Strings are separated at , (unless \ escaped) and trimmed. + * Note that the escape characters are removed from before the ,. + * @param spec the String to break into String[] + * @param toSet the RegexpFilter to initialize - if null, construct one from + * the file argument or stdin if there is no file argument. + */ + public static RegexpFilter init(String arg, RegexpFilter toSet) { + if ((null == arg) || (1 > arg.length())) { + return init(NO_STRINGS, toSet); + } + StringTokenizer st = new StringTokenizer(arg, ","); + Vector result = new Vector(); + String last = null; + String next; + while (st.hasMoreTokens()) { + next = st.nextToken(); + if (next.endsWith("\\") && (st.hasMoreTokens())) { + next = next.substring(0, next.length()-1); + last = last == null ? next : last + next; + continue; + } + if (null != last) { + next = last + next; + last = null; + } + result.add(next.trim()); + } + String[] args = new String[result.size()]; + result.copyInto(args); + return RegexpFilter.init(args, toSet); + } + + /** + * Initialize a RegexpFilter based on command-line style arguments. + * This is the only way (currently) to set up a RegexpFilter. + * syntax: <code>{file | {-i|-t|-b|-s <pattern>|-s <patternFile>}..}</code> + * (for booleans, use lowercase to enable, uppercase to disable). + * @param args the String[] containing file to input plus any number of... + * <li>-i "ignore": ignore case</li> + * <li>-t "trim" : ignore leading and trailing white space</li> + * <li>-b "blanks": ignore differences in all white space</li> + * <li>-s "{s/pattern/expression/};...": + * replace pattern in lines with expression</li> + * <li>-S <file> : same as s, but read commands from file</li> + * @param toSet the RegexpFilter to initialize - if null, construct one from + * the file argument or stdin if there is no file argument. + */ + public static RegexpFilter init(String[] args, RegexpFilter toSet) { + final String syntax = " - syntax: {file | {-i|-t|-b|-s <pattern>|-s <patternFile>}..}"; + RegexpFilter result = (null != toSet ? toSet : new RegexpFilter()); + if ((null != args) && (0 < args.length)) { + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + if ((null == arg) || (1 > arg.length())) continue; + if (arg.startsWith("-")) { + switch (arg.charAt(1)) { + case 'i' : result.ignoreCase = true; break; + case 'I' : result.ignoreCase = false; break; + case 'b' : result.collapseWhitespace = true; break; + case 'B' : result.collapseWhitespace = false; break; + case 't' : result.trimWhitespace = true; break; + case 'T' : result.trimWhitespace = false; break; + case 's' : ++i; + if (i < args.length) { + result.getOperationList().addOperation(args[i]); + } else { + String err = "need arg after -s " + syntax; + ErrHandler.handleErr(err, ErrHandler.WARN); + } + break; + case 'S' : ++i; + if (i < args.length) { + result.getOperationList().addFile(args[i]); + } else { + String err = "need arg after -s " + syntax; + ErrHandler.handleErr(err, ErrHandler.WARN); + } + break; + default: + String err = "unrecognized flag : " + arg + syntax; + ErrHandler.handleErr(err, ErrHandler.WARN); + break; + } + } else if (null != result) { + ErrHandler.handleErr("unexpected arg " + arg + syntax, ErrHandler.WARN); + break; + } else { // unflagged argument, need file - should be input file + File _file = new File(arg); + if (_file.exists() && _file.canRead()) { + result.setFile(_file); + } + } + } // reading args + } // have args + return result; + } // init + + // ---------------------------------------------- instance fields + /** ignore case by converting lines to upper case */ + protected boolean ignoreCase = false; + /** collapse internal whitespace by converting to space character */ + protected boolean collapseWhitespace = true; + /** trim leading and trailing whitespace from lines before comparison */ + protected boolean trimWhitespace = false; + /** replace input per replace operations */ + protected boolean replace = false; + /** operations to process the file with */ + protected OperationList operations; + /** handler for our errors*/ + protected ErrHandler handler = ErrHandler.DEFAULT; + /** the File to use */ + protected File file = null; + + // ---------------------------------------------- constructors + /** no getter/setters yet, so construct using + * <code>static RegexpFilter init(String[],RegexpFilter)</code> + */ + protected RegexpFilter() { } + + // ---------------------------------------------- instance methods + + + /** + * Set a file for this RegexpFilter. + * This makes command-line initialization easier. + * @param file the File to set for this RegexpFilter + */ + public void setFile(File file) { this.file = file; } + + /** + * Return file this RegexpFilter was initialized with. + * @return the File this RegexpFilter was initialized with (may be null). + */ + public File getFile() { return file; } + + /** + * Lazy construction of operations list + */ + protected OperationList getOperationList() { + if (null == operations) { + operations = new OperationList(); + replace = true; + } + return operations; + } + + /** + * Process line, applying case and whitespace operations + * before delegating to replace. + * @param string the String to proces + * @return the String as processed + */ + protected String process(String string) { + String label = "process(\"" + string + "\")"; + D.log(label); + if (null == string) return null; + String result = string; + if (ignoreCase) { + result = result.toUpperCase(); + } + if (trimWhitespace) { + result = result.trim(); + } + if (collapseWhitespace) { + final StringBuffer collapse = new StringBuffer(); + StringTokenizer tokens = new StringTokenizer(result); + boolean hasMoreTokens = tokens.hasMoreTokens(); + while (hasMoreTokens) { + collapse.append(tokens.nextToken()); + hasMoreTokens = tokens.hasMoreTokens(); + if (hasMoreTokens) { + collapse.append(" "); + } + } + result = collapse.toString(); + } + if (replace) { + result = getOperationList().replace(result); + D.log(label + " result " + result); + } + return result; + } + + /** + * container for ReplaceOperations constructs on add, + * runs operations against input. + */ + class OperationList { + final ArrayList list; + public OperationList() { + list = new ArrayList(); + } + + /** + * Run input through all the operations in this list + * and return the result. + * @param input the String to process + * @return the String result of running input through all replace + * operations in order. + */ + public String replace(String input) { + if (null == input) return null; + Iterator operations = operations(); + while (operations.hasNext()) { + ReplaceOperation operation = (ReplaceOperation) operations.next(); + input = operation.replace(input); + } + return input; + } + + /** + * Add operations read from file, one per line, + * ignoring empty lines and # or // comments. + * ';' delimits operations within a line as it does + * for addOperation(String), so you must \ escape ; + * in the search or replace segments + */ + public void addFile(String path) { + if (null == path) { + handler.handle("null path", ErrHandler.ABORT); + } else { + File file = new File(path); + if (!file.exists() && file.canRead()) { + handler.handle("invalid path: " + path, ErrHandler.ABORT); + } else { + BufferedReader reader = null; + int lineNumber = 0; + String line = null; + try { + reader = new BufferedReader(new FileReader(file)); + while (null != (line = reader.readLine())) { + lineNumber++; + int loc = line.indexOf("#"); + if (-1 != loc) { + line = line.substring(0,loc); + } + loc = line.indexOf("//"); + if (-1 != loc) { + line = line.substring(0,loc); + } + line = line.trim(); + if (1 > line.length()) continue; + addOperation(line); + } + } catch (IOException e) { + String message ="Error processing file " + path + + " at line " + lineNumber + ": \"" + line + "\"" + + ": " + e.getClass().getName() + ": " + e.getMessage() ; + handler.handle(message, ErrHandler.ABORT); + } finally { + try { + if (reader != null) reader.close(); + } catch (IOException e) { + // ignore + } + } + } + } + } + + /** + * Add operation to list, emitting warning and returning false if not created. + * Add multiple operations at once by separating with ';' + * (so any ; in search or replace must be escaped). + * @param operation a String acceptable to + * <code>ReplaceOperation.makeReplaceOperation(String, ErrHandler)</code>, + * of the form sX{search}X{replace}X{g};.. + * @return false if not all added. + */ + public boolean addOperation(String operation) { + StringTokenizer st = new StringTokenizer(operation, ";", false); + String last = null; + ReplaceOperation toAdd; + boolean allAdded = true; + while (st.hasMoreTokens()) { + // grab tokens, accumulating if \ escapes ; delimiter + String next = st.nextToken(); + if (next.endsWith("\\") && (st.hasMoreTokens())) { + next = next.substring(0, next.length()-1); + last = (last == null ? next : last + next); + continue; + } + if (null != last) { + next = last + next; + last = null; + } + toAdd = ReplaceOperation.makeReplaceOperation(next, handler); + if (null != toAdd) { + list.add(toAdd); + } else { + String label = "RegexpFilter.OperationList.addOperation(\"" + operation + "\"): "; + handler.handle(label + " input not accepted " , ErrHandler.WARN); + if (allAdded) allAdded = false; + } + } + return allAdded; + } + + /** + * @return an Iterator over the list of ReplaceOperation + */ + public Iterator operations() { + return list.iterator(); + } + } // class OperationList +} // class RegexpFilter + +/** + * Encapsulate a search/replace operation which uses a RegExp. + */ +class ReplaceOperation { + /** + * This accepts a sed-like substitute command, except that + * the delimiter character may not be used anywhere in the + * search or replace strings, even if escaped. You may use + * any delimiter character. + * Note that although g (replace-globally) is supported as input, + * it is ignored in this implementation. + * @param operation a String of the form sX{search}X{replace}X{g} + */ + public static ReplaceOperation makeReplaceOperation(String operation, ErrHandler handler) { + ReplaceOperation result = null; + StringBuffer err = (null == handler ? null : new StringBuffer()); + final String syntax = "sX{search}X{replace}X{g}"; + // todo: use Point p = isValidOperation(operation); + if (null == operation) { + if (null != err) err.append("null operation"); + } else if (5 > operation.length()) { + if (null != err) err.append("empty operation"); + } else if (!operation.startsWith("s")) { + if (null != err) err.append("expecting s: " + syntax); + } else { + String sep = operation.substring(1,2); + int mid = operation.indexOf(sep, 2); + if (-1 == mid) { + if (null != handler) err.append("expecting middle \"" + sep + "\": " + syntax); + } else if (mid == 2) { + if (null != handler) err.append("expecting search before middle \"" + sep + "\": " + syntax); + } else { + int end = operation.indexOf(sep, mid+1); + if (-1 == end) { + if (null != handler) err.append("expecting final \"" + sep + "\": " + syntax); + } else { + String search = operation.substring(2,mid); + if (!ReplaceOperation.isValidSearch(search)) { + if (null != handler) err.append("invalid search \"" + search + "\": " + syntax); + } else { + String replace = operation.substring(mid+1,end); + if (!ReplaceOperation.isValidReplace(replace)) { + if (null != handler) err.append("invalid replace \"" + replace + "\": " + syntax); + } else { + result = new ReplaceOperation(search, replace, operation.endsWith("g"), handler); + } + } + } + } + } + if ((0 < err.length()) && (null != handler)) { + err.append(" operation=\"" + operation + "\""); + handler.handle(err.toString(), ErrHandler.HALT); + } + return result; + } + + /** + * Return true if the input string represents a valid search operation + * @param replace the String representing a search expression + */ + protected static boolean isValidSearch(String search) { // todo: too weak to be useful now + return ((null != search) && (0 < search.length())); + } + + /** + * Return Point x=mid, y=end if the input string represents a valid search operation + * @param search the String representing a search expression + protected static Point isValidOperation(String search) { + if (null != search) { + final int length = search.length(); + if (5 < length) { + String sep = search.substring(2,3); + int mid = search.indexOf(sep, 3); + if (3 < mid) { + int end = search.indexOf(sep, mid+1); + if ((end == length-1) + || ((end == length-2) + && search.endsWith("g"))) { + return new Point(mid, end); + } + } + } + } + return null; + } + */ + + /** + * Return true if the input string represents a valid replace operation + * @param replace the String representing a replace expression + */ + protected static boolean isValidReplace(String replace) { // todo: too weak to be useful now + boolean result = (null != replace); + return result; + } // isValidReplace + + // ------------------------------------------------- instance members + /** If true, repeat replace as often as possible (todo: repeat not supported) */ + protected final boolean repeat; + /** search pattern */ + protected final String search; + /** replace pattern */ + protected final String replace; + /** regexp processor */ + protected final Regexp regexp; + /** replace buffer (read-only) */ + protected final char[] replaceBuffer; + /** error handler */ + protected final ErrHandler handler; + + // ------------------------------------------------- constructors + private ReplaceOperation(String search, String replace, boolean repeat, ErrHandler handler) { + this.search = search; + this.replace = replace; + this.replaceBuffer = replace.toCharArray(); + this.repeat = repeat; + this.handler = (null != handler ? handler : ErrHandler.DEFAULT); + this.regexp = RegexpFactory.makeRegexp(); + try { + this.regexp.setPattern(search); + } catch (Exception e) { + this.handler.handle("setting search=" + search, e); + } + } + + + /** + * Return true if the input would be matched by the search string of this ReplaceOperation. + * @param input the String to compare + * @return true if the input would be matched by the search string of this ReplaceOperation + */ + public boolean matches(String input) { + return ((null != input) && regexp.matches(input)); + } // matches + + /** + * Replace any search text in input with replacement text, + * returning input if there is no match. More specifically, + * <li> emit unmatched prefix, if any</li> + * <li> emit replacement text as-is, except that + * \[0-9] in the replacement text is replaced + * with the matching subsection of the input text</li> + * <li> emit unmatched suffix, if any</li> + * @param input the String to search and replace + * @throws IllegalArgumentException if null == input + */ + public String replace(String input) { + if (null == input) throw new IllegalArgumentException("null input"); + String label = "replace(\"" + input + "\") "; + D.log(label); + if (matches(input)) { + StringBuffer buffer = new StringBuffer(); + final int length = replaceBuffer.length; + Vector groups = regexp.getGroups(input); + if ((null == groups) || (1 > groups.size())) { + handler.handle(label + "matched but no groups? "); + return input; + } + buffer.setLength(0); + // group 0 is whole; if not same as input, print prefix/suffix + String matchedPart = (String) groups.elementAt(0); + final int matchStart = input.indexOf(matchedPart); + final int matchEnd = matchStart + matchedPart.length(); + if (0 < matchStart) { + buffer.append(input.substring(0, matchStart)); + } + // true if \ escaping special char, esp. replace \[0-9] + boolean specialChar = false; + for (int i = 0; i < length; i++) { + char c = replaceBuffer[i]; + if (specialChar) { + int value = Character.digit(c, 10); // only 0-9 supported + if ((0 <= value) && (value < groups.size())) { + buffer.append((String) groups.elementAt(value)); + } else { + buffer.append(c); + } + specialChar = false; + } else if ('\\' != c) { + D.log("." + c); + buffer.append(c); + } else { + specialChar = true; + } + } + if (specialChar) { + handler.handle(label + "\\ without register: " + replace, + ErrHandler.ABORT); + } + if (matchEnd < input.length()) { + buffer.append(input.substring(matchEnd)); + } + input = buffer.toString(); + } + return input; + } // replace +} // class ReplaceOperation + diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/RegexpFilterReader.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/RegexpFilterReader.java new file mode 100644 index 000000000..5021a5efe --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/RegexpFilterReader.java @@ -0,0 +1,291 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.testing.compare; + + +import org.aspectj.testing.util.StringVisitor; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.Reader; +import java.util.Vector; + +/** + * Adapt Writer to StringVisitor, with pre- and post-write + * methods for subclasses to override. + */ +class StringDumper implements StringVisitor { + /** subclasses should write errors here and be silent if null */ + protected PrintStream errSink; + private final boolean appendNewlines; + private final BufferedWriter writer; + /** true only if writer non-null and close() was invoked */ + private boolean closed; + + /** + * @param writer the Writer to write to - ignored if null + * @param appendNewLines if true, append newlines after writing + */ + public StringDumper(BufferedWriter writer, boolean appendNewlines) { + this.appendNewlines = appendNewlines; + this.writer = writer; + // closed = false; + errSink = System.err; + } + + /** + * Set error sink - may be null to ignore IOExceptions. + * @param err the PrintStream to use for errors (silent if null) + */ + public void setErrorSink(PrintStream err) { + errSink = err; + } + + /** + * Invoked before the String is written. + * This implementation does nothing. + * @param string the String to be written + * @return false if writing should abort + */ + protected boolean preWrite(String string) + throws IOException { + return true; + } + + /** + * Implement StringVisitor.accept(String) + * by doing preWrite(String), process(String), + * writer.write(String...), and postWrite(String), + * any one of which may result in a false return value. + * @throws Error if invoked after <code>close()</code> + */ + public final boolean accept(String string) { + if (closed) { + String m = "did close() before accept(\"" + string + "\")"; + throw new Error(m); + } + if (null == writer) return false; + try { + if (!preWrite(string)) return false; + string = process(string); + if (null == string) return false; + if (null != writer) writer.write(string, 0, string.length()); + if (!postWrite(string)) return false; + } catch (IOException e) { + PrintStream sink = errSink; + if (null != sink) e.printStackTrace(sink); + return false; + } + return true; + } + + /** + * Transform the input before writing. + * This implementation returns the input. + * @param string the String to transform + * @return the String as changed - if null, + * then halt and return false from the accept method. + */ + protected String process(String string) { + return string; + } + + /** + * Invoked after the String is written. + * This implementation handles writing of the newline. + * @param string the String that was written + * @return false if writing should abort + */ + protected boolean postWrite(String string) + throws IOException { + if (appendNewlines && null != writer) { + writer.newLine(); + } + return true; + } + + /** convenience method to close adopted writer */ + public void close() throws IOException { + if (null != writer) { + writer.close(); + closed = true; + } + } +} +class FilteredDumper extends StringDumper { + protected final RegexpFilter filter; + public FilteredDumper(BufferedWriter writer, + boolean appendNewlines, + RegexpFilter filter) { + super(writer, appendNewlines); + this.filter = filter; + } + public String process(String arg) { + return filter.process(arg); + } +} + +class FilteredAccumulator extends FilteredDumper { + protected final Vector results; + public FilteredAccumulator(RegexpFilter filter) { + super(null, false, filter); + results = new Vector(); + } + public String process(String arg) { + arg = super.process(arg); + synchronized (results) { + results.add(arg); + } + return arg; + } + public Vector getResults() { + synchronized (results) { + return (Vector) results.clone(); + } + } +} + +/** + +/** + * Input file, using a RegexpFilter to preprocess each line. + * <li>read line using superclass LineNumberReader.readLine()</li> + * <li>Preprocess with case and white space operations</li> + * <li>run all the replace operations on the input, in order</li> + * <li>return the line.</li> + * Using anything but the <code>readLine()</code> method will circumvent + * the regular expression replacement processing. + */ +public class RegexpFilterReader extends BufferedReader { + + // ---------------------------------------------- static methods + + /** + * Pass lines from BufferedReader to visitor. + * Stop reading lines if visitor returns false. + * @param input the BufferedReader with the input + * - if null, use System.in + * @param visitor the StringVisitor to pass each line + * if null, just read in all lines and ignore + */ + public static void visitLines(BufferedReader input, + StringVisitor visitor) + throws IOException { + final boolean openInput = (null == input); + if (openInput) input = new BufferedReader(new InputStreamReader(System.in)); + try { + String line = null; + if (null == visitor) { + while (null != (line = input.readLine())) { + // read and ignore + } + } else { + while (null != (line = input.readLine())) { + if (!visitor.accept(line)) { + break; + } + } + } + } finally { + if (openInput && (null != input)) { + try { input.close(); } // todo: ok to close since System.in underlies? + catch (IOException e) { + e.printStackTrace(System.err); + } + } + } + } // visitLines + + // todo move write(in,out) to a utility class + /** + * Convenience method to write one file to another by line. + * Neither input nor output are closed. + * @param input the BufferedReader with the input - if null, use System.in + * @param output the BufferedWriter for the output - if null, use System.out + */ + public static void write(BufferedReader input, + BufferedWriter output) + throws IOException { + final boolean openOutput = (null == output); + if (openOutput) output = new BufferedWriter(new OutputStreamWriter(System.out)); + StringDumper visitor = new StringDumper(output, true); + try { + RegexpFilterReader.visitLines(input, visitor); + } finally { + if (openOutput && (null != visitor)) { + try { visitor.close(); } // todo: ok to close since System.out underlies? + catch (IOException e) { + e.printStackTrace(System.err); + } + } + } + } // write + + /** + * Process file (or System.in) like sed. + * @param args the String[] containing RegexpFilter arguments + */ + public static void main(String[] args) throws IOException { + RegexpFilter filter = RegexpFilter.init(args, null); + if (null != filter) { + File file = filter.getFile(); + Reader reader = null; + if (file != null) { + reader = new FileReader(file); + } else { + reader = new InputStreamReader(System.in); + } + RegexpFilterReader me = new RegexpFilterReader(reader); + me.setFilter(filter); + RegexpFilterReader.write(me, null); + } + } + + // ---------------------------------------------- constructors + public RegexpFilterReader(Reader reader) { + super(reader); + } + public RegexpFilterReader(Reader reader, int size) { + super(reader, size); + } + + // ---------------------------------------------- instance fields + protected RegexpFilter filter; + // ---------------------------------------------- instance methods + public void setFilter(RegexpFilter filter) { + this.filter = filter; + } + + /** + * Process each line as it is read in by the superclass. + */ + public String readLine() throws IOException { + RegexpFilter filter = this.filter; + String line = super.readLine(); + if (null != filter) { + line = filter.process(line); + } + return line; + } + +} // class RegexpFilterReader + + diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/TreeCompare.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/TreeCompare.java new file mode 100644 index 000000000..cbf92daf5 --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/TreeCompare.java @@ -0,0 +1,217 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.testing.compare; +import org.aspectj.testing.compare.adapters.StructureGenericTreeNodeFactory; +import org.aspectj.testing.compare.adapters.JTreeNodeGenericTreeNodeFactory; +import org.aspectj.testing.compare.adapters.GenericTreeNodeFactoryI; +// target +// XXX compiler import org.aspectj.asm.StructureNode; +// testing +import javax.swing.tree.TreeNode; +import javax.swing.tree.DefaultMutableTreeNode; +// utils +import java.io.*; + +/** + * Compare two generic trees for tree-equality. + * Currently does not indicate where or how they failed. + * Input trees are serialized to disk in a format that + * is (or can be wrapped using) <code>GenericTreeNode</code>. + * requires files expected.ser and actual.ser + * to deserialize to type StructureNode (untested - use Structure) + * or Swing TreeNode. + */ +public class TreeCompare { + public static boolean DODIFF; + /** + * @param args ignored - reading expected.ser and actual.ser + */ + public static void main(String[] args) { + TreeCompare me = new TreeCompare(); + File expected = new File("expected.ser"); + File actual = new File("actual.ser"); + if ((args.length > 0) + || (!expected.exists() || (!actual.exists()))) { + DODIFF = (args.length > 1); + takeSnapshot(expected); + takeSnapshot(actual); + } + me.compareSnapshots(expected, actual); + } + private static void takeSnapshot(File file) { + DefaultMutableTreeNode snapshot = getRoot(file) ; + ObjectOutputStream p = null; + FileOutputStream ostream = null; + try { + ostream = new FileOutputStream(file); + p = new ObjectOutputStream(ostream); + p.writeObject(snapshot); + } catch (IOException e) { + e.printStackTrace(System.err); + } finally { + try { + if (null != p) p.flush(); + if (null != ostream) ostream.close(); + } catch (IOException o) {} // ignored + } + } + + private static DefaultMutableTreeNode getRoot(File file) { + boolean isActual = (!DODIFF ? false : (-1 != (file.getPath().indexOf("actual")))); + DefaultMutableTreeNode root = new DefaultMutableTreeNode("Edna"); + DefaultMutableTreeNode child = new DefaultMutableTreeNode("Evalyn"); + root.add(child); + child.add(new DefaultMutableTreeNode("Marsha")); + child.add(new DefaultMutableTreeNode("Ray")); + if (DODIFF && isActual) { // Bill added to actual + child.add(new DefaultMutableTreeNode("Bill")); + } + child = new DefaultMutableTreeNode("Clifford"); + root.add(child); + child.add(new DefaultMutableTreeNode("Terry")); + if (DODIFF && isActual) { // Peter mispelled in actual + child.add(new DefaultMutableTreeNode("peter")); + } else { + child.add(new DefaultMutableTreeNode("Peter")); + } + child.add(new DefaultMutableTreeNode("Mary")); + child = new DefaultMutableTreeNode("Anastasia"); + root.add(child); + child.add(new DefaultMutableTreeNode("Victor")); + child.add(new DefaultMutableTreeNode("Valerie")); + child.add(new DefaultMutableTreeNode("Valentine")); + if (DODIFF && isActual) { // Vim added in actual, with a child + DefaultMutableTreeNode par = new DefaultMutableTreeNode("VimAdded"); + par.add(new DefaultMutableTreeNode("Vim kid")); + child.add(par); + } + return root; + } + + /** + * Compare two File by reading in as serialized + * and selecting the appropriate wrappers for the resulting + * Object. + */ + public void compareSnapshots(File expected, File actual) { + try { + // construct the respective trees + FileInputStream efStream = new FileInputStream(expected); + ObjectInputStream eStream = new ObjectInputStream(efStream); + FileInputStream afStream = new FileInputStream(actual); + ObjectInputStream aStream = new ObjectInputStream(afStream); + Object expectedObject = eStream.readObject(); + Object actualObject = aStream.readObject(); + Class expectedObjectClass = (null == expectedObject ? null : expectedObject.getClass()); + Class actualObjectClass = (null == actualObject ? null : actualObject.getClass()); + // todo yuck: switch by type using known factories +// XXX compiler +// if (StructureNode.class.isAssignableFrom(expectedObjectClass)) { +// if (StructureNode.class.isAssignableFrom(actualObjectClass)) { +// compareSnapshots((StructureNode) expectedObject,(StructureNode) actualObject); +// System.err.println("ok"); +// } else { +// signalDifferentTypes(expectedObject, actualObject); +// } +// } else if (DefaultMutableTreeNode.class.isAssignableFrom(expectedObjectClass)) { +// if (DefaultMutableTreeNode.class.isAssignableFrom(actualObjectClass)) { +// compareSnapshots((DefaultMutableTreeNode) expectedObject, +// (DefaultMutableTreeNode) actualObject); +// } else { +// signalDifferentTypes(expectedObject, actualObject); +// } +// } else { + System.err.println("Unrecognized objects - expected: " + + expectedObject + " actual: " + actualObject); +// } + } catch (Throwable t) { + System.err.println("TEST FAILED: " + t.getMessage()); + t.printStackTrace(System.err); + return; + } + } // compareSnapshots(File, File) + + public void signalDifferentTypes(Object lhs, Object rhs) { + Class lhc = lhs.getClass(); + Class rhc = rhs.getClass(); + String err = "Different Types? lhs: " + lhc + "=" + lhs + + " rhs: " + rhc + "=" + rhs; + throw new Error(err); + } + + /** + * Compare two StructureNode by wrapping in GenericTreeNode + */ +// XXX compiler +// public void compareSnapshots(StructureNode expected, StructureNode actual) { +// try { +// GenericTreeNodeFactoryI factory = +// StructureGenericTreeNodeFactory.SINGLETON; +// // this is the custom part: adapter generating generic model +// GenericTreeNode expectRoot +// = factory.createGenericTreeNode(expected, null); +// GenericTreeNode actualRoot +// = factory.createGenericTreeNode(actual, null); +// if (null == actualRoot) System.err.println("null actualRoot"); +// if (null == expectRoot) System.err.println("null expectRoot"); +// compareSnapshots(expectRoot, actualRoot); +// } catch (Throwable t) { +// System.err.println("TEST FAILED: " + t.getMessage()); +// t.printStackTrace(System.err); +// return; +// } +// } // compareSnapshots(TreeModel, TreeModel) + + /** + * Compare two Swing TreeModel by wrapping in GenericTreeNode + */ + public void compareSnapshots(TreeNode expected, TreeNode actual) { + try { + GenericTreeNodeFactoryI factory = + JTreeNodeGenericTreeNodeFactory.SINGLETON; + // this is the custom part: adapter generating generic model + GenericTreeNode expectRoot + = factory.createGenericTreeNode(expected, null); + GenericTreeNode actualRoot + = factory.createGenericTreeNode(actual, null); + if (null == actualRoot) System.err.println("null actualRoot"); + if (null == expectRoot) System.err.println("null expectRoot"); + compareSnapshots(expectRoot, actualRoot); + } catch (Throwable t) { + System.err.println("TEST FAILED: " + t.getMessage()); + t.printStackTrace(System.err); + return; + } + } // compareSnapshots(TreeModel, TreeModel) + + /** Compare GenericTreeNode trees exactly, printing errors */ + public void compareSnapshots(GenericTreeNode expected, GenericTreeNode actual) { + try { + //GenericTreeNodesVisitorI visitor = GenericTreeNode.PRINTALL; + GenericTreeNodesVisitorI visitor = GenericTreeNode.PRINTERR; + //GenericTreeNodesVisitorI visitor = GenericTreeNode.EXACT; + + if (GenericTreeNode.traverse(expected, actual, null, visitor)) { + System.err.println("TEST PASSED"); + } else { + System.err.println("TEST FAILED"); + } + } catch (Throwable t) { + System.err.println("TEST FAILED: " + t.getMessage()); + t.printStackTrace(System.err); + return; + } + } // compareSnapshots(GenericTreeNode, GenericTreeNode) +} // TreeCompare + diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/GenericTreeNodeFactoryI.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/GenericTreeNodeFactoryI.java new file mode 100644 index 000000000..adee3d3ee --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/GenericTreeNodeFactoryI.java @@ -0,0 +1,33 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.testing.compare.adapters; + +import org.aspectj.testing.compare.GenericTreeNode; +/** + * Encapsulate the implementation of an factory to create + * a GenericTreeNode tree from a given input type, to permit + * a general, pluggable factory to operate based on source type. + */ +public interface GenericTreeNodeFactoryI { + /** @return the expected Class of the root node supported by this factory */ + public Class getRootClass(); + + /** + * Create a wrapped generic tree with the input root tree as delegates. + * @param root the {rootClass} root of the tree to convert - never null + * @throws IllegalArgumentException if root is null or not assignable to rootClass + */ + public GenericTreeNode createGenericTreeNode(Object root, GenericTreeNode parent); +} + diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/JTreeNodeGenericTreeNodeFactory.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/JTreeNodeGenericTreeNodeFactory.java new file mode 100644 index 000000000..5f48eef40 --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/JTreeNodeGenericTreeNodeFactory.java @@ -0,0 +1,102 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.testing.compare.adapters; + +import org.aspectj.testing.compare.GenericTreeNode; +import org.aspectj.testing.compare.*; + +import javax.swing.tree.*; // sample uses TreeModel... +import java.util.*; + +/** + * Factory for adapting Swing TreeNode to GenericTreeNode + */ +public class JTreeNodeGenericTreeNodeFactory implements GenericTreeNodeFactoryI { + public static final GenericTreeNodeFactoryI SINGLETON + = new JTreeNodeGenericTreeNodeFactory(); + private JTreeNodeGenericTreeNodeFactory() {} + public Class getRootClass() { + return TreeNode.class; + } + + /** + * Adapt swing TreeModel to tree rooted at GenericTreeNode + * Only takes the current state of a TreeModel which does not + * change during the construction of the adapter. If the + * TreeModel changes, you can ask the adapter for a newly + * wrapped tree. + * Recursively convert entire tree from root to a wrapped tree + * Note this takes a snapshot of the tree such that changes to + * the TreeModel after the constructor returns are ignored. + * Changes during the constructor produce undetermined results. + * @param root the TreeNode taken as root of a tree to wrap + * @param parent the parent of the resulting GenericTreeNode + * @throws IllegalArgumentException if root is null + * or if children are not instanceof TreeNode. + */ + public GenericTreeNode createGenericTreeNode(Object root, GenericTreeNode parent) { + if (null == root) { + throw new IllegalArgumentException("null root"); + } + if (! (root instanceof TreeNode)) { + throw new IllegalArgumentException("not TreeNode: " + root); + } + TreeNode rootNode = (TreeNode) root; + final int numKids = rootNode.getChildCount(); + ArrayList kids = new ArrayList(numKids); + Enumeration children = rootNode.children(); + Object child; + GenericTreeNode result = new GenericTreeNode(); + for (int i = 0; i < numKids; i++) { + if (! children.hasMoreElements()) { + throw new Error("(! children.hasNext())"); + } + child = children.nextElement(); + if (! (child instanceof TreeNode)) { + throw new Error("! (child instanceof TreeNode)): " + child ); + } + kids.add(createGenericTreeNode((TreeNode) child, result)); + } + //result.init(parent, GenericTreeNode.COMPARATOR, rootNode, kids, null); + result.init(parent, JTreeNodeComparator.SINGLETON, rootNode, kids, null); + return result; + } + + /** Comparator for swing TreeNode todo convert from TreeNode to DefaultMutableTreeNode */ + static class JTreeNodeComparator implements Comparator { + public static Comparator SINGLETON = new JTreeNodeComparator(); + private JTreeNodeComparator () {} + public int compare(Object lhs, Object rhs) { + int result = CompareUtil.compare(lhs, rhs); + if (Integer.MAX_VALUE == result) { + Class lhClass = lhs.getClass() ; + Class rhClass = rhs.getClass() ; + if ((DefaultMutableTreeNode.class.isAssignableFrom(lhClass)) + && (DefaultMutableTreeNode.class.isAssignableFrom(rhClass))) { + DefaultMutableTreeNode lh = (DefaultMutableTreeNode) lhs ; + DefaultMutableTreeNode rh = (DefaultMutableTreeNode) rhs ; + Object lhObject = lh.getUserObject(); + Object rhObject = rh.getUserObject(); + result = CompareUtil.compare(lhs, rhs); + if (Integer.MAX_VALUE == result) { + result = lhObject.toString().compareTo(rhObject.toString()); + } + } else { // urk - broken unless wrapper + result = lhs.toString().compareTo(rhs.toString()); + } + } + return result; + } + } +} diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/Structure.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/Structure.java new file mode 100644 index 000000000..b669f7c6b --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/Structure.java @@ -0,0 +1,438 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.testing.compare.adapters; + +import org.aspectj.testing.compare.GenericTreeNode; +import org.aspectj.testing.compare.GenericTreeNodesVisitorI; +import org.aspectj.testing.compare.GenericTreeNodeListOrdererFactoryI; +import org.aspectj.testing.compare.GenericTreeNodeListOrdererI; +import org.aspectj.testing.compare.adapters.StructureGenericTreeNodeFactory; + +// XXX compiler import org.aspectj.asm.StructureNode; +// XXX wes move to ajde tests import org.aspectj.tools.ajde.StructureUtilities; + +import java.util.*; +import java.io.*; + +/** + * Compare or print (serialized) structure tree/graph(s). + * Mostly thread-safe, except that (todo for now) PRINT_SINK is a mutable static + * variable settable by the arguments. + * See {@link #main(String[]) main} for usage. + */ +public class Structure { + // ---------------------------------- static fields + // PRINT_SINK as static permits static inner class singleton visitors to reflect output sink policy + /** WARNING: print sink changeable at runtime (affects all instances of class!) */ + public static PrintStream PRINT_SINK = System.err; + + /** singleton factory to sort by string */ + protected static final GenericTreeNodeListOrdererFactoryI STRING_SORTER_FACTORY + = new StringSortFactory(); + /** singleton factory to sort by string */ + protected static final GenericTreeNodeListOrdererI STRING_SORTER + = new StringSort(); + /** singleton to sort by string */ + protected static final Comparator STRING_COMP = new StringComparator(); + /** default visitor printer expects only one of pair and render each parent as tab */ + protected static final GenericTreeNodesVisitorI PRINT_EACH = new PrintEach(); + /** visitor printer prints long version of both input (todo: not redirected by toOut) */ + protected static final GenericTreeNodesVisitorI PRINT_ALL = GenericTreeNode.PRINTALL; + /** visitor printer prints long version of both input if non-matching (todo: not redirected by toOut) */ + protected static final GenericTreeNodesVisitorI PRINT_ERR = GenericTreeNode.PRINTERR; + /** parms: default list of files used if no files given */ + public static final String DEFAULT_LST = "default.lst"; + /** parms: default argument list if none given */ + public static final String[] DEFAULT_ARGS = new String[] + { "-d" , "classes" , "-workingdir", "ajworkingdir", "-argfile", DEFAULT_LST}; + + // ---------------------------------- static methods + /** + * Print and/or compare Structure trees. + * One (actual) can be compiled at the same time; + * either (expected or actual) may be read in from a serialized tree. + * Supported arguments: + * <table> + * <tr><td>{ajc compiler args}</td> + * <td>The set of compiler arguments, to compile a new tree</td></tr> + * <tr><td>-expect {file}.ser</td> + * <td>The old Structure tree to read in and compare with new</td></tr> + * <tr><td>-actual {file}.ser</td> + * <td>The new Structure tree to read in (i.e., no compile)</td></tr> + * <tr><td>-save {file}.ser</td> + * <td>Serialize the results of the compile to {file}.ser.</td></tr> + * <tr><td>-printall</td> + * <td>Print all pairs in long format</td></tr> + * <tr><td>-printeach</td> + * <td>Print each item in short format + * - used when only one tree is available</td></tr> + * <tr><td>-printerr</td> + * <td>Print pairs of items that do not match + * - used when only both trees are available</td></tr> + * <tr><td>-sortString</td> + * <td>before comparing, do a string-sort on each child list.</td></tr> + * <tr><td>-toOut</td> + * <td>Redirect output from System.err to System.out (for all instances of this class)</td></tr> + * <tr><td>-notest</td> + * <td>Do not run test (e.g., just compile and save)</td></tr> + * </table> + * @param args the String[] of arguments for this test - defaults supplied if empty. + */ + public static void main(String[] args) { + new Structure().runTest(args); + } + + // ---------------------------------- static util + protected static final void log(String s) { + final PrintStream sink = PRINT_SINK; + if ((null != s) && (null != sink)) { + sink.println("Structure: " + s); + } + } + + protected static void signal(String message) { + log(message); + } + protected static void signal(String context, String event) { + log(context + ": " + event); + } + protected static void signal(String context, Throwable event) { + if (null == event) { + log(context); + } else { + String msg = event.getMessage(); + log(context + ": " + msg); + event.printStackTrace(PRINT_SINK); + } + } + + // ---------------------------------- instance fields + /** args result: path to serialize actual tree to */ + protected String actualSavePath = null; + /** args result: path of actual serialized tree */ + protected String actualPath = null; + /** args result: path of expected serialized tree */ + protected String expectedPath = null; + /** args result: false if not running comparisons */ + protected boolean doComparison = true; + + /** visitor to run - print or test (default is PRINT_ALL) */ + protected GenericTreeNodesVisitorI visitor = PRINT_ALL; + /** this determines for each set of children whether/how to sort them */ + protected GenericTreeNodeListOrdererFactoryI sorter = null; + + // ---------------------------------- instance methods + /** no-arg (default) constructor */ + public Structure() { } + + /** + * Clear settings before running. + * Use this unless you want to inherit settings from + * a prior run. You can specify new arguments that + * overwrite the old settings. + * @param args the String[] used for runTest(String[]) + * @see #clear() + * @see #runTest(String[]) + */ + public synchronized void clearAndRunTest(String[] args) { + actualSavePath = null; + actualPath = null; + expectedPath = null; + doComparison = true; + visitor = PRINT_ALL; + runTest(args); + } + + /** + * Read and/or write and/or compare structure trees. + * Any results are delivered by the comparison visitors. + * The test process is as follows: + * <li>processArgs(..)</li> + * <li>saveActual(..) if saving actual to file</li> + * <li>doComparison(..) of actual and expected per visitor/sorter options</li> + * <p>If you run this consecutively, you'll inherit the values + * of the last run that you do not overwrite + * unless you invoke {@link #clear()}. The method synchronization + * will not prevent another thread from interrupting your + * @param args the String[] defined by <code>main(String[] args)</code> + */ + public synchronized void runTest(String[] args) { + if (null == args) throw new IllegalArgumentException("null args"); + args = processArgs(args); + if (null == args) throw new IllegalArgumentException("bad args"); +// XXX compiler +//// StructureNode lhsRoot = loadStructureNode(expectedPath); +//// StructureNode rhsRoot = loadStructureNode(actualPath); +//// if (null == rhsRoot) { // not serialized - try compile +//// // XXX wes move to ajde tests rhsRoot = StructureUtilities.buildStructureModel(args); +//// } +//// // save actual, if requested +//// saveActual(rhsRoot); +//// // do comparison (i.e., run test) +//// doComparison(lhsRoot, rhsRoot); + } + + /** + * Process arguments by extracting our arguments from callee arguments + * and initializing the class accordingly. + * {@link See main(String[])} for a list of valid arguments. + * @param args the String[] adopted, elements shifted down to remove ours + * @return a String[] containing args not relevant to us (i.e., for callee = compiler) + */ + protected String[] processArgs(String[] args) { + if ((null == args) || (1 > args.length)) { + return processArgs(DEFAULT_ARGS); + } + int numFiles = 0; + int numArgFiles = 0; + final String SKIP = "skip"; + String calleeArg; + int readIndex = 0; + int writeIndex = 0; + while (readIndex < args.length) { + final String arg = args[readIndex]; + calleeArg = arg; + // assume valid arg for callee unless shown to be ours + if ((null == arg) || (0 == arg.length())) { + signal("processArgs", "empty arg at index "+ readIndex); + break; + } else if (arg.startsWith("@") || "-argfile".equals(arg)) { + numArgFiles++; + } else if (arg.endsWith(".java")) { + numFiles++; + } else if (arg.startsWith("-")) { + calleeArg = SKIP; // assume args are ours unless found otherwise + if ("-toOut".equals(arg)) { + Structure.PRINT_SINK = System.out; + } else if ("-notest".equals(arg)) { + doComparison = false; + } else if ("-printall".equals(arg)) { + visitor = PRINT_ALL; + } else if ("-printeach".equals(arg)) { + visitor = PRINT_EACH; + } else if ("-printerr".equals(arg)) { + visitor = PRINT_ERR; + } else if ("-sortString".equals(arg)) { + sorter = STRING_SORTER_FACTORY; + } else { // the rest of ours require a parm + readIndex++; + String next = ((readIndex < args.length) + ? args[readIndex] : null); + boolean nextIsOption + = ((null != next) && next.startsWith("-")); + if ("-expect".equals(arg)) { + expectedPath = next; + } else if ("-actual".equals(arg)) { + actualPath = next; + } else if ("-save".equals(arg)) { + actualSavePath = next; + } else { + readIndex--; + calleeArg = arg; // ok, not ours - save + } + if ((calleeArg == SKIP) + && ((null == next) || (nextIsOption))) { + signal("processArgs", arg + " requires a parameter"); + break; + } + } + } + if (SKIP != calleeArg) { + args[writeIndex++] = calleeArg; + } + readIndex++; + } // end of reading args[] + if (readIndex < args.length) { // bad args[] - abort (see signals above) + return null; + } + // if no input specified, supply default list file + if ((0 == numFiles) && (0 == numArgFiles) && (null == actualPath)) { + if (writeIndex+3 > args.length) { + String[] result = new String[writeIndex+2]; + System.arraycopy(args, 0, result, 0, writeIndex); + args = result; + } + args[writeIndex++] = "-argfile"; + args[writeIndex++] = DEFAULT_LST; + } + // if some args clipped (ours), clip array to actual (callee) + if (writeIndex < args.length) { + String[] result = new String[writeIndex]; + System.arraycopy(args, 0, result, 0, writeIndex); + args = result; + } + return args; + } // processArgs(String[]) + +// XXX compiler +// /** +// * Load any StructureNode tree at path, if possible +// * @param path the String path to a serialized StructureNode +// */ +// protected StructureNode loadStructureNode(String path) { +// if (null == path) return null; +// StructureNode result = null; +// try { +// FileInputStream stream = new FileInputStream(path); +// ObjectInputStream ois = new ObjectInputStream(stream); +// Object o = ois.readObject(); +// Class oClass = (null == o ? null : o.getClass()); +// if (StructureNode.class.isAssignableFrom(oClass)) { +// result = (StructureNode) o; +// } else { +// signal("loadStructureNode(\"" + path +// + "\") - wrong type: " + oClass); +// } +// } catch (Throwable t) { +// signal("loadStructureNode(\"" + path + "\")", t); +// } +// return result; +// } +// +// /** +// * Save any StructureNode tree to actualSavePath, if possible +// * @param actual the StructureNode root of the actual tree to save +// * (ignored if null) +// */ +// protected void saveActual(StructureNode actual) { +// if ((null != actual) && (null != actualSavePath)) { +// ObjectOutputStream p = null; +// FileOutputStream ostream = null; +// try { +// ostream = new FileOutputStream(actualSavePath); +// p = new ObjectOutputStream(ostream); +// p.writeObject(actual); +// } catch (Throwable e) { +// signal("saveActual(\"" + actual + "\") -> " +// + actualSavePath, e); +// } finally { +// try { +// if (null != p) p.flush(); +// if (null != ostream) ostream.close(); +// } catch (IOException o) {} // ignored +// } +// } +// } + + /** + * Compare two trees based on the settings for + * the visitor and sorter. All results should be + * delivered by the visitor. + * @param expected the StructureNode actual tree to compare + * @param actual the StructureNode actual tree to compare + */ + // XXX compiler +// protected void doComparison(StructureNode expected, StructureNode actual) { +// if (doComparison) { +// final GenericTreeNodeFactoryI fact = +// StructureGenericTreeNodeFactory.SINGLETON; +// GenericTreeNode lhs = null; +// if (expected != null) { +// lhs = fact.createGenericTreeNode(expected, null); +// } +// GenericTreeNode rhs = null; +// if (actual != null) { +// rhs = fact.createGenericTreeNode(actual, null); +// } +// GenericTreeNode.traverse(lhs, rhs, sorter, visitor); +// } +// } + + /** + * A visitor which prints each to the sink (if any). + * If only one of the pair is not null, + * render it using GenericTreeNode.shortString() + */ + static class PrintEach implements GenericTreeNodesVisitorI { + private PrintEach() {} + public boolean visit(GenericTreeNode lhs,GenericTreeNode rhs) { + PrintStream sink = PRINT_SINK; + if (null != sink) { + if ((lhs != null) && (rhs != null)) { // both + sink.println("[lhs=" + lhs + "] [rhs=" + rhs + "]"); + } else { + GenericTreeNode gtn = (null == lhs ? rhs : lhs); + if (null != gtn) { // one + sink.println(gtn.shortString()); + } + } + } + return true; + } + } // class PrintEach + + static class StringSortFactory implements GenericTreeNodeListOrdererFactoryI { + /** + * Produce the correct orderer for the children of the given GenericTreeNodes + * This always produces the same StringSorter. + * @return GenericTreeNodeListOrdererI for children, or null if none to be used + */ + public GenericTreeNodeListOrdererI produce(GenericTreeNode lhs, GenericTreeNode rhs, + GenericTreeNodesVisitorI visitor) { + return STRING_SORTER; + } + } // class StringSortFactory + + /** + * sort input lists by Comparator <code>Structure.STRING_COMP</code>. + */ + static class StringSort implements GenericTreeNodeListOrdererI { + /** + * Order input lists (not copies) + * using the Comparator <code>Structure.STRING_COMP</code>. + * @param lhs the List representing the left-hand-side + * which contains only GenericTreeNode + * @param rhs the List representing the right-hand-side + * which contains only GenericTreeNode + * @return two lists List[] (0 => lhs, 1 => rhs) + */ + public List[] produceLists(List lhs, List rhs) { + if (null != lhs) Collections.sort(lhs, STRING_COMP); + if (null != rhs) Collections.sort(rhs, STRING_COMP); + List[] result = new List[2]; + result[0] = lhs; + result[1] = rhs; + return result; + } + } // class CompSort + + /** + * Comparator that imposes case-sensitive String order + * based on GenericTreeNode.shortString() if both are + * GenericTreeNode, or toString() otherwise. + * If both are null, considered equal. + * If one is null, the other is considered larger. + */ + static class StringComparator implements Comparator { + public int compare(Object lhs, Object rhs) { + if (null == lhs) { + return (null == rhs ? 0 : -1); + } else if (null == rhs) { + return 1; + } else if ((lhs instanceof GenericTreeNode) + && (rhs instanceof GenericTreeNode)) { + String lhsString = ((GenericTreeNode) lhs).shortString(); + String rhsString = ((GenericTreeNode) rhs).shortString(); + if (null == lhsString) { + return (null == rhsString ? 0 : rhsString.compareTo(lhsString)); + } else { + return lhsString.compareTo(rhsString); + } + } else { + return lhs.toString().compareTo(rhs.toString()); + } + } + } // class StringComparator +} // class Structure + diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/StructureGenericTreeNodeFactory.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/StructureGenericTreeNodeFactory.java new file mode 100644 index 000000000..1f5883d2c --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/StructureGenericTreeNodeFactory.java @@ -0,0 +1,308 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.testing.compare.adapters; +// +//import org.aspectj.asm.SourceLocation; +import org.aspectj.testing.compare.GenericTreeNode; +//import org.aspectj.testing.compare.*; +//import org.aspectj.asm.StructureNode; +//import org.aspectj.asm.LinkNode; +//import org.aspectj.asm.RelationNode; +//import org.aspectj.asm.Relation; +//import org.aspectj.asm.ProgramElementNode; +//import java.util.*; +// +///** +// * Factory for adapting StructureNode trees to GenericTreeNode +// */ +// XXX compiler +public class StructureGenericTreeNodeFactory implements GenericTreeNodeFactoryI { + public GenericTreeNode createGenericTreeNode(Object root, + GenericTreeNode parent) { + return null; + } + /** + * @see org.aspectj.testing.compare.adapters.GenericTreeNodeFactoryI#getRootClass() + */ + public Class getRootClass() { + return null; + } + +} +// protected static final String[] MODIFIERS = new String[] +// { "strictfp", "abstract", "synchronized", "native", +// "final", "transient", "static", "volatile" }; +// protected static final List MODIFIERS_LIST = Arrays.asList(MODIFIERS); +// protected static final String[] ACCESS = new String[] +// { "private", "protected", "package", "public", "privileged" }; +// protected static final List ACCESS_LIST = Arrays.asList(ACCESS); +// +// /** represent empty list of children (todo: use immutable instead) */ +// public static final List EMPTY_LIST = new ArrayList(); +// public static final GenericTreeNodeFactoryI SINGLETON +// = new StructureGenericTreeNodeFactory(); +// /** delegate of the factory */ +// private static final Comparator NODE_COMPARATOR; +// +// static { +// SubTypeComparator init = new SubTypeComparator(); +// init.addComparator(LinkNode.class, LinkNodeComparator.SINGLETON); +// init.addComparator(RelationNode.class, RelationNodeComparator.SINGLETON); +// init.addComparator(ProgramElementNode.class, ProgramElementNodeComparator.SINGLETON); +// init.addComparator(StructureNode.class, StructureNodeComparator.SINGLETON); +// GenericTreeNodeComparator gtnc = new GenericTreeNodeComparator(init); +// NODE_COMPARATOR = gtnc; +// } +// +// private StructureGenericTreeNodeFactory() {} +// public Class getRootClass() { +// return StructureNode.class; +// } +// +// /** +// * Adapt Structure model to tree rooted at GenericTreeNode. +// * Only takes the current state of a model which does not +// * change during the construction of the adapter. If the +// * model changes, you can ask the adapter for a newly +// * wrapped tree. +// * @param root the TreeNode taken as root of a tree to wrap +// * @param parent the parent of the resulting GenericTreeNode +// * @throws IllegalArgumentException if root is null +// * or if children are not instanceof TreeNode. +// */ +// public GenericTreeNode createGenericTreeNode(Object root, +// GenericTreeNode parent) { +// if (null == root) { +// throw new IllegalArgumentException("null root"); +// } +// if (! (root instanceof StructureNode)) { +// throw new IllegalArgumentException("not StructureNode: " + root); +// } +// GenericTreeNode result = new GenericTreeNode(); +// StructureNode rootNode = (StructureNode) root; +// List kidList = rootNode.getChildren(); +// List kids = EMPTY_LIST; +// // get kids of result +// if (null != kidList) { +// final int numKids = kidList.size(); +// ArrayList newKids = new ArrayList(numKids); +// ListIterator kidIter = kidList.listIterator(); +// Object child; +// for (int i = 0; i < numKids; i++) { +// if (! kidIter.hasNext()) { // items removed from list while iterating +// throw new Error("(! hasNext())"); +// } +// child = kidIter.next(); +// if (! (child instanceof StructureNode)) { +// throw new Error("! (child instanceof StructureNode)): " + child ); +// } +// newKids.add(createGenericTreeNode((StructureNode) child, result)); +// } +// kids = newKids; +// } +// // todo: select comparator here - avoids type checking at run time +// //result.init(parent, StructureComparator.SINGLETON, rootNode, kids, null); +// result.init(parent, NODE_COMPARATOR, rootNode, kids, null); +// return result; +// } +// +// /** Comparator for GenericTreeNode delegates to handler for nodes... */ +// static final class GenericTreeNodeComparator implements Comparator { +// private final Comparator delegate; +// private GenericTreeNodeComparator (Comparator delegate) { +// this.delegate = delegate; +// } +// public final int compare(Object lhs, Object rhs) { +// return delegate.compare(((GenericTreeNode)lhs).getNode() +// , ((GenericTreeNode)lhs).getNode()); +// } +// } +// +// /** +// * Comparator for RelationNode delegates to String & boolean comparison of public attributes. +// */ +// static class RelationNodeComparator implements Comparator { +// public static Comparator SINGLETON = new RelationNodeComparator(); +// private RelationNodeComparator () {} +// /** +// * Comparator for RelationNode uses String & boolean comparison of public attributes +// * forwardNavigationName, backNavigationName, associationName, symmetrical, associative. +// */ +// public int compare(Object lhs, Object rhs) { +// int result = CompareUtil.compare(lhs, rhs); +// if (Integer.MAX_VALUE == result) { +// RelationNode lh = (RelationNode) lhs ; +// RelationNode rh = (RelationNode) rhs ; +// Relation leftRelation = lh.getRelation(); +// Relation rightRelation = rh.getRelation(); +// String left = null; +// String right = null; +// result = CompareUtil.compare(leftRelation, rightRelation); +// if (0 == result) { +// left = leftRelation.getForwardNavigationName(); +// right = rightRelation.getForwardNavigationName(); +// result = CompareUtil.compare(left, right); +// } +// if (0 == result) { +// left = leftRelation.getBackNavigationName(); +// right = rightRelation.getBackNavigationName(); +// result = CompareUtil.compare(left, right); +// } +// if (0 == result) { +// left = leftRelation.getAssociationName(); +// right = rightRelation.getAssociationName(); +// result = CompareUtil.compare(left, right); +// } +// boolean l = false; +// boolean r = false; +// if (0 == result) { +// l = leftRelation.isSymmetrical(); +// r = rightRelation.isSymmetrical(); +// result = CompareUtil.compare(l, r); +// } +// if (0 == result) { +// l = leftRelation.isTransitive(); +// r = rightRelation.isTransitive(); +// result = CompareUtil.compare(l, r); +// } +// } +// return result; +// } +// } +// +// /** Comparator for ProgramElementNode. */ +// static class ProgramElementNodeComparator implements Comparator { +// public static Comparator SINGLETON = new ProgramElementNodeComparator(); +// private ProgramElementNodeComparator () {} +// public int compare(Object lhs, Object rhs) { +// int result = CompareUtil.compare(lhs, rhs); +// if (Integer.MAX_VALUE == result) { +// ProgramElementNode lh = (ProgramElementNode) lhs ; +// ProgramElementNode rh = (ProgramElementNode) rhs ; +// +// boolean rhStmntKind = rh.isCode(); +// boolean lhStmntKind = lh.isCode(); +// if (lhStmntKind != rhStmntKind) { +// return (lhStmntKind ? 1 : -1); +// } +// String left= lh.getKind(); +// String right= rh.getKind(); +// // boilerplate +// result = CompareUtil.compare(left, right); +// if (Integer.MAX_VALUE == result) { +// result = left.compareTo(right); +// if (0 != result) return result; +// } +// right = rh.getSignature(); +// left = lh.getSignature(); +// result = CompareUtil.compare(left, right); +// if (Integer.MAX_VALUE == result) { +// result = left.compareTo(right); +// if (0 != result) return result; +// } +// List rightList = rh.getModifiers(); +// List leftList = lh.getModifiers(); +// result = CompareUtil.compare(leftList, rightList, MODIFIERS_LIST); +// if (0 != result) return result; +// +// result = compare(rh.getAccessibility(), lh.getAccessibility()); +// if (0 != result) return result; +// +// right = rh.getDeclaringType(); +// left = lh.getDeclaringType(); +// result = CompareUtil.compare(left, right); +// if (Integer.MAX_VALUE == result) { +// result = left.compareTo(right); +// if (0 != result) return result; +// } +// +// SourceLocation leftSourceLocation = rh.getSourceLocation(); +// SourceLocation rightSourceLocation = rh.getSourceLocation(); +// int iright= rightSourceLocation.getLineNumber(); +// int ileft= leftSourceLocation.getLineNumber(); +// if (iright != ileft) return (ileft-iright); +// iright= rightSourceLocation.getColumnNumber(); +// ileft= leftSourceLocation.getColumnNumber(); +// if (iright != ileft) return (ileft-iright); +// +// right= rh.getFormalComment(); +// left= lh.getFormalComment(); +// if (Integer.MAX_VALUE == result) { +// result = left.compareTo(right); +// if (0 != result) return result; +// } +// +// right = rh.toString(); // ignored? super +// left = lh.toString(); // ignored? super +// if (Integer.MAX_VALUE == result) { +// result = left.compareTo(right); +// if (0 != result) return result; +// } +// // ignore source file path - may change? +// // lhSourceFilePath = lh.getSourceFilePath(); // ignored +// // lh.sourceFilePath = lh.getSourceFilePath(); // ignored +// // List rhRelations= rh.getRelations() ; // ignored +// // List lhRelations= lh.getRelations(); // ignored +// return 0; +// } +// return result; +// } +// } +// +// /** Comparator for LinkNode. */ +// static class LinkNodeComparator implements Comparator { +// public static Comparator SINGLETON = new LinkNodeComparator(); +// private LinkNodeComparator () {} +// public int compare(Object lhs, Object rhs) { +// int result = CompareUtil.compare(lhs, rhs); +// if (Integer.MAX_VALUE == result) { +// LinkNode lh = (LinkNode) lhs ; +// LinkNode rh = (LinkNode) rhs ; +// // LinkNode only has child and lexical name in toString +// result = lh.toString().compareTo(rh.toString()); +// } +// return result; +// } +// } // class LinkNodeComparator +// +// /** +// * Comparator for StructureNode. +// * <li>todo: implement comparators for each StructureNode subtype</li> +// */ +// static class StructureNodeComparator implements Comparator { +// public static Comparator SINGLETON = new StructureNodeComparator(); +// private StructureNodeComparator () {} +// public int compare(Object lhs, Object rhs) { +// int result = CompareUtil.compare(lhs, rhs); +// if (Integer.MAX_VALUE == result) { +// Class lhClass = lhs.getClass() ; +// Class rhClass = rhs.getClass() ; +// if ((StructureNode.class.isAssignableFrom(lhClass)) +// && (StructureNode.class.isAssignableFrom(rhClass))) { +// StructureNode lh = (StructureNode) lhs ; +// StructureNode rh = (StructureNode) rhs ; +// Object lhObject = lh.getName(); // todo: weak name-based comparison +// Object rhObject = rh.getName(); +// result = CompareUtil.compare(lhs, rhs); +// if (Integer.MAX_VALUE == result) { +// result = lhObject.toString().compareTo(rhObject.toString()); +// } +// } else { // urk - broken unless wrapper +// result = lhs.toString().compareTo(rhs.toString()); +// } +// } +// return result; +// } +// } +//} diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/SubTypeComparator.java b/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/SubTypeComparator.java new file mode 100644 index 000000000..3094fdb64 --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/SubTypeComparator.java @@ -0,0 +1,224 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + +package org.aspectj.testing.compare.adapters; + +import org.aspectj.testing.compare.CompareUtil; +import java.util.Comparator; + +// for testing code +import java.util.*; +import java.text.Collator; + +/** + * This adopts pairs of [Class, Comparator] + * and implements compare by delegation thereto, + * selecting the first added where the classes of the both inputs + * are assignable to the pair Class. + * It first applies the null-semantics of CompareUtil.compare(..), + * which holds that null values are less-than non-null values, + * and that two nulls are equal. + * Note that this class uses Object.equals(..)'s default reference + * equality, so two SubTypeComparator with the same list + * of delegates will NOT be considered equal. + * <p>todo: This class is not thread-safe. + * <p>todo: immutable: final list copy on construction to implement equals?? + */ +public class SubTypeComparator implements Comparator { + /** order-sensitive comparators collection */ + private Vector comparators; + /** copy of comparators for compare method */ + private Struct[] cache; + + /** default constructor */ + public SubTypeComparator () { + comparators = new Vector(); + } + + /** + * Return true if the Class is in the list of comparators + * as of the initial execution of the method. + * @param c the Class to look for a comparator + * @return true of comparator exists for c + */ + private boolean contains(Class c) { + final int size = comparators.size(); + for (int i = 0; i < size; i++) { + if (c == ((Struct) comparators.elementAt(i)).accepts) { + return true; + } + } + return false; + } + + /** + * Get copy of comparators for compare operation. + */ + private Struct[] getComparators() { // todo: sync on comparators + if ((null == cache) || (cache.length < comparators.size())) { + cache = new Struct[comparators.size()]; + comparators.copyInto(cache); + } + return cache; + } + + /** + * Add a Class, Comparator pair as delegate when + * both input are assignable to Class. + * Note that additions are checked in the order they are added, + * so add the lowest subtypes first. (i.e., if you add + * a comparator for Class Object first, none of the others + * will ever match.) + * @param accepts the Class supertype of objects to accept + * @param comparator the Comparator to use on such objects + * @return false if not added, because either input is null + * or because the Class is represented already. + */ + public final boolean addComparator(Class accepts, Comparator comparator) { + if ((null == accepts) || (null == comparator) + || (contains(accepts))) { + return false; + } + comparators.addElement(new Struct(accepts, comparator)); + return true; + } + + /** + * This implements compare by delegating + * to the first input Comparator + * where the class of the both input + * is assignable to the pair Class. + * It first enforces the null-semantics of CompareUtil.compare(..). + * @throws ClassCastException if both input are not null and + * they are not assignable to any registered Comparator. + */ + public final int compare(Object lhs, Object rhs) { + int result = CompareUtil.compare(lhs, rhs); + if (Integer.MAX_VALUE == result) { + Class lhClass = lhs.getClass() ; + Class rhClass = rhs.getClass() ; + Struct[] comp = getComparators(); + Class compClass; + for (int i = 0; i < comp.length; i++) { + compClass = comp[i].accepts; + if ((compClass.isAssignableFrom(lhClass)) + && (compClass.isAssignableFrom(lhClass))) { + return comp[i].comparator.compare(lhs, rhs); + } + } + // not found - throw ClassCastException + String mssg = "Unable to find Comparator for " + + "lhs Class: " + lhClass.getName() + + " rhs Class: " + rhClass.getName(); + throw new ClassCastException(mssg); + } + return result; + } + + /** + * (unnecessary) Struct to hold class-comparator pair + * is preparation for using collections + */ + static class Struct { + public final Class accepts; + public final Comparator comparator; + /** + * @param accepts the Class to accept input for - not null + * @param comparator the Comparator to compare input with - not null + */ + public Struct(Class accepts,Comparator comparator) { + this.accepts = accepts; + this.comparator = comparator; + } + /** delegate to accept hashcode */ + public int hashCode() { return accepts.hashCode() ; } + /** WARNING: fast comparison based only on accept key reference identity */ + public boolean equals(Object o) { + return ((null != o) && (o instanceof Struct) + && (accepts == ((Struct)o).accepts)); + } + } // class Struct + + //----------------------------------------------- test code + /** test code */ + static class Test { // todo move elsewhere + public void runTest(String[] args) { + if ((null == args) || (1 > args.length)) { + args = new String[] { "one", "two", "THREE", "FOUR", "fIVE", "6" }; + } + SubTypeComparator me = new SubTypeComparator(); + String[] copy = new String[args.length]; + System.arraycopy(args, 0, copy, 0, copy.length); + List list = Arrays.asList(args); + Throwable result = test(me, list); + if ((null == result) + && (!ClassCastException.class.isAssignableFrom(result.getClass()))) { + System.err.println("FAIL: expected cce: " + result); + } + me.addComparator(String.class, Collator.getInstance()); + me.addComparator(Object.class, new Comparator () { + public int compare (Object lhs, Object rhs) { + throw new Error("never used"); + }}); + result = test(me, list); + if (null != result) { + if (ClassCastException.class.isAssignableFrom(result.getClass())) { + System.err.println("FAIL: unexpected cce: " + result); + } else { + System.err.println("FAIL: unexpected Throwable: " + result); + } + } + // heterogeneous - pick Object + Object[] temp = new Object[] { "string", new Integer(1) }; + result = test(me, Arrays.asList(temp)); + if ((null == result) + && (!Error.class.isAssignableFrom(result.getClass()))) { + System.err.println("FAIL: expected Error: " + result); + } + + StringBuffer toPrint = print(Arrays.asList(copy), null); + toPrint.append("\n"); + print(list, toPrint); + System.err.println(toPrint.toString()); + } + + StringBuffer print(List list, StringBuffer sb) { + if (null == sb) sb = new StringBuffer(); + sb.append("["); + ListIterator iter = list.listIterator(); + while (iter.hasNext()) { + sb.append(iter.next().toString()); + if (iter.hasNext()) sb.append(", "); + } + sb.append("]"); + return sb; + } + + /** + * run comparison, return true if got expected exception + */ + Throwable test(Comparator c, List l) { + try { + Collections.sort(l,c); + return null; + } catch (Throwable r) { + return r; + } + } + } // class Test + + /** invoke Test.runTest(args) todo remove */ + public static void main(String[] args) { + new Test().runTest(args); + } +} // class SubTypeComparator diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/package.html b/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/package.html new file mode 100644 index 000000000..c2d839ed1 --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/package.html @@ -0,0 +1,50 @@ +<html> +<body> +These adapters facilitate using the GenericTreeNode +comparison utilities on different types of trees. +The classes currently in this package support Swing +<code>TreeNode</code> and AspectJ compiler <code>StructureNode</code>. + +<p> +<code>Structure</code> supports a command-line interface for +compiling <code>StructureNode</code> trees using +<code>AspectJCompiler</code> and saving +or loading them using serialization. Once loaded, two trees +can be compared using the <code>GenericTreeNode.traverse(..)</code> method, +providing it the appropriate visitors and comparators to implement +a comparison of the expected and actual structure trees and +report on any differences. (This is used to validate that +changes to the structure tree are backwards compatible with +trees from previous compiles, since the structure tree is +effectively exported from the compiler to the IDE tools.) + +<p> +If you want to support new trees, +<code>GenericTreeNodeFactoryI</code> is the interface to implement. +It specifies a factory to provide a <code>Comparator</code> based +on a given pair to be compared. This means that you can +use (or generate) comparators based on specific pairs. +Usually you will implement only for the target type, +so your factory may be final and always return a +singleton <code>Comparator.</code> +<p> +The <code>SubTypeComparator</code> implements a <code>Comparator</code> +container that acts as a <code>Comparator</code> by delegating to +contained <code>Comparator</code>s registered along with the class of the input +they accept. This permits you to write type-specific +<code>Comparator</code>s for heterogeneous trees. It does require +both members of the comparison be assignable to the +target class. + +<p> +<code>StructureGenericTreeNodeFactory</code> is an implementation of +<code>GenericTreeNodeFactoryI</code> for StructureNode trees. It +contains comparator classes for <code>RelationNode</code>, <code>LinkNode,</code> +and <code>ProgramElementNode</code>, +combined using a <code>SubTypeComparator.</code> +<p> +<code>JTreeNodeGenericTreeNodeFactory</code> is an implementation of +<code>GenericTreeNodeFactoryI</code> for Swing <code>TreeNode</code>'s, done only +for testing purposes. +</body> +</html> diff --git a/aspectj-attic/testing-src/org/aspectj/testing/compare/package.html b/aspectj-attic/testing-src/org/aspectj/testing/compare/package.html new file mode 100644 index 000000000..6bfec163d --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/compare/package.html @@ -0,0 +1,128 @@ +<html> +<body> +This package implements a general tree comparison as a +special case of visiting generic trees pairwise. +Trees are wrapped as <code>GenericTreeNode</code>, which has a static +method <code>boolean traverse(..)</code> which accepts +a visitor and traverses a pair of trees, calling the +visitor on each. +<p>This package supports four forms of generality +through the following classes: +<table border="+1"> +<tr><th>Classes</th> + <th>Capability</th></tr> +<tr><td valign="top">GenericTreeNode</td> + <td>Able to handle trees of varying types + by wrapping in a generic form.<td> + </tr> +<tr><td valign="top">GenericTreeNodesVisitorI, GenericTreeNode.traverse(..)</td> + <td>Can handle any type of pairwise visitor function + by accepting visitor in the traverse method.</td> + </tr> +<tr><td valign="top">{java.util.Comparator}, GenericTreeNode.getComparator()</td> + <td>The object comparison can be sensitive to the type + of the object on a per-object basis, established during + the process of wrapping the tree.</td> + </tr> +<tr><td valign="top">GenericTreeListOrdererI, GenericTreeListOrdererFactoryI</td> + <td>The ordering of children can be appropriate to + the objective of the traversal. e.g., when computing + "set intersection" rather than "list equals", the + order of children might be changed to align matching + children for the visits. + <br>This ordering can be determined as appropriate for each + list comparison by implementing a factory which selects + from the appropriate orderers. Any factory product is used + by the traverse(..) method to order children before + visiting.</td> + <td></td> + </tr> +</table> + +<p><u>Supported variants</u>: +The following variants are implemented or planned using the components above: +<table border="1"> +<tr><th>Component</th><th>Description</th></tr> +<tr><td colspan=2>Current</th></tr> +<tr><td>GenericTreeNode.PRINTALL</td> + <td>A visitor which prints out the entire tree.</td></tr> +<tr><td>GenericTreeNode.PRINTERR</td> + <td>A visitor which prints the nonmatching pairs.</td></tr> +<tr><td>GenericTreeNode.EXACT</td> + <td>A visitor which returns false if any pairs do not match.</td></tr> +<tr><td>TreeCompare</td> + <td>A sample program to read in serialized trees and compare them. + (but see Structure in the compare subpackage for a better example) </td></tr> +<tr><td>CompareUtil</td> + <td>Misc comparison utilities (e.g., for short-circuiting comparisons).</td></tr> +<tr><td colspan=2>Planned</th></tr> +<tr><td>GenericTreeNode.KIDDIFF</td> + <td>A visitor which calculates tree differences, using ordering children + (catches swaps, missed or added elements, within children)</td></tr> +<tr><td>GenericTreeNode.TREEDIFF</td> + <td>A visitor which calculates tree differences, accumulating the tree + (catches swaps, missed or added elements, throughout tree.)</td></tr> +</table> + +<p><u>Use</u>: + Run TreeCompare to use the comparer from the command line on a supported tree, + (currently only the Swing TreeNode implemented as DefaultMutableTreeNode). + +<p><u>Programming</u>:<br> +To support a new tree, see the Structure example in the compare subpackage +or use example of the Swing TreeNode: +<li>Write an adapter that uses GenericTreeNode to wrap your tree nodes</li> +<li>Write a factory that produces a wrapped tree</li> +<li>Write a Comparator that compares the underlying node object + to include with each node object wrapped. Be sure to implement + the Comparator.equals(Object) method correctly, i.e., returning + true when it is equivalent to another comparator. It is best + to use a singleton for each type of node you support. </li> +<li>Optionally write a visitor to perform whatever operations you wish. + Note that visitors must tolerate a single null input in order to + completely traverse unmatching trees.</li> +<li>To perform operations requiring preprocessing of child List's, + write children orderer(s) and provide a factory to select them.</li> + +<p>To design new algorithms/applications, bear in mind the main tools: +<li>The comparator that follows the node object</li> +<li>The visitor that traverses the tree </li> +<li>The child orderer that may preprocess the child lists </li> +<br>In particular, when going beyond pair-wise comparisons to + list-wise or tree-wise comparisons, you'll have to decide + where to put the appropriate logic. You may have a relatively + lightweight visitor and a heavyweight orderer, or no + orderer at all. In that case, you may need to invoke a + special method on your visitor after the traversal completes + to do any final processing. + +<h2>Future Work</h2>: +<p><u>Smarter group comparisons</u>: +<br>Does calculating maps help with diagnosing problems? +<pre> +Given two lists, + A [ a, b, c, d, e, f, g, h ] + B [ a, e, c, d, b, g, h ] +The result should say: + - B swapped order of e and b + - B omitted f +Note the match-map (index of matching element, if any): + A->B [ 0, 4, 2, 3, 1, -, 5, 6 ] + B->A [ 0, 4, 2, 3, 1, 6, 7 ] +the shift-map (difference between expected and actual order): + A->B [ 0, 3, 0, 0, -3, -, -1, -1 ] + B->A [ 0, 3, 0, 0, -3, 1, 1 ] + +Thus: +- detect swaps as complementary out-of-index order pairs + (todo: three-way or n-ary?) + - fix or ignore swaps +- detect shifts as complementary series + where shift-width n is +/- for both + - -n => + - if negative element is actual, n elements omitted + - if negative element is expected, n elements added +<pre> + +</body> +</html> diff --git a/aspectj-attic/testing-src/org/aspectj/testing/taskdefs/CompareFiles.java b/aspectj-attic/testing-src/org/aspectj/testing/taskdefs/CompareFiles.java new file mode 100644 index 000000000..fe1b760ac --- /dev/null +++ b/aspectj-attic/testing-src/org/aspectj/testing/taskdefs/CompareFiles.java @@ -0,0 +1,103 @@ +/* ******************************************************************* + * 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 Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Xerox/PARC initial implementation + * ******************************************************************/ + + +package org.aspectj.testing.taskdefs; + +import java.io.File; +import java.io.IOException; +import org.apache.tools.ant.*; + +/** + * Wrap file comparison utility as ant taskdef. + * (Whitespace semantics track the String.trim() and StringTokenizer class.) + * <table> + * <tr><td>lhsFile</td><td>path to left-hand-side file to compare (required)</td></tr> + * <tr><td>rhsFile</td><td>path to right-hand-side file to compare (required)</td></tr> + * <tr><td>output</td><td>path to output file (System.out otherwise)</td></tr> + * <tr><td>ignoreCase</td><td>convert to uppercase before comparison (boolean yes/no)</td></tr> + * <tr><td>trimWhitespace</td><td>ignore leading/trailing white space(boolean yes/no)</td></tr> + * <tr><td>collapseWhitespace</td><td>convert all white space runs to a single space (boolean yes/no)</td></tr> + * <tr><td>filterSpec</td><td>all specifications for a filter, based on the RegexpFilter class + * (currently, syntax: <code>{file | {-i|-t|-b|-s <pattern>|-s <patternFile>}..}</code></td></tr> + * </table> + * @see org.aspectj.testing.compare.RegexpFilter#init(String[],RegexpFilter) + */ +public class CompareFiles extends org.apache.tools.ant.Task { + /* + Unable to implement multiple inheritance except by delegation: + - Task subclass must be outer or ant throws InstantiationException + - if/since outer, the subclass getter/setters cannot refer to + protected fields in worker superclass absent inner worker + subclass delegate methods. yuck. + - getting access errors when trying to use the RuntimeConfigurable + to initialize the task. Looking at the Ant code, it does not + appear to be used for tasks?? I found none using it and the + initialization seems wrong... + */ + final private Worker worker; + public CompareFiles() { + worker = new Worker(); + } + protected File lhsFile; + protected File rhsFile; + public void setLhsFile(File file) { lhsFile = file; } + public void setRhsFile(File file) { rhsFile = file; } + public void setOutput(File file) { worker.setOutput(file); } + public void setFilterSpec(String str) { worker.setFilterSpec(str); } + public void setCollapseWhitespace(boolean ok) { worker.setCollapseWhitespace(ok); } + public void setTrimWhitespace(boolean ok) { worker.setTrimWhitespace(ok); } + public void setIgnoreCase(boolean ok) { worker.setIgnoreCase(ok); } + public void execute() throws BuildException { + if (!lhsFile.canRead()) { + log("FAIL taskdefs.CompareFiles: bad lhsFile: " + lhsFile); + } else if (!rhsFile.canRead()) { + log("FAIL taskdefs.CompareFiles: bad rhsFile: " + rhsFile); + } else if (rhsFile.isDirectory() != lhsFile.isDirectory()) { + log("FAIL taskdefs.CompareFiles: both must be dirs." + + " lhsFile=" + lhsFile + + " rhsFile=" + rhsFile); + } else { + worker.dodiff(lhsFile, rhsFile); + } + } +} // class CompareFiles + +/** worker class exposes state and converts Exception to BuildException */ +class Worker extends org.aspectj.testing.compare.CompareFiles { + String spec = null; + public void setOutput(File file) { output = file; } + public void setCollapseWhitespace(boolean ok) { collapseWhitespace = ok; } + public void setTrimWhitespace(boolean ok) { trimWhitespace = ok; } + public void setIgnoreCase(boolean ok) { ignoreCase = ok; } + public void setFilterSpec(String str) { spec = str; } + public void initFilter() { + if (null != spec) { + initFilter(spec, false); + } + } + + public void dodiff(File lhsFile, File rhsFile) throws BuildException { + initFilter(); + try { + super.diff(lhsFile, rhsFile); + } catch (IOException t) { + String s = t.getClass().getName() + ": " + t.getMessage(); + throw new BuildException(s, t); + } catch (Throwable e) { + String s = e.getClass().getName() + ": " + e.getMessage(); + throw new BuildException("Error - " + s, e); + } + } +} // class CompareFiles$Worker + |