From 63d88f163be18c14fccd02cc88b691679eb17e93 Mon Sep 17 00:00:00 2001 From: wisberg Date: Tue, 31 Dec 2002 18:25:37 +0000 Subject: initial version of unused/attic code for later reference --- .../org/aspectj/testing/compare/CompareFiles.java | 571 +++++++++++++++++++ .../org/aspectj/testing/compare/CompareUtil.java | 143 +++++ .../aspectj/testing/compare/GenericTreeNode.java | 427 ++++++++++++++ .../GenericTreeNodeListOrdererFactoryI.java | 30 + .../compare/GenericTreeNodeListOrdererI.java | 44 ++ .../testing/compare/GenericTreeNodesVisitorI.java | 31 + .../org/aspectj/testing/compare/Regexp.java | 40 ++ .../org/aspectj/testing/compare/RegexpFactory.java | 64 +++ .../org/aspectj/testing/compare/RegexpFilter.java | 622 +++++++++++++++++++++ .../testing/compare/RegexpFilterReader.java | 291 ++++++++++ .../org/aspectj/testing/compare/TreeCompare.java | 217 +++++++ .../compare/adapters/GenericTreeNodeFactoryI.java | 33 ++ .../adapters/JTreeNodeGenericTreeNodeFactory.java | 102 ++++ .../testing/compare/adapters/Structure.java | 438 +++++++++++++++ .../adapters/StructureGenericTreeNodeFactory.java | 308 ++++++++++ .../compare/adapters/SubTypeComparator.java | 224 ++++++++ .../aspectj/testing/compare/adapters/package.html | 50 ++ .../org/aspectj/testing/compare/package.html | 128 +++++ .../org/aspectj/testing/taskdefs/CompareFiles.java | 103 ++++ 19 files changed, 3866 insertions(+) create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/CompareFiles.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/CompareUtil.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNode.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNodeListOrdererFactoryI.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNodeListOrdererI.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/GenericTreeNodesVisitorI.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/Regexp.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/RegexpFactory.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/RegexpFilter.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/RegexpFilterReader.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/TreeCompare.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/GenericTreeNodeFactoryI.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/JTreeNodeGenericTreeNodeFactory.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/Structure.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/StructureGenericTreeNodeFactory.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/SubTypeComparator.java create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/adapters/package.html create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/compare/package.html create mode 100644 aspectj-attic/testing-src/org/aspectj/testing/taskdefs/CompareFiles.java (limited to 'aspectj-attic/testing-src/org') 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 + *
  • -i "ignore": ignore case
  • + *
  • -t "trim" : ignore leading and trailing white space
  • + *
  • -b "blanks": ignore differences in all white space
  • + * @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: + *
    descendFileTree(new File("."), new FileRunnerI() {
    +     *     public boolean accept(File f){
    +     *        System.out.println(f.getAbsolutePath());
    +     *        return true;
    +     *     }});
    + * @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: + *
    descendFileTree(new File("/"), new FileFilter() {
    +     *     public boolean run(File f){
    +     *        System.out.println(f.getAbsolutePath());
    +     *        return true;
    +     *     }}, false);
    + * To list files/dir from root using user recursion: + *
    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);
    + * @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. + *
    if (notSame(foo,bar,true)) then return false;
    + * @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: + *
  • null considered to be lesser
  • + *
  • reference or Object.equals() considered to be 0
  • + *
  • return Integer.MAX_VALUE for all other cases
  • + * + * + * + * + * + * + * + * + *
    resultinput
    -1null < rhs
    1lhs > null
    0null == null
    0lhs == rhs
    0lhs.equals(rhs)
    Integer.MAX_VALUE{all other cases}
    + * @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: + *
  • nodeComparator is not null
  • + *
  • node is not null
  • + *
  • children, if any, are assignable to childClass + * (only as of initialization, since list is adopted)
  • + *
  • Obeys GenericTreeNode contract, especially regarding equals
  • + *

    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. + *

    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 + *

  • this is the input, or
  • + *
  • the input is a GenericTreeNode, and
  • + *
  • the underlying nodes are equal(), or
  • the underlying nodes have equal comparators + * which return 0 from compare(...) + * @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: + *
    GenericTreeNode [parent={parentString()] [children={size}] [Node={thisString()]
    + */ + 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., return compare(this, rhs) + * @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. + *
  • any null component translates to "less than" another null component
  • + *
  • comparators must be equal according to + * Comparator.equals(Comparator)
  • + *
  • underlying nodes equal according to + * Comparator.compare(Object lhs, Object rhs)
  • + *
  • if the two comparators are not equal, then + * do arbitrary ordering of the node by toString().compareTo() + * of the underlying objects
  • + *
  • children are ignored. Two nodes may be equal even if + * they do not have the same children.
  • + * @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: + *
  • test line against all registered select statements + * to get all the matching (replace) operations (unsupported)
  • + * 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: + *
  • read line using superclass LineNumberReader.readLine()
  • + *
  • Preprocess with case and white space operations
  • + *
  • run all the replace operations on the input, in order
  • + *
  • return the line.
  • + * Using anything but the readLine() 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 RegexpFilterReader.main(args). + * @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 + * init(String[], RegexpFilter)) + * 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: {file | {-i|-t|-b|-s |-s }..} + * (for booleans, use lowercase to enable, uppercase to disable). + * @param args the String[] containing file to input plus any number of... + *
  • -i "ignore": ignore case
  • + *
  • -t "trim" : ignore leading and trailing white space
  • + *
  • -b "blanks": ignore differences in all white space
  • + *
  • -s "{s/pattern/expression/};...": + * replace pattern in lines with expression
  • + *
  • -S : same as s, but read commands from file
  • + * @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 |-s }..}"; + 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 + * static RegexpFilter init(String[],RegexpFilter) + */ + 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 + * ReplaceOperation.makeReplaceOperation(String, ErrHandler), + * 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, + *
  • emit unmatched prefix, if any
  • + *
  • emit replacement text as-is, except that + * \[0-9] in the replacement text is replaced + * with the matching subsection of the input text
  • + *
  • emit unmatched suffix, if any
  • + * @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 close() + */ + 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. + *
  • read line using superclass LineNumberReader.readLine()
  • + *
  • Preprocess with case and white space operations
  • + *
  • run all the replace operations on the input, in order
  • + *
  • return the line.
  • + * Using anything but the readLine() 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) GenericTreeNode. + * 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: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    {ajc compiler args}The set of compiler arguments, to compile a new tree
    -expect {file}.serThe old Structure tree to read in and compare with new
    -actual {file}.serThe new Structure tree to read in (i.e., no compile)
    -save {file}.serSerialize the results of the compile to {file}.ser.
    -printallPrint all pairs in long format
    -printeachPrint each item in short format + * - used when only one tree is available
    -printerrPrint pairs of items that do not match + * - used when only both trees are available
    -sortStringbefore comparing, do a string-sort on each child list.
    -toOutRedirect output from System.err to System.out (for all instances of this class)
    -notestDo not run test (e.g., just compile and save)
    + * @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: + *
  • processArgs(..)
  • + *
  • saveActual(..) if saving actual to file
  • + *
  • doComparison(..) of actual and expected per visitor/sorter options
  • + *

    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 main(String[] args) + */ + 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 Structure.STRING_COMP. + */ + static class StringSort implements GenericTreeNodeListOrdererI { + /** + * Order input lists (not copies) + * using the Comparator Structure.STRING_COMP. + * @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. +// *

  • todo: implement comparators for each StructureNode subtype
  • +// */ +// 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. + *

    todo: This class is not thread-safe. + *

    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 @@ + + +These adapters facilitate using the GenericTreeNode +comparison utilities on different types of trees. +The classes currently in this package support Swing +TreeNode and AspectJ compiler StructureNode. + +

    +Structure supports a command-line interface for +compiling StructureNode trees using +AspectJCompiler and saving +or loading them using serialization. Once loaded, two trees +can be compared using the GenericTreeNode.traverse(..) 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.) + +

    +If you want to support new trees, +GenericTreeNodeFactoryI is the interface to implement. +It specifies a factory to provide a Comparator 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 Comparator. +

    +The SubTypeComparator implements a Comparator +container that acts as a Comparator by delegating to +contained Comparators registered along with the class of the input +they accept. This permits you to write type-specific +Comparators for heterogeneous trees. It does require +both members of the comparison be assignable to the +target class. + +

    +StructureGenericTreeNodeFactory is an implementation of +GenericTreeNodeFactoryI for StructureNode trees. It +contains comparator classes for RelationNode, LinkNode, +and ProgramElementNode, +combined using a SubTypeComparator. +

    +JTreeNodeGenericTreeNodeFactory is an implementation of +GenericTreeNodeFactoryI for Swing TreeNode's, done only +for testing purposes. + + 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 @@ + + +This package implements a general tree comparison as a +special case of visiting generic trees pairwise. +Trees are wrapped as GenericTreeNode, which has a static +method boolean traverse(..) which accepts +a visitor and traverses a pair of trees, calling the +visitor on each. +

    This package supports four forms of generality +through the following classes: + + + + + + + + + + + + + + + +
    ClassesCapability
    GenericTreeNodeAble to handle trees of varying types + by wrapping in a generic form. +
    GenericTreeNodesVisitorI, GenericTreeNode.traverse(..)Can handle any type of pairwise visitor function + by accepting visitor in the traverse method.
    {java.util.Comparator}, GenericTreeNode.getComparator()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.
    GenericTreeListOrdererI, GenericTreeListOrdererFactoryIThe 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. +
    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.
    + +

    Supported variants: +The following variants are implemented or planned using the components above: + + + + + + + + + + + + + + + + + + +
    ComponentDescription
    Current
    GenericTreeNode.PRINTALLA visitor which prints out the entire tree.
    GenericTreeNode.PRINTERRA visitor which prints the nonmatching pairs.
    GenericTreeNode.EXACTA visitor which returns false if any pairs do not match.
    TreeCompareA sample program to read in serialized trees and compare them. + (but see Structure in the compare subpackage for a better example)
    CompareUtilMisc comparison utilities (e.g., for short-circuiting comparisons).
    Planned
    GenericTreeNode.KIDDIFFA visitor which calculates tree differences, using ordering children + (catches swaps, missed or added elements, within children)
    GenericTreeNode.TREEDIFFA visitor which calculates tree differences, accumulating the tree + (catches swaps, missed or added elements, throughout tree.)
    + +

    Use: + Run TreeCompare to use the comparer from the command line on a supported tree, + (currently only the Swing TreeNode implemented as DefaultMutableTreeNode). + +

    Programming:
    +To support a new tree, see the Structure example in the compare subpackage +or use example of the Swing TreeNode: +

  • Write an adapter that uses GenericTreeNode to wrap your tree nodes
  • +
  • Write a factory that produces a wrapped tree
  • +
  • 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.
  • +
  • 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.
  • +
  • To perform operations requiring preprocessing of child List's, + write children orderer(s) and provide a factory to select them.
  • + +

    To design new algorithms/applications, bear in mind the main tools: +

  • The comparator that follows the node object
  • +
  • The visitor that traverses the tree
  • +
  • The child orderer that may preprocess the child lists
  • +
    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. + +

    Future Work

    : +

    Smarter group comparisons: +
    Does calculating maps help with diagnosing problems? +

    +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
    +
    +
    +
    +
    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.)
    + * 
    + * 
    + * 
    + * 
    + * 
    + * 
    + * 
    + * 
    + * 
    lhsFilepath to left-hand-side file to compare (required)
    rhsFilepath to right-hand-side file to compare (required)
    outputpath to output file (System.out otherwise)
    ignoreCaseconvert to uppercase before comparison (boolean yes/no)
    trimWhitespaceignore leading/trailing white space(boolean yes/no)
    collapseWhitespaceconvert all white space runs to a single space (boolean yes/no)
    filterSpecall specifications for a filter, based on the RegexpFilter class + * (currently, syntax: {file | {-i|-t|-b|-s |-s }..}
    + * @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 + -- cgit v1.2.3