You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@abdera.apache.org by jm...@apache.org on 2006/07/07 00:08:33 UTC

svn commit: r419720 [3/5] - in /incubator/abdera/java/trunk/build: ./ tools/ tools/retroweaver/ tools/retroweaver/docs/ tools/retroweaver/docs/guide/ tools/retroweaver/docs/images/ tools/retroweaver/lib/ tools/retroweaver/release/ tools/retroweaver/src...

Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RefVerifier.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RefVerifier.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RefVerifier.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RefVerifier.java Thu Jul  6 15:08:30 2006
@@ -0,0 +1,575 @@
+package com.rc.retroweaver;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+import com.rc.retroweaver.event.VerifierListener;
+
+
+/**
+ * Reads through a class file searching for references to classes, methods, or
+ * fields, which don't exist on the specified classpath. This is primarily
+ * useful when trying to target one JDK while using the compiler for another.
+ */
+public class RefVerifier extends ClassAdapter {
+
+	private int target;
+
+	private String currentclassName;
+
+	private RetroWeaverClassLoader classLoader;
+
+	private List<String> classPathArray;
+
+	private Set<String> failedClasses;
+
+	private VerifierListener listener;
+
+	private int warningCount;
+
+	private List<String> classes;
+
+	private Map<String, SoftReference<ClassReader>> classReaderCache = new HashMap<String, SoftReference<ClassReader>>();
+
+	private static final String nl = System.getProperty("line.separator");
+
+	public RefVerifier(int target, ClassVisitor cv, List<String> classPathArray, VerifierListener listener) {
+		super(cv);
+		classLoader = new RetroWeaverClassLoader();
+		this.classPathArray = classPathArray;
+
+		this.listener = listener;
+		this.target = target;
+
+		classes = new LinkedList<String>();
+	}
+
+	public void addClass(String className) {
+		classes.add(className);
+	}
+
+	public void verifyJarFile(String jarFileName) throws IOException {
+		JarFile jarFile = new JarFile(jarFileName);
+
+		listener.verifyPathStarted("Verifying " + classes.size() + " classe(s)");
+		classLoader.setClassPath(classPathArray);
+
+		for (String name : classes) {
+			JarEntry entry = jarFile.getJarEntry(name);
+			InputStream is = jarFile.getInputStream(entry);
+			verifyClass(is, name);
+		}
+	}
+
+	public void verifyFiles() throws IOException {
+		listener.verifyPathStarted("Verifying " + classes.size() + " classe(s)");
+		classLoader.setClassPath(classPathArray);
+
+		for (String sourcePath : classes) {
+			verifyClass(new FileInputStream(sourcePath), sourcePath);			
+		}
+	}
+
+	private void verifySingleClass(String classFileName) throws IOException {
+		classLoader.setClassPath(classPathArray);
+
+		verifyClass(new FileInputStream(classFileName), classFileName);
+	}
+
+	private void verifyClass(InputStream sourceStream, String classFileName)
+			throws IOException {
+
+		failedClasses = new HashSet<String>();
+
+        ClassReader cr = new ClassReader(sourceStream);
+        cr.accept(this, false);
+	}
+
+	private void unknowClassWarning(String className, String msg) {
+		String report = currentclassName + ": unknown class "
+				+ className;
+
+		if (msg != null) {
+			report += ": " + msg;
+		}
+
+		warning(report);
+	}
+
+	private void unknownFieldWarning(String owner, String name, String desc, String msg) {
+		String report = currentclassName + ": unknown field " + name + '/' + desc.replace('/', '.');
+
+		if (msg != null) {
+			report += ", " + msg;
+		}
+
+		warning(report);
+	}
+
+	private void unknownMethodWarning(String owner, String name, String desc, String msg) {
+		String report = currentclassName + ": unknown method " + name + '/' + desc.replace('/', '.');
+
+		if (msg != null) {
+			report += ", " + msg;
+		}
+
+		warning(report);
+	}
+
+	private void invalidClassVersion(String className, int target, int version) {
+		String report = className + ": invalid class version " + version + ", target is " + target;
+
+		warning(report);
+	}
+
+	private void warning(String report) {
+		warningCount++;
+		listener.acceptWarning(report);
+	}
+
+	public void displaySummary() {
+		if (warningCount != 0)
+			listener.displaySummary(warningCount);
+	}
+
+	private ClassReader getClassReader(String className) throws ClassNotFoundException {
+		ClassReader reader = null;
+		SoftReference<ClassReader> ref = classReaderCache.get(className);
+		if (ref != null)
+			reader = ref.get();
+
+		if (reader == null) {
+			byte b[] = classLoader.getClassData(className);
+
+			reader = new ClassReader(b);
+
+			classReaderCache.put(className, new SoftReference<ClassReader>(reader));
+
+			// class file version should not be higher than target
+			int version = reader.readShort(6); // get major number only
+			if (version > target) {
+				invalidClassVersion(className.replace('/', '.'), target, version);
+			}
+		}
+		return reader;		
+	}
+
+	public static String getUsage() {
+		return "Usage: RefVerifier <options>" + nl + " Options: " + nl
+				+ " -class <path to class to verify> (required) " + nl
+				+ " -cp <classpath containing valid classes> (required)";
+	}
+
+	public static void main(String[] args) throws IOException {
+
+		List<String> classpath = new ArrayList<String>();
+		String classfile = null;
+
+		for (int i = 0; i < args.length; ++i) {
+			String command = args[i];
+			++i;
+
+			if (command.equals("-class")) {
+				classfile = args[i];
+			} else if (command.equals("-cp")) {
+				String path = args[i];
+				StringTokenizer st = new StringTokenizer(path,
+						File.pathSeparator);
+				while (st.hasMoreTokens()) {
+					classpath.add(st.nextToken());
+				}
+			} else {
+				System.out
+						.println("I don't understand the command: " + command);
+				System.out.println();
+				System.out.println(getUsage());
+				return;
+			}
+		}
+
+		if (classfile == null) {
+			System.out.println("Option \"-class\" is required.");
+			System.out.println();
+			System.out.println(getUsage());
+			return;
+		}
+
+		RefVerifier vr = new RefVerifier(Weaver.VERSION_1_4, EMPTY_VISITOR, classpath,
+				new DefaultRefVerifierListener(true));
+		vr.verifySingleClass(classfile);
+		vr.displaySummary();
+	}
+	
+	private void checkClassName(String className) {
+		Type t = Type.getType(className);
+
+		switch (t.getSort()) {
+		case Type.ARRAY:
+			t = t.getElementType();
+			if (t.getSort() != Type.OBJECT)
+				return;
+
+			// fall through to object processing
+		case Type.OBJECT:
+			className = t.getClassName();
+			break;
+		default:
+			return;
+		}
+		
+		checkSimpleClassName(className);
+	}
+
+	private void checkClassNameInType(String className) {
+		switch (className.charAt(0)) {
+			case 'L':
+			case '[':
+				checkClassName(className);
+				break;
+			default:
+				checkSimpleClassName(className);
+		}
+	}
+
+	private void checkSimpleClassName(String className) {
+		try {
+			className = className.replace('.', '/');
+			getClassReader(className);
+		} catch (ClassNotFoundException e) {
+			failedClasses.add(className);
+			unknowClassWarning(className.replace('/', '.'), null);
+		}
+	}
+
+	// visitor methods
+
+    public void visit(
+        final int version,
+        final int access,
+        final String name,
+        final String signature,
+        final String superName,
+        final String[] interfaces)
+    {
+		listener.verifyClassStarted("Verifying " + name);
+
+		currentclassName = name.replace('/', '.');
+
+		if (superName != null) {
+			checkSimpleClassName(superName);
+		}
+		if (interfaces != null) {
+			for (int i = 0; i < interfaces.length; ++i) {
+				checkSimpleClassName(interfaces[i]);
+			}
+		}
+
+		cv.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    public void visitOuterClass(
+        final String owner,
+        final String name,
+        final String desc)
+    {
+    	checkSimpleClassName(owner);
+
+    	cv.visitOuterClass(owner, name, desc);
+    }
+
+    public void visitInnerClass(
+        final String name,
+        final String outerName,
+        final String innerName,
+        final int access)
+    {
+        if (name != null) {
+        	checkSimpleClassName(name);
+        }
+        if (outerName != null) {
+        	checkSimpleClassName(outerName);
+        }
+        
+        cv.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        if (exceptions != null) {
+            for (String s: exceptions)
+            	checkSimpleClassName(s);
+        }
+    
+        return new MethodVerifier(cv.visitMethod(access, name, desc, signature, exceptions));
+    }
+
+    private class MethodVerifier extends MethodAdapter {
+
+    	MethodVerifier(MethodVisitor mv) {
+    		super(mv);
+    	}
+        
+    	public void visitTypeInsn(int opcode, String desc) {
+    		checkClassNameInType(desc);
+    		
+    		mv.visitTypeInsn(opcode, desc);
+    	}
+ 
+    	public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+			// Don't report a field error, about a class for which we've
+			// already shown an error
+			if (!failedClasses.contains(owner)) {
+				try {
+					if (!findField(owner, name, desc))
+						unknownFieldWarning(owner, name,desc, "Field not found in " + owner.replace('/', '.'));
+				} catch (ClassNotFoundException e) {
+						unknownFieldWarning(owner, name,desc, "The class, " + owner.replace('/', '.')
+						+ ", could not be located: " + e.getMessage());
+				}
+			}
+			mv.visitFieldInsn(opcode, owner, name, desc);
+    	}
+    	
+    	public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+			if (!failedClasses.contains(owner) && owner.charAt(0) != '[') {
+				// Don't report a method error, about a class for which we've
+				// already shown an error.
+				// We just ignore methods called on arrays, because we know
+				// they must exist   		
+
+				try {
+					if (!findMethod(owner, name, desc)) {
+						unknownMethodWarning(owner, name, desc, "Method not found in " + owner.replace('/', '.'));						
+					}
+				} catch (ClassNotFoundException e) {
+					unknownMethodWarning(owner, name, desc, "The class, " + owner.replace('/', '.')
+							+ ", could not be located: " + e.getMessage());
+				}
+			}
+
+			mv.visitMethodInsn(opcode, owner, name, desc);
+    	}
+
+    	public void visitMultiANewArrayInsn(String desc, int dims) {
+    		checkClassName(desc);
+    		
+    		mv.visitMultiANewArrayInsn(desc, dims);
+    	}
+
+    	public void visitLocalVariable(
+	            String name,
+	            String desc,
+	            String signature,
+	            Label start,
+	            Label end,
+	            int index) {
+    		checkClassName(desc);
+    		
+    		mv.visitLocalVariable(name, desc, signature, start, end, index);
+    	}
+
+    }
+
+	private boolean findField(String owner, final String name, final String c) throws ClassNotFoundException {
+		String javaClassName = owner;
+		while (true) {
+			ClassReader reader = getClassReader(javaClassName);
+			FindFieldOrMethodClassVisitor visitor = new FindFieldOrMethodClassVisitor(false, name, c);
+		
+			try {
+				reader.accept(visitor, false);
+			} catch (Success s) {
+				return true;
+			}
+			String[] is = visitor.classInterfaces;
+			for (String i : is)
+				if (findField(i, name, c))
+					return true;
+
+			if (javaClassName.equals("java/lang/Object"))
+				return false;
+			javaClassName = visitor.superClassName;
+		}
+	}
+
+	private boolean findMethod(final String owner, final String name, final String desc) throws ClassNotFoundException {
+		String javaClassName = owner;
+		while (true) {
+			ClassReader reader = getClassReader(javaClassName);
+			FindFieldOrMethodClassVisitor visitor = new FindFieldOrMethodClassVisitor(true, name, desc);
+			try {
+				reader.accept(visitor, false);
+			} catch (Success s) {
+				return true;
+			}
+
+			if (visitor.isInterface || visitor.isAbstract) {
+				String[] is = visitor.classInterfaces;
+				for (String i : is) {
+					if (findMethod(i, name, desc))
+						return true;
+				}
+				if (visitor.isInterface) {
+					return false;
+				}
+			}
+
+			if (javaClassName.equals("java/lang/Object"))
+				return false;
+			javaClassName = visitor.superClassName;
+		}
+	}
+
+	private static final EmptyVisitor EMPTY_VISITOR = new EmptyVisitor();
+
+	private static class Success extends RuntimeException {};
+
+	// Visitor to search for fields or methods in supplier classes
+
+	private static class FindFieldOrMethodClassVisitor implements ClassVisitor {
+		FindFieldOrMethodClassVisitor(boolean methdodMatcher, final String name, final String desc) {
+			this.searchedName = name;
+			this.searchedDesc = desc;
+			this.methdodMatcher = methdodMatcher;
+		}
+		boolean methdodMatcher;
+		final String searchedName;
+		final String searchedDesc;
+
+		String classInterfaces[];
+		String superClassName;
+		boolean isInterface;
+		boolean isAbstract;
+
+	    public void visit(
+	            final int version,
+	            final int access,
+	            final String name,
+	            final String signature,
+	            final String superName,
+	            final String[] interfaces)
+	        {
+	    		classInterfaces = interfaces;
+	    		superClassName = superName;
+	    		isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
+	    		isAbstract = (access & Opcodes.ACC_ABSTRACT) != 0;
+	        }
+
+	    public void visitSource(final String source, final String debug) {
+	    }
+
+	    public void visitOuterClass(
+	        final String owner,
+	        final String name,
+	        final String desc)
+	    {
+	    }
+
+	    public AnnotationVisitor visitAnnotation(
+	        final String desc,
+	        final boolean visible)
+	    {
+	        return EMPTY_VISITOR;
+	    }
+
+	    public void visitAttribute(final Attribute attr) {
+	    }
+
+	    public void visitInnerClass(
+	        final String name,
+	        final String outerName,
+	        final String innerName,
+	        final int access)
+	    {
+	    }
+
+	    public FieldVisitor visitField(
+	            final int access,
+	            final String name,
+	            final String desc,
+	            final String signature,
+	            final Object value) {
+	    	if (!methdodMatcher)
+		    	if (name.equals(searchedName) && desc.equals(searchedDesc)) {
+		    		throw new Success();
+		    	}
+	        return null;
+	   }
+
+		public MethodVisitor visitMethod(
+	            int access,
+	            String name,
+	            String desc,
+	            String signature,
+	            String[] exceptions) {
+			if (methdodMatcher)
+				if (name.equals(searchedName) && desc.equals(searchedDesc)) {
+					throw new Success();
+				}
+	        return null;
+	   }
+
+	    public void visitEnd() {
+	    }
+	}
+
+}
+
+class DefaultRefVerifierListener implements VerifierListener {
+
+	private boolean verbose;
+
+	DefaultRefVerifierListener(boolean verbose) {
+		this.verbose = verbose;
+	}
+
+	public void verifyPathStarted(String msg) {
+		System.out.println("[RefVerifier] " + msg);
+	}
+
+	public void verifyClassStarted(String msg) {
+		if (verbose)
+			System.out.println("[RefVerifier] " + msg);
+	}
+
+	public void acceptWarning(String msg) {
+		System.out.println("[RefVerifier] " + msg);
+	}
+
+	public void displaySummary(int warningCount) {
+		System.out.println("[RefVerifier] Verification complete, "
+				+ warningCount + " warning(s).");
+	}
+
+}

Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaver.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaver.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaver.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaver.java Thu Jul  6 15:08:30 2006
@@ -0,0 +1,1013 @@
+package com.rc.retroweaver;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import com.rc.retroweaver.event.WeaveListener;
+import com.rc.retroweaver.optimizer.ClassConstantsCollector;
+import com.rc.retroweaver.optimizer.Constant;
+import com.rc.retroweaver.optimizer.ConstantComparator;
+import com.rc.retroweaver.optimizer.ConstantPool;
+
+/**
+ * A bytecode enhancer that translates Java 1.5 class files into Java 1.4 class
+ * files. The enhancer performs primarily two tasks: 1) Reverses changes made to
+ * the class file format in 1.5 to the former 1.4 format. 2) Replaces compiler
+ * generated calls into the new 1.5 runtime with calls into RetroWeaver's
+ * replacement runtime.
+ */
+public class RetroWeaver {
+
+	private int target;
+
+	private boolean lazy;
+
+	/**
+	 * Indicates whether the generic signatures should be stripped. Default to <code>false</code>.
+	 */
+	private boolean stripSignatures;
+
+	private int weavedClassCount;
+
+	private WeaveListener listener;
+
+	private RefVerifier verifier;
+
+	private static final String newLine = System.getProperty("line.separator");
+
+	public RetroWeaver(int target) {
+		this.target = target;
+	}
+
+	protected static final FileFilter classFilter = new FileFilter() {
+		public boolean accept(File f) {
+			return f.getName().endsWith(".class");
+		}
+	};
+
+	protected static final FileFilter subdirFilter = new FileFilter() {
+		public boolean accept(File f) {
+			return f.isDirectory();
+		}
+	};
+
+	protected static void buildFileSets(ArrayList<File[]> fileSets, File path) {
+		File[] files = path.listFiles(classFilter);
+		if (files != null) {
+			fileSets.add(files);
+		}
+
+		File[] subdirs = path.listFiles(subdirFilter);
+		if (subdirs != null) {
+			for (File subdir : subdirs)
+				buildFileSets(fileSets, subdir);
+		}
+	}
+
+	private void displayStartMessage(int n) {
+		listener.weavingStarted("Processing " + n + " classe(s)");	
+	}
+
+	private void displayEndMessage() {
+		listener.weavingCompleted(Integer.toString(weavedClassCount) + " classe(s) weaved.");
+	}
+
+	public void weave(File path) throws IOException {
+		ArrayList<File[]> fileSets = new ArrayList<File[]>();
+
+		buildFileSets(fileSets, path);
+
+		int n = 0;
+		for (File[] set : fileSets)
+			n += set.length;
+		displayStartMessage(n);
+
+		for (int i = 0; i < fileSets.size(); i++) {
+			for (File file : fileSets.get(i)) {
+				String sourcePath = file.getCanonicalPath();
+				weave(sourcePath, null);
+			}
+		}
+		displayEndMessage();
+
+		if (verifier != null) {
+			verifier.verifyFiles();
+			verifier.displaySummary();
+		}
+	}
+
+	public void weave(File[] baseDirs, String[][] fileSets, File outputDir)
+			throws IOException {
+		int n = 0;
+		for (String[] set : fileSets)
+			n += set.length;
+		displayStartMessage(n);
+
+		Set<String> weaved = new HashSet<String>();
+		for (int i = 0; i < fileSets.length; i++) {
+			for (String fileName : fileSets[i]) {
+				File file = new File(baseDirs[i], fileName);
+				String sourcePath = file.getCanonicalPath();
+				String outputPath = null;
+				if (outputDir != null)
+					outputPath = new File(outputDir, fileName)
+							.getCanonicalPath();
+
+				// Weave it unless already weaved.
+				if (!weaved.contains(sourcePath)) {
+					weave(sourcePath, outputPath);
+					weaved.add(sourcePath);
+				}
+			}
+		}
+		displayEndMessage();
+
+		if (verifier != null) {
+			verifier.verifyFiles();
+			verifier.displaySummary();
+		}
+	}
+
+	public void weaveJarFile(String sourceJarFileName, String destJarFileName)
+			throws IOException {
+		JarFile jarFile = new JarFile(sourceJarFileName);
+		ArrayList<JarEntry> entries = Collections.list(jarFile.entries());
+
+		OutputStream os = new FileOutputStream(destJarFileName);
+		JarOutputStream out = new JarOutputStream(os);
+
+		int n = 0;
+		for (JarEntry entry : entries) {
+			if (entry.getName().endsWith(".class"))
+				n++;
+		}
+		displayStartMessage(n);
+
+		for (JarEntry entry : entries) {
+			String name = entry.getName();
+			InputStream dataStream = null;
+			if (name.endsWith(".class")) {
+				// weave class
+				InputStream is = jarFile.getInputStream(entry);
+				ByteArrayOutputStream classStream = new ByteArrayOutputStream();
+				if (weave(is, name, classStream)) {
+					// class file was modified
+					weavedClassCount++;
+
+					dataStream = new ByteArrayInputStream(classStream
+							.toByteArray());
+
+					// create new entry
+					entry = new JarEntry(name);
+					recordFileForVerifier(name);
+				}
+			}
+
+			if (dataStream == null) {
+				// not a class file or class wasn't no
+				dataStream = jarFile.getInputStream(entry);
+			}
+			// writing entry
+			out.putNextEntry(entry);
+
+			// writing data
+			int len;
+			final byte[] buf = new byte[1024];
+			while ((len = dataStream.read(buf)) >= 0) {
+				out.write(buf, 0, len);
+			}
+		}
+		out.close();
+
+		displayEndMessage();
+
+		if (verifier != null) {
+			verifier.verifyJarFile(destJarFileName);
+			verifier.displaySummary();
+		}
+	}
+
+	public void weave(String sourcePath, String outputPath) throws IOException {
+		InputStream is = new FileInputStream(sourcePath);
+		try {
+			ByteArrayOutputStream bos = new ByteArrayOutputStream();
+			if (weave(is, sourcePath, bos)) {
+				// new class was generated
+				weavedClassCount++;
+
+				if (outputPath == null) {
+					outputPath = sourcePath;
+				} else {
+					// create parent dir if necessary
+					File parentDir = new File(outputPath).getParentFile();
+					if (parentDir != null) {
+						parentDir.mkdirs();
+					}
+				}
+				FileOutputStream fos = new FileOutputStream(outputPath);
+				fos.write(bos.toByteArray());
+				fos.close();
+				
+				recordFileForVerifier(outputPath);
+			} else {
+				// We're lazy and the class already has the target version.
+
+				if (outputPath == null) {
+					// weaving in place
+					return;
+				}
+
+				File dir = new File(outputPath).getParentFile();
+				if (dir != null)
+					dir.mkdirs();
+
+				File sf = new File(sourcePath);
+				File of = new File(outputPath);
+
+				if (!of.isFile()
+						|| !of.getCanonicalPath().equals(sf.getCanonicalPath())) {
+					// Target doesn't exist or is different from source so copy
+					// the file and transfer utime.
+					FileInputStream fis = new FileInputStream(sf);
+					byte[] bytes = new byte[(int) sf.length()];
+					fis.read(bytes);
+					fis.close();
+					FileOutputStream fos = new FileOutputStream(of);
+					fos.write(bytes);
+					fos.close();
+					of.setLastModified(sf.lastModified());
+				}
+			}
+		} finally {
+			try {
+				is.close();
+			} catch (IOException e) {
+			}
+		}
+	}
+
+	private void recordFileForVerifier(String fileName) {
+		if (verifier != null) {
+			verifier.addClass(fileName);
+		}
+	}
+
+	private static final boolean COMPACT_CONSTANTS = true;
+
+	protected boolean weave(InputStream sourceStream, String fileName, ByteArrayOutputStream bos)
+			throws IOException {
+
+        ClassReader cr = new ClassReader(sourceStream);
+        ClassWriter cw = new ClassWriter(true);
+
+        try {
+        	NameTranslatorClassVisitor translator;
+        	ConstantPool cp;
+            if (COMPACT_CONSTANTS) {
+                cp = new ConstantPool();
+                ClassConstantsCollector ccc = new ClassConstantsCollector(cw, cp);
+                translator = new NameTranslatorClassVisitor(ccc);
+            } else {
+            	translator = new NameTranslatorClassVisitor(cw);
+            }
+            ClassWeaver classWeaver = new ClassWeaver(translator, lazy, target, listener);
+            ClassVisitor v;
+            if (stripSignatures) {
+                v = new SignatureStripper(classWeaver);
+            } else {
+            	v = classWeaver;
+            }
+            cr.accept(v, false);      	
+
+            if (COMPACT_CONSTANTS) {
+            	Set<Constant> constants = new TreeSet<Constant>(new ConstantComparator());
+            	constants.addAll(cp.values());
+
+            	cr = new ClassReader(cw.toByteArray());
+                cw = new ClassWriter(false);
+                for(Constant c: constants)
+                	c.write(cw);
+                cr.accept(cw, false);
+            }
+
+        	bos.write(cw.toByteArray());
+        	return true;
+        } catch (LazyException e) {
+        	return false;
+        }
+ 	}
+
+    static void optimize(final InputStream is, final ByteArrayOutputStream os)
+            throws IOException
+    {
+    }
+
+	public void setListener(WeaveListener listener) {
+		this.listener = listener;
+	}
+
+	public void setLazy(boolean lazy) {
+		this.lazy = lazy;
+	}
+
+	public void setVerifier(RefVerifier verifier) {
+		this.verifier = verifier;
+	}
+
+	public static String getUsage() {
+		return "Usage: RetroWeaver " + newLine + " <source path>" + newLine
+				+ " [<output path>]";
+	}
+
+	public static void main(String[] args) {
+
+		if (args.length < 1) {
+			System.out.println(getUsage());
+			return;
+		}
+
+		String sourcePath = args[0];
+		String outputPath = null;
+
+		if (args.length > 1) {
+			outputPath = args[1];
+		}
+
+		try {
+			RetroWeaver weaver = new RetroWeaver(Weaver.VERSION_1_4);
+			weaver.weave(sourcePath, outputPath);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * @param stripSignatures The stripSignatures to set.
+	 */
+	public void setStripSignatures(boolean stripSignatures) {
+		this.stripSignatures = stripSignatures;
+	}
+}
+
+class LazyException extends RuntimeException {
+}
+
+class ClassWeaver extends ClassAdapter implements Opcodes {
+
+    private boolean lazy;
+    private int target;
+    private WeaveListener listener;
+
+    private String className;
+
+    private boolean isEnum;
+    private boolean isInterface;
+
+    private Set<String> classLiteralCalls = new HashSet<String>();
+
+    public ClassWeaver(final ClassVisitor cv, boolean lazy, int target, WeaveListener listener) {
+        super(cv);
+        this.lazy = lazy;
+        this.target = target;
+        this.listener = listener;
+    }
+
+    public void visit(
+        final int version,
+        final int access,
+        final String name,
+        final String signature,
+        final String superName,
+        final String[] interfaces)
+    {
+    	if (lazy && (version <= target)) {
+        	// abort all visitors
+    		throw new LazyException();
+    	}
+
+		if (listener != null) {
+			listener.weavingPath(name);
+		}
+
+		className = name;
+        isEnum = superName != null && superName.equals("java/lang/Enum");
+        isInterface = (access & ACC_INTERFACE) == ACC_INTERFACE;
+
+        cv.visit(target, // Changes the format of the class file from 1.5 to the target value.
+                access,
+                name,
+                signature,
+                superName,
+                interfaces);
+    }
+
+    public void visitInnerClass(
+        final String name,
+        final String outerName,
+        final String innerName,
+        final int access)
+    {
+        cv.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    public FieldVisitor visitField(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        return cv.visitField(access, name, desc, signature, value);
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        int newAccess;
+        if ((access&ACC_SYNTHETIC&ACC_BRIDGE) == (ACC_SYNTHETIC&ACC_BRIDGE)) {
+            /*
+            bridge methods for generic create problems with RMIC code in 1.4.
+            It's a known bug with 1.4, see SUN's bug database at:
+                http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4811083
+                http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5035300
+
+            Problem found when implementing Comparable<E>, with bridge method
+                compareTo(Ljava/lang/Object;)I;
+            */
+            newAccess = access & ~ACC_SYNTHETIC & ~ACC_BRIDGE;
+        } else
+            newAccess = access;
+
+        MethodVisitor mv = new MethodWeaver(super.visitMethod(newAccess,
+                    name,
+                    desc,
+                    signature,
+                    exceptions));
+    	
+    	if (!isEnum || !name.equals("<clinit>"))
+    		return mv;
+
+    	return new EnumMethodWeaver(mv);
+    }
+
+    public void visitEnd() {
+        if (isEnum) {
+        	cv.visitField(ACC_PRIVATE + ACC_STATIC + ACC_FINAL + ACC_SYNTHETIC,
+        			SERIAL_ID_FIELD,
+        			SERIAL_ID_SIGNATURE,
+        			null, new Long(0L));
+        }
+        if (classLiteralCalls.size() != 0) {
+    		// generate synthetic fields and class$ method
+    		for(String fieldName: classLiteralCalls) {
+    			FieldVisitor fv = visitField(ACC_STATIC + ACC_SYNTHETIC + ACC_FINAL
+    					+ (isInterface?ACC_PUBLIC:ACC_PRIVATE),
+	    					fieldName,
+	    					CLASS_FIELD_DESC,
+	    					null, null);
+    			fv.visitEnd();
+    		}
+
+    		if (!isInterface) {
+	    		// "class$" method
+	    		String exceptionTable[] = { JAVA_LANG_NOCLASSDEFFOUNDERROR };
+	    		MethodVisitor mv = cv.visitMethod(ACC_STATIC+ACC_SYNTHETIC,
+								    				CLASS_METHOD,
+								    				CLASS_SIGNATURE,
+								    				null, exceptionTable);
+	
+	    		mv.visitCode();
+
+	            mv.visitVarInsn(ALOAD, 0);
+	            generateClassCall(mv);
+	            mv.visitInsn(ARETURN);
+	    
+	            mv.visitMaxs(0, 0);
+	            mv.visitEnd();
+    		}
+    	}
+        cv.visitEnd();
+    }
+
+    /**
+     * Generate the byte code equivalent to ".class"
+     * 
+     * Note: assumes the class name is already on the stack
+     * 
+     * @param mv method visitor to use
+     */
+    private void generateClassCall(MethodVisitor mv) {
+    	/* 
+    	 * generate the code equivalent to ".class"
+    	 * 
+
+    	 	try {
+    	 		c = Class.forName(name);
+			} catch (ClassNotFoundException e) {
+				NoClassDefFoundError t = new NoClassDefFoundError(e.getMessage());
+				try {
+					t.initCause(e); // only works with 1.4+
+				} catch (NoSuchMethodError nsm) {
+				}
+				throw t;
+			}
+    	 */
+    	Label start1 = new Label();
+    	Label end1 = new Label();
+    	Label handler1 = new Label();
+    	mv.visitTryCatchBlock(start1, end1, handler1, JAVA_LANG_CLASSNOTFOUNDEXCEPTION);
+    	Label start2 = new Label();
+    	Label end2 = new Label();
+    	Label handler2 = new Label();
+    	mv.visitTryCatchBlock(start2, end2, handler2, JAVA_LANG_NOSUCHMETHODERROR);
+
+    	mv.visitLabel(start1);
+    	mv.visitMethodInsn(INVOKESTATIC, JAVA_LANG_CLASS, FOR_NAME_METHOD, FOR_NAME_SIGNATURE);
+    	mv.visitVarInsn(ASTORE, 1);
+    	mv.visitLabel(end1);
+    	Label gotoLabel1 = new Label();
+    	mv.visitJumpInsn(GOTO, gotoLabel1);
+
+    	mv.visitLabel(handler1);
+    	mv.visitVarInsn(ASTORE, 2);
+    	mv.visitTypeInsn(NEW, JAVA_LANG_NOCLASSDEFFOUNDERROR);
+    	mv.visitInsn(DUP);
+    	mv.visitVarInsn(ALOAD, 2);
+    	mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_CLASSNOTFOUNDEXCEPTION, GET_MESSAGE_METHOD, GET_MESSAGE_SIGNATURE);
+    	mv.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_NOCLASSDEFFOUNDERROR, INIT_METHOD, JAVA_LANG_NOCLASSDEFFOUNDERROR_INIT_SIGNATURE);
+    	mv.visitVarInsn(ASTORE, 3);
+
+    	mv.visitLabel(start2);
+    	mv.visitVarInsn(ALOAD, 3);
+    	mv.visitVarInsn(ALOAD, 2);
+    	mv.visitMethodInsn(INVOKEVIRTUAL, JAVA_LANG_NOCLASSDEFFOUNDERROR, INIT_CAUSE_METHOD, INIT_CAUSE_SIGNATURE);
+    	mv.visitInsn(POP);
+    	mv.visitLabel(end2);
+    	Label gotoLabel2 = new Label();
+    	mv.visitJumpInsn(GOTO, gotoLabel2);
+
+    	mv.visitLabel(handler2);
+    	mv.visitVarInsn(ASTORE, 4);
+
+    	mv.visitLabel(gotoLabel2);
+    	mv.visitVarInsn(ALOAD, 3);
+    	mv.visitInsn(ATHROW);
+    	mv.visitLabel(gotoLabel1);
+    	mv.visitVarInsn(ALOAD, 1);
+    }
+
+    private class EnumMethodWeaver extends MethodAdapter implements Opcodes {
+    	public EnumMethodWeaver(final MethodVisitor mv) {
+    		super(mv);
+    	}
+
+        public void visitInsn(final int opcode) {
+        	if (opcode == RETURN) {
+            	// add call to setEnumValues(Object[] values, Class c)
+
+            	String owner = className.replace('.', '/');
+            	String fullName = 'L' + owner + ';';
+            	Type t = Type.getType(fullName);
+
+            	mv.visitMethodInsn(INVOKESTATIC, owner, "values", "()[" + fullName);
+            	mv.visitLdcInsn(t);
+            	mv.visitMethodInsn(INVOKESTATIC, "com/rc/retroweaver/runtime/Enum_",
+            			"setEnumValues", "([Ljava/lang/Object;Ljava/lang/Class;)V");     		
+        	}
+            mv.visitInsn(opcode);
+        }
+
+    }
+
+    private static final String JAVA_LANG_CLASS = "java/lang/Class";
+    private static final String FOR_NAME_METHOD = "forName";
+    private static final String FOR_NAME_SIGNATURE = "(Ljava/lang/String;)Ljava/lang/Class;";
+
+    private static final String SERIAL_ID_FIELD = "serialVersionUID";
+    private static final String SERIAL_ID_SIGNATURE = "J";
+
+    private static final String JAVA_LANG_NOCLASSDEFFOUNDERROR = "java/lang/NoClassDefFoundError";
+    private static final String INIT_METHOD = "<init>";
+    private static final String JAVA_LANG_NOCLASSDEFFOUNDERROR_INIT_SIGNATURE = "(Ljava/lang/String;)V";
+    private static final String INIT_CAUSE_METHOD = "initCause";
+    private static final String INIT_CAUSE_SIGNATURE = "(Ljava/lang/Throwable;)Ljava/lang/Throwable;";
+    
+    private static final String JAVA_LANG_CLASSNOTFOUNDEXCEPTION = "java/lang/ClassNotFoundException";
+    private static final String GET_MESSAGE_METHOD = "getMessage"; 	 
+    private static final String GET_MESSAGE_SIGNATURE = "()Ljava/lang/String;";
+
+    private static final String JAVA_LANG_NOSUCHMETHODERROR = "java/lang/NoSuchMethodError";
+
+	private static final String CLASS_FIELD_DESC = "Ljava/lang/Class;";
+	private static final String CLASS_METHOD = "class$";
+	private static final String CLASS_SIGNATURE = "(Ljava/lang/String;)Ljava/lang/Class;";
+
+	private static final String ITERABLE_CLASS = "java/lang/Iterable";
+	private static final String ITERATOR_METHOD = "iterator";
+	private static final String ITERATOR_SIGNATURE = "()Ljava/util/Iterator;";
+	private static final String ITERABLE_METHODS_CLASS = "com/rc/retroweaver/runtime/IterableMethods";
+	private static final String ITERABLE_METHODS_ITERATOR_SIGNATURE = "(Ljava/lang/Object;)Ljava/util/Iterator;";
+
+	private static final String JAVA_LANG_SYSTEM = "java/lang/System";
+	private static final String NANO_TIME_METHOD = "nanoTime";
+	private static final String NANO_TIME_SIGNATURE = "()J";
+	private static final String CURRENT_TIME_MILLIS_METHOD = "currentTimeMillis";
+	private static final String CURRENT_TIME_MILLIS_TIME_SIGNATURE = "()J";
+	
+	private static final String CLASS_METHODS_CLASS = "com/rc/retroweaver/runtime/ClassMethods";
+	private static final String CAST_METHOD = "cast";
+	private static final String CAST_SIGNATURE = "(Ljava/lang/Object;)Ljava/lang/Object;";
+	private static final String CAST_NEW_SIGNATURE = "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/Object;";
+	private static final String AS_SUB_CLASS_METHOD = "asSubclass";
+	private static final String AS_SUB_CLASS_SIGNATURE = "(Ljava/lang/Class;)Ljava/lang/Class;";
+	private static final String AS_SUB_CLASS_NEW_SIGNATURE = "(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Class;";
+	private static final String IS_ENUM_METHOD = "isEnum";
+	private static final String IS_ENUM_SIGNATURE = "()Z";
+	private static final String IS_ENUM_NEW_SIGNATURE = "(Ljava/lang/Class;)Z";
+	private static final String GET_ENUM_CONSTANTS_METHOD = "getEnumConstants";
+	private static final String GET_ENUM_CONSTANTS_SIGNATURE = "()[Ljava/lang/Object;";
+	private static final String GET_ENUM_CONSTANTS_NEW_SIGNATURE = "(Ljava/lang/Class;)[Ljava/lang/Object;";
+	
+	private static final String JAVA_LANG_STRINGBUFFER = "java/lang/StringBuffer";
+	private static final String JAVA_LANG_STRINGBUILDER = "java/lang/StringBuilder";
+    private static final String JAVA_LANG_STRINGBUFFER_INIT_SIGNATURE = "(Ljava/lang/CharSequence;)V";
+    private static final String JAVA_LANG_STRINGBUFFER_INIT_STRING_SIGNATURE = "(Ljava/lang/String;)V";
+	private static final String APPEND_METHOD = "append";
+	private static final String APPEND_SIGNATURE1 = "(Ljava/lang/CharSequence;)Ljava/lang/StringBuffer;";
+	private static final String APPEND_SIGNATURE2 = "(Ljava/lang/CharSequence;II)Ljava/lang/StringBuffer;";
+	private static final String APPEND_SIGNATURE3 = "(Ljava/lang/String;)Ljava/lang/StringBuffer;";
+	private static final String INSERT_METHOD = "insert";
+	private static final String INSERT_SIGNATURE1 = "(ILjava/lang/CharSequence;)Ljava/lang/StringBuffer;";
+	private static final String INSERT_SIGNATURE2 = "(ILjava/lang/CharSequence;II)Ljava/lang/StringBuffer;";
+	private static final String INSERT_SIGNATURE3 = "(ILjava/lang/String;)Ljava/lang/StringBuffer;";
+	private static final String TRIM_TO_SIZE = "trimToSize";
+	private static final String TRIM_TO_SIZE_SIGNATURE = "()V";
+
+	private static final String CHAR_SEQUENCE_CLASS = "java/lang/CharSequence";
+	private static final String TO_STRING_METHOD = "toString";
+	private static final String TO_STRING_SIGNATURE = "()Ljava/lang/String;";
+
+	private static final String SUB_SEQUENCE_METHOD = "subSequence";
+	private static final String SUB_SEQUENCE_SIGNATURE = "(II)Ljava/lang/CharSequence;";
+	
+	private static final String AUTOBOX_CLASS = "com/rc/retroweaver/runtime/Autobox";
+	private static final String VALUE_OF_METHOD = "valueOf";
+
+	private static final String JAVA_MATH_BIGDECIMAL_CLASS = "java/math/BigDecimal";
+	private static final String INT_CONSTRUCTOR_SIGNATURE = "(I)V";
+	private static final String LONG_CONSTRUCTOR_SIGNATURE = "(J)V";
+	private static final String DOUBLE_CONSTRUCTOR_SIGNATURE = "(D)V";
+	private static final String STRING_CONSTRUCTOR_SIGNATURE = "(Ljava/lang/String;)V";
+	private static final String TO_STRING_LONG_SIGNATURE = "(J)Ljava/lang/String;";
+	private static final String JAVA_LANG_LONG = "java/lang/Long";
+
+	private static final String JAVA_UTIL_COLLECTIONS = "java/util/Collections";
+	private static final String EMPTY_LIST_METHOD = "emptyList";
+	private static final String EMPTY_LIST_SIGNATURE = "()Ljava/util/List;";
+	private static final String EMPTY_LIST_FIELD = "EMPTY_LIST";
+	private static final String EMPTY_LIST_FIELD_DESC = "Ljava/util/List;";
+	private static final String EMPTY_MAP_METHOD = "emptyMap";
+	private static final String EMPTY_MAP_SIGNATURE = "()Ljava/util/Map;";
+	private static final String EMPTY_MAP_FIELD = "EMPTY_MAP";
+	private static final String EMPTY_MAP_FIELD_DESC = "Ljava/util/Map;";
+	private static final String EMPTY_SET_METHOD = "emptySet";
+	private static final String EMPTY_SET_SIGNATURE = "()Ljava/util/Set;";
+	private static final String EMPTY_SET_FIELD = "EMPTY_SET";
+	private static final String EMPTY_SET_FIELD_DESC = "Ljava/util/Set;";
+
+	private static Map<String, String> boxSignatures = new HashMap<String, String>();
+	static {
+		boxSignatures.put("java/lang/Boolean", "(Z)Ljava/lang/Boolean;");
+		boxSignatures.put("java/lang/Byte", "(B)Ljava/lang/Byte;");
+		boxSignatures.put("java/lang/Character", "(C)Ljava/lang/Character;");
+		boxSignatures.put("java/lang/Short", "(S)Ljava/lang/Short;");
+		boxSignatures.put("java/lang/Integer", "(I)Ljava/lang/Integer;");
+		boxSignatures.put("java/lang/Long", "(J)Ljava/lang/Long;");
+		boxSignatures.put("java/lang/Float", "(F)Ljava/lang/Float;");
+		boxSignatures.put("java/lang/Double", "(D)Ljava/lang/Double;");
+	}
+
+
+	class MethodWeaver extends MethodAdapter implements Opcodes {
+		
+	public MethodWeaver(final MethodVisitor mv) {
+		super(mv);
+	}
+
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        mv.visitMaxs(maxStack, maxLocals);
+    }
+
+    public void visitMethodInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+    	// ClassMethods substitutions
+    	if ((opcode == INVOKEVIRTUAL || opcode == INVOKESPECIAL) && owner.equals(JAVA_LANG_CLASS)) {
+			// Change calls to "cast" or "asSubclass" to an INVOKESTATIC
+    		// targeting one of our runtime methods  				
+			if (name.equals(CAST_METHOD) && desc.equals(CAST_SIGNATURE)) {
+				super.visitMethodInsn(INVOKESTATIC,
+						CLASS_METHODS_CLASS,
+						name,
+						CAST_NEW_SIGNATURE);
+				return;
+			} else if (name.equals(AS_SUB_CLASS_METHOD) && desc.equals(AS_SUB_CLASS_SIGNATURE)) {
+				super.visitMethodInsn(INVOKESTATIC,
+						CLASS_METHODS_CLASS,
+						name,
+						AS_SUB_CLASS_NEW_SIGNATURE);
+				return;
+			} else if (name.equals(IS_ENUM_METHOD) && desc.equals(IS_ENUM_SIGNATURE)) {
+				super.visitMethodInsn(INVOKESTATIC,
+						CLASS_METHODS_CLASS,
+						name,
+						IS_ENUM_NEW_SIGNATURE);
+				return;
+			} else if (name.equals(GET_ENUM_CONSTANTS_METHOD) && desc.equals(GET_ENUM_CONSTANTS_SIGNATURE)) {
+				super.visitMethodInsn(INVOKESTATIC,
+						CLASS_METHODS_CLASS,
+						name,
+						GET_ENUM_CONSTANTS_NEW_SIGNATURE);
+				return;
+			}
+    	} else if (owner.equals(JAVA_MATH_BIGDECIMAL_CLASS)&&name.equals(INIT_METHOD)) {
+    		if (desc.equals(INT_CONSTRUCTOR_SIGNATURE)) {
+    			// replace int constructor with double one after convertion i2d, no loss of precision
+    			super.visitInsn(I2D);
+    			super.visitMethodInsn(INVOKESPECIAL,
+    					JAVA_MATH_BIGDECIMAL_CLASS,
+    					INIT_METHOD,
+    					DOUBLE_CONSTRUCTOR_SIGNATURE);
+    			return;
+    		} else if (desc.equals(LONG_CONSTRUCTOR_SIGNATURE)) {
+    			// longs cannot be converted to double, use toString() instead
+    			super.visitMethodInsn(INVOKESTATIC,
+    					JAVA_LANG_LONG,
+    					TO_STRING_METHOD,
+    					TO_STRING_LONG_SIGNATURE);
+    			super.visitMethodInsn(INVOKESPECIAL,
+    					JAVA_MATH_BIGDECIMAL_CLASS,
+    					INIT_METHOD,
+    					STRING_CONSTRUCTOR_SIGNATURE);
+    			return;
+    		}
+    	} else if (opcode == INVOKEINTERFACE &&
+    				owner.equals(ITERABLE_CLASS) &&
+    				name.equals(ITERATOR_METHOD) &&
+    				desc.equals(ITERATOR_SIGNATURE)) {
+    		super.visitMethodInsn(INVOKESTATIC,
+    				ITERABLE_METHODS_CLASS,
+    				ITERATOR_METHOD,
+    				ITERABLE_METHODS_ITERATOR_SIGNATURE);
+    		return;
+    	} else if (opcode == INVOKESTATIC &&
+    				owner.equals(JAVA_LANG_SYSTEM)&&
+    				name.equals(NANO_TIME_METHOD)&&
+    				desc.equals(NANO_TIME_SIGNATURE)) {
+    		// call to nanoTime() is replaced by currentTimeMillis()*1000000L
+    		super.visitMethodInsn(INVOKESTATIC,
+    				JAVA_LANG_SYSTEM,
+    				CURRENT_TIME_MILLIS_METHOD,
+    				CURRENT_TIME_MILLIS_TIME_SIGNATURE);
+    		super.visitLdcInsn(new Long(1000000L));
+    		super.visitInsn(LMUL);
+    		return;
+    	} else if (owner.equals(JAVA_LANG_STRINGBUFFER)||owner.equals(JAVA_LANG_STRINGBUILDER)) {
+    		if ((opcode == INVOKESPECIAL) && name.equals(INIT_METHOD) && desc.equals(JAVA_LANG_STRINGBUFFER_INIT_SIGNATURE)) {
+        		// StringBuffer(CharSequence)
+    			// add toString() before arg and call StringBuffer(String)
+    			super.visitMethodInsn(INVOKEINTERFACE,
+    					CHAR_SEQUENCE_CLASS,
+    					TO_STRING_METHOD,
+    					TO_STRING_SIGNATURE);
+    			super.visitMethodInsn(INVOKESPECIAL,
+    					JAVA_LANG_STRINGBUFFER,
+    					INIT_METHOD,
+    					JAVA_LANG_STRINGBUFFER_INIT_STRING_SIGNATURE);
+    			return;
+    		} else if (opcode == INVOKEVIRTUAL) {
+    			if (name.equals(TRIM_TO_SIZE) && desc.equals(TRIM_TO_SIZE_SIGNATURE)) {
+    				// do nothing: call is removed as from the 1.5 javadoc,
+    				// there is no garantee the buffer capacity will be reduced to
+    				// fit the actual size
+    				super.visitInsn(POP);
+    				return;
+    			} else if (name.equals(APPEND_METHOD)) {
+    				String d = desc.replaceAll("StringBuilder", "StringBuffer");
+    				if (d.equals(APPEND_SIGNATURE1)) {
+    	    			super.visitMethodInsn(INVOKEINTERFACE,
+    	    					CHAR_SEQUENCE_CLASS,
+    	    					TO_STRING_METHOD,
+    	    					TO_STRING_SIGNATURE);
+    	    			super.visitMethodInsn(INVOKEVIRTUAL,
+    	    					JAVA_LANG_STRINGBUFFER,
+    	    					APPEND_METHOD,
+    	    					APPEND_SIGNATURE3);
+    	    			return;
+    				} else if (d.equals(APPEND_SIGNATURE2)) {
+    	    			super.visitMethodInsn(INVOKEINTERFACE,
+    	    					CHAR_SEQUENCE_CLASS,
+    	    					SUB_SEQUENCE_METHOD,
+    	    					SUB_SEQUENCE_SIGNATURE);
+    	    			super.visitMethodInsn(INVOKEINTERFACE,
+    	    					CHAR_SEQUENCE_CLASS,
+    	    					TO_STRING_METHOD,
+    	    					TO_STRING_SIGNATURE);
+    	    			super.visitMethodInsn(INVOKEVIRTUAL,
+    	    					JAVA_LANG_STRINGBUFFER,
+    	    					APPEND_METHOD,
+    	    					APPEND_SIGNATURE3);
+    	    			return;    					
+    				}
+    			} else if (name.equals(INSERT_METHOD)) {
+    				String d = desc.replaceAll("StringBuilder", "StringBuffer");
+    				if (d.equals(INSERT_SIGNATURE1)) {
+    	    			super.visitMethodInsn(INVOKEINTERFACE,
+    	    					CHAR_SEQUENCE_CLASS,
+    	    					TO_STRING_METHOD,
+    	    					TO_STRING_SIGNATURE);
+    	    			super.visitMethodInsn(INVOKEVIRTUAL,
+    	    					JAVA_LANG_STRINGBUFFER,
+    	    					INSERT_METHOD,
+    	    					INSERT_SIGNATURE3);
+    	    			return;
+    				} else if (d.equals(INSERT_SIGNATURE2)) {
+    	    			super.visitMethodInsn(INVOKEINTERFACE,
+    	    					CHAR_SEQUENCE_CLASS,
+    	    					SUB_SEQUENCE_METHOD,
+    	    					SUB_SEQUENCE_SIGNATURE);
+    	    			super.visitMethodInsn(INVOKEINTERFACE,
+    	    					CHAR_SEQUENCE_CLASS,
+    	    					TO_STRING_METHOD,
+    	    					TO_STRING_SIGNATURE);
+    	    			super.visitMethodInsn(INVOKEVIRTUAL,
+    	    					JAVA_LANG_STRINGBUFFER,
+    	    					INSERT_METHOD,
+    	    					INSERT_SIGNATURE3);
+    	    			return;    					
+
+    				}
+    			}
+    		}
+		} else if (opcode == INVOKESTATIC && owner.equals(JAVA_UTIL_COLLECTIONS)) {
+			if (name.equals(EMPTY_LIST_METHOD) && desc.equals(EMPTY_LIST_SIGNATURE)) {
+				super.visitFieldInsn(GETSTATIC, owner, EMPTY_LIST_FIELD, EMPTY_LIST_FIELD_DESC);
+				return;
+			} else if (name.equals(EMPTY_MAP_METHOD) && desc.equals(EMPTY_MAP_SIGNATURE)) {
+				super.visitFieldInsn(GETSTATIC, owner, EMPTY_MAP_FIELD, EMPTY_MAP_FIELD_DESC);
+				return;
+			} else if (name.equals(EMPTY_SET_METHOD) && desc.equals(EMPTY_SET_SIGNATURE)) {
+				super.visitFieldInsn(GETSTATIC, owner, EMPTY_SET_FIELD, EMPTY_SET_FIELD_DESC);
+				return;
+			}
+		}
+
+    	// Autoboxing
+		else if (opcode == INVOKESTATIC && name.equals(VALUE_OF_METHOD)) {
+    		String sig = boxSignatures.get(owner);		
+    		if (sig != null && sig.equals(desc)) {
+    			// owner and signature match
+    			mv.visitMethodInsn(INVOKESTATIC,
+    					AUTOBOX_CLASS,
+    					VALUE_OF_METHOD,
+    					boxSignatures.get(owner));
+
+    			return;
+    		}
+    	}
+
+    	// not a special case, use default implementation
+    	super.visitMethodInsn(opcode, owner, name, desc);
+	}
+
+
+    public void visitLdcInsn(final Object cst) {
+    	if (cst instanceof Type) {
+    		/**
+    		 * Fix class literals. The 1.5 VM has had its ldc* instructions updated so
+    		 * that it knows how to deal with CONSTANT_Class in addition to the other
+    		 * types. So, we have to search for uses of ldc* that point to a
+    		 * CONSTANT_Class and replace them with synthetic field access the way
+    		 * it was generated in 1.4.
+    		 */
+
+    		// LDC or LDC_W with a class as argument
+
+    		Type t = (Type) cst;
+    		String fieldName = getClassLiteralFieldName(t);
+
+    		classLiteralCalls.add(fieldName);
+
+    		mv.visitFieldInsn(GETSTATIC, className, fieldName, CLASS_FIELD_DESC);
+    		Label nonNullLabel = new Label();
+    		mv.visitJumpInsn(IFNONNULL, nonNullLabel);
+    		String s;
+    		if (t.getSort() == Type.OBJECT)
+    			s = t.getInternalName();
+    		else
+    			s = t.getDescriptor();
+    		
+    		/* convert retroweaver runtime classes:
+    		 * 		Enum into com.rc.retroweaver.runtime.Enum_
+    		 *		concurrent classes into their backport equivalent
+    		 *		...
+    		 */
+    		s = NameTranslator.translate(s);
+
+    		mv.visitLdcInsn(s.replace('/', '.'));
+    		if (isInterface) {
+    			/* synthethic methods cannot be generated in interfaces so the byte
+    			 * code has to be inlined for each call to ".class"
+    			 */
+    			generateClassCall(mv);
+    		} else {
+    			mv.visitMethodInsn(INVOKESTATIC, className, CLASS_METHOD, CLASS_SIGNATURE);
+    		}
+    		mv.visitInsn(DUP);
+    		mv.visitFieldInsn(PUTSTATIC, className, fieldName, CLASS_FIELD_DESC);
+    		Label endLabel = new Label();
+    		mv.visitJumpInsn(GOTO, endLabel);
+    		mv.visitLabel(nonNullLabel);
+    		mv.visitFieldInsn(GETSTATIC, className, fieldName, CLASS_FIELD_DESC);
+    		mv.visitLabel(endLabel);
+
+    	} else {
+    		super.visitLdcInsn(cst);
+    	}
+    }
+
+    private String getClassLiteralFieldName(Type type) {
+    	String fieldName;
+    	if (type.getSort() == Type.ARRAY) {
+    		fieldName = "array" + type.getDescriptor().replace('[', '$');
+    		if (fieldName.charAt(fieldName.length()-1) == ';')
+    			fieldName = fieldName.substring(0, fieldName.length()-1);
+    	} else {
+    		fieldName = "class$" + type.getInternalName();
+    	}
+    	fieldName = fieldName.replace('/', '$');
+
+    	return fieldName;
+    }
+
+}
+
+}
+
+class DefaultWeaveListener implements WeaveListener {
+
+	private boolean verbose;
+
+	DefaultWeaveListener(boolean verbose) {
+		this.verbose = verbose;
+	}
+
+	public void weavingStarted(String msg) {
+		System.out.println("[RetroWeaver] " + msg);
+	}
+
+	public void weavingCompleted(String msg) {
+		System.out.println("[RetroWeaver] " + msg);
+	}
+
+	public void weavingPath(String sourcePath) {
+		if (verbose)
+			System.out.println("[RetroWeaver] Weaving " + sourcePath);
+	}
+};
+

Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverClassLoader.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverClassLoader.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverClassLoader.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverClassLoader.java Thu Jul  6 15:08:30 2006
@@ -0,0 +1,245 @@
+package com.rc.retroweaver;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class RetroWeaverClassLoader extends ClassLoader {
+
+	private RetroWeaver retroWeaver;
+	
+	private List<ClassPathElement> classPathElements;
+
+	protected void setWeaver(RetroWeaver retroWeaver) {
+		this.retroWeaver = retroWeaver;
+	}
+
+	protected void setClassPath(List<String> classPath) {
+		classPathElements = new LinkedList<ClassPathElement>();
+
+		for(String pathEntry: classPath) {
+			File f = new File(pathEntry);
+			if (f.exists()) {
+				if (f.isDirectory())
+					addDirectoryClassPathElement(pathEntry);
+				else
+					addJarClassPathElement(pathEntry);
+			}
+		}
+	}
+
+	protected void setClassPath(String classPath) {
+		List<String> l = new LinkedList<String>();
+		
+		if (classPath != null) {
+			StringTokenizer t = new StringTokenizer(classPath, File.pathSeparator);
+			while (t.hasMoreTokens()) {
+				l.add(t.nextToken());
+			}
+		}
+
+		setClassPath(l);
+	}
+
+	protected void addDirectoryClassPathElement(String dirName) {
+		DirectoryElement e = new DirectoryElement(dirName);
+		classPathElements.add(e);
+	}
+
+	protected void addJarClassPathElement(String jarName) {
+		try {
+			JarElement e = new JarElement(jarName);
+			classPathElements.add(e);
+		} catch (IOException ioe) {
+		}
+	}
+
+	protected Class<?> findClass(String name) throws ClassNotFoundException {
+		String resourceName = name.replace('.', '/') + ".class";
+		for (ClassPathElement e : classPathElements) {
+			if (e.hasResource(resourceName)) {
+				ByteArrayOutputStream bos = new ByteArrayOutputStream();
+				InputStream is = e.getResourceStream(resourceName);
+
+				byte b[];
+				boolean weaved;
+				try {
+					weaved = retroWeaver.weave(is, name, bos);
+				} catch (IOException ioe) {
+					throw new RetroWeaverException("Problem weaving class " + name
+							+ ": " + ioe.getMessage());
+				}
+				if (weaved)
+					b = bos.toByteArray();
+				else
+					b = e.getResourceData(resourceName);
+
+				Class clazz = defineClass(name.replace('/', '.'), b, 0, b.length);
+
+				return clazz;
+			}
+		}
+
+		throw new ClassNotFoundException(name);
+	}
+
+	protected byte[] getClassData(String name) throws ClassNotFoundException {
+		String resourceName = name.replace('.', '/') + ".class";
+		for (ClassPathElement e : classPathElements) {
+			if (e.hasResource(resourceName)) {
+				byte b[] = e.getResourceData(resourceName);
+
+				return b;
+			}
+		}
+
+		throw new ClassNotFoundException(name);
+	}
+
+	protected URL findResource(String name) {
+		for (ClassPathElement e : classPathElements) {
+			if (e.hasResource(name))
+				return e.getResourceURL(name);
+		}
+		return null;
+	}
+
+	protected Enumeration<URL> findResources(String name) throws IOException {
+		ArrayList<URL> l = new ArrayList<URL>();
+		for (ClassPathElement e : classPathElements) {
+			if (e.hasResource(name))
+				l.add(e.getResourceURL(name));
+		}
+		return Collections.enumeration(l);
+	}
+
+}
+
+abstract class ClassPathElement {
+
+	protected abstract boolean hasResource(String name);
+
+	protected abstract URL getResourceURL(String name);
+
+	protected abstract InputStream getResourceStream(String name);
+
+	protected byte[] getResourceData(String name) {
+		assert (hasResource(name));
+
+		InputStream is = getResourceStream(name);
+		ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+		DataInputStream ds = new DataInputStream(is);
+
+		byte b[] = new byte[2048];
+		int i;
+		try {
+			while((i = ds.read(b)) != -1) {
+				bos.write(b, 0, i);
+			}
+			return bos.toByteArray();
+		} catch (IOException e) {
+			return null;
+		} finally {
+			try {
+				ds.close();
+			} catch (IOException e) {
+			}
+		}
+	}
+
+}
+
+class DirectoryElement extends ClassPathElement {
+
+	private String dirName;
+
+	DirectoryElement(String dirName) {
+		this.dirName = dirName;
+	}
+
+	protected boolean hasResource(String name) {
+		String fullPath = dirName + File.separatorChar + name;
+
+		File f = new File(fullPath);
+
+		return f.exists() && f.isFile();
+	}
+
+	protected URL getResourceURL(String name) {
+		assert (hasResource(name));
+
+		String fullPath = dirName + File.separatorChar + name;
+
+		try {
+			return new URL("file:" + fullPath);
+		} catch (MalformedURLException e) {
+		}
+		return null;
+	}
+
+	protected InputStream getResourceStream(String name) {
+		assert (hasResource(name));
+
+		try {
+			File f = new File(dirName + File.separatorChar + name);
+			return new FileInputStream(f);
+		} catch (IOException ioe) {
+		}
+		return null;
+	}
+
+}
+
+class JarElement extends ClassPathElement {
+
+	private String jarName;
+
+	private ZipFile jarFile;
+
+	JarElement(String jarName) throws IOException {
+		this.jarName = jarName;
+		jarFile = new ZipFile(jarName);
+	}
+
+	protected boolean hasResource(String name) {
+		ZipEntry entry = jarFile.getEntry(name);
+
+		return entry != null;
+	}
+
+	protected URL getResourceURL(String name) {
+		assert (hasResource(name));
+
+		try {
+			return new URL("jar:file:" + jarName + "!/" + name);
+		} catch (MalformedURLException e) {
+		}
+		return null;
+	}
+
+	protected InputStream getResourceStream(String name) {
+		assert (hasResource(name));
+
+		try {
+			ZipEntry entry = jarFile.getEntry(name);
+			return jarFile.getInputStream(entry);
+		} catch (IOException ioe) {
+		}
+		return null;
+	}
+
+}

Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverException.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverException.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverException.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/RetroWeaverException.java Thu Jul  6 15:08:30 2006
@@ -0,0 +1,17 @@
+package com.rc.retroweaver;
+
+public class RetroWeaverException extends RuntimeException {
+
+	public RetroWeaverException(String message) {
+		super(message);
+	}
+
+	public RetroWeaverException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+	public RetroWeaverException(Throwable cause) {
+		super(cause);
+	}
+
+}

Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/SignatureStripper.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/SignatureStripper.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/SignatureStripper.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/SignatureStripper.java Thu Jul  6 15:08:30 2006
@@ -0,0 +1,64 @@
+package com.rc.retroweaver;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+
+public class SignatureStripper extends ClassAdapter {
+
+	public SignatureStripper(ClassVisitor cv) {
+		super(cv);
+	}
+
+    public void visit(
+        final int version,
+        final int access,
+        final String name,
+        final String signature,
+        final String superName,
+        final String[] interfaces)
+    {
+        super.visit(version, access, name, null, superName, interfaces);
+    }
+
+    public FieldVisitor visitField(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        return super.visitField(access, name, desc, null, value);
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        return new MethodSignatureStripper(super.visitMethod(access, name, desc, null, exceptions));
+    }
+
+    static class MethodSignatureStripper extends MethodAdapter {
+    	MethodSignatureStripper(MethodVisitor mv) {
+    		super(mv);
+    	}
+        public void visitLocalVariable(
+                final String name,
+                final String desc,
+                final String signature,
+                final Label start,
+                final Label end,
+                final int index)
+            {
+                super.visitLocalVariable(name, desc, null, start, end, index);
+            }
+
+    }
+
+}

Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/WeaveRunner.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/WeaveRunner.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/WeaveRunner.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/WeaveRunner.java Thu Jul  6 15:08:30 2006
@@ -0,0 +1,160 @@
+package com.rc.retroweaver;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.util.jar.Attributes;
+
+public class WeaveRunner {
+
+	private RetroWeaver retroWeaver;
+
+	private RetroWeaverClassLoader classLoader;
+
+	public WeaveRunner(String classPath) {
+		// use the current JVM version as the target
+		String version = System.getProperty("java.version");
+		int target;
+		switch (version.charAt(2)) {
+		case '2':
+			target = Weaver.VERSION_1_2;
+			break;
+		case '3':
+			target = Weaver.VERSION_1_3;
+			break;
+		case '4':
+			target = Weaver.VERSION_1_4;
+			break;
+		case '5':
+			target = Weaver.VERSION_1_5;
+			break;
+		default:
+			throw new RetroWeaverException("Unsupported JVM version: " + version);
+		}
+		retroWeaver = new RetroWeaver(target);
+		retroWeaver.setLazy(true);
+		
+		classLoader = new RetroWeaverClassLoader();
+		classLoader.setClassPath(classPath);
+		classLoader.setWeaver(retroWeaver);
+	}
+
+	public void run(String className, String[] args)
+			throws ClassNotFoundException, NoSuchMethodException {
+		Class clazz = classLoader.loadClass(className);
+
+		Method m = clazz.getMethod("main", new Class[] { args.getClass() });
+		m.setAccessible(true);
+		int mods = m.getModifiers();
+		if (m.getReturnType() != void.class || !Modifier.isStatic(mods)
+				|| !Modifier.isPublic(mods)) {
+			throw new NoSuchMethodException("main");
+		}
+		try {
+			m.invoke(null, new Object[] { args });
+		} catch (IllegalAccessException e) {
+		} catch (InvocationTargetException ite) {
+			throw new RetroWeaverException(ite);
+		}
+	}
+
+	public void executeJar(String jarFileName, String[] args)
+			throws ClassNotFoundException, NoSuchMethodException {
+		// add jar to class path
+		classLoader.addJarClassPathElement(jarFileName);
+
+		// get class name from MANIFEST
+		String className = null;
+		try {
+			URL u = new URL("jar:file:" + jarFileName + "!/");
+			JarURLConnection uc = (JarURLConnection) u.openConnection();
+			Attributes attr = uc.getMainAttributes();
+
+			if (attr != null)
+				className = attr.getValue(Attributes.Name.MAIN_CLASS);
+		} catch (IOException ioe) {
+		}
+
+		if (className == null) {
+			System.err.println("No " + Attributes.Name.MAIN_CLASS
+					+ " specified in jar file " + jarFileName);
+		} else {
+			run(className, args);
+		}
+	}
+
+	public static void main(String[] args) throws ClassNotFoundException,
+			NoSuchMethodException {
+		String classPath = null;
+		String mainClass = null;
+		String jarFileName = null;
+
+		int argIndex = 0;
+		while (argIndex < args.length) {
+			String command = args[argIndex++];
+
+			if (command.equals("-cp") || command.equals("-classpath")) {
+				classPath = args[argIndex++];
+			} else if (command.equals("-jar")) {
+				jarFileName = args[argIndex++];
+				break;
+			} else {
+				mainClass = command;
+				break;
+			}
+		}
+		if (jarFileName == null) {
+			String errorMsg = null;
+
+			if (classPath == null)
+				errorMsg = "Missing class path";
+			if (mainClass == null)
+				errorMsg = "Missing main class or jar option";
+
+			if (errorMsg != null) {
+				System.out.println(errorMsg);
+				System.out.println();
+				usage();
+				return;
+			}
+		}
+
+		String[] realArgs = new String[args.length - argIndex];
+		System.arraycopy(args, argIndex, realArgs, 0, args.length - argIndex);
+
+		WeaveRunner runner = new WeaveRunner(classPath);
+
+		if (jarFileName != null)
+			runner.executeJar(jarFileName, realArgs);
+		else
+			runner.run(mainClass, realArgs);
+	}
+
+	private static final String nl = System.getProperty("line.separator");
+
+	private static void usage() {
+		String msg = "Usage: WeaveRunner [-options] class [args...]"
+				+ nl
+				+ "\t\t(to execute a class)"
+				+ nl
+				+ "\tor WeaveRunner [-options] -jar jarfile [args...]"
+				+ nl
+				+ "\t\t(to execute a jar file)"
+				+ nl
+				+ nl
+				+ "where options include:"
+				+ nl
+				+ "\t-cp <class search path of directories and zip/jar files>"
+				+ nl
+				+ "\t-classpath <class search path of directories and zip/jar files>"
+				+ nl + "\t\tA " + File.pathSeparatorChar
+				+ " separated list of directories, JAR archives," + nl
+				+ "\t\tand ZIP archives to search for class files." + nl;
+		System.out.println(msg);
+	}
+
+}

Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/Weaver.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/Weaver.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/Weaver.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/Weaver.java Thu Jul  6 15:08:30 2006
@@ -0,0 +1,168 @@
+package com.rc.retroweaver;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+
+import org.objectweb.asm.commons.EmptyVisitor;
+
+/**
+ * Applies the RetroWeaver against a set of classes.
+ *
+ */
+public class Weaver {
+
+	private static final String VERSION;
+
+	private static final String BUILD_NUMBER;
+
+	public static final String getVersion() {
+		return VERSION + " (build " + BUILD_NUMBER + ')';
+	}
+
+	static {
+		ResourceBundle bundle = ResourceBundle.getBundle("retroweaver");
+		VERSION = bundle.getString("retroweaver.version");
+		BUILD_NUMBER = bundle.getString("retroweaver.buildNumber");
+	}
+
+	// Read the new class file format spec for how the version is computed.
+	public static final int VERSION_1_5 = 49;
+
+	public static final int VERSION_1_4 = 48;
+
+	public static final int VERSION_1_3 = 47;
+
+	public static final int VERSION_1_2 = 46;
+
+	private static final String nl = System.getProperty("line.separator");
+
+	public static void main(String[] args) {
+
+		String source = null;
+		String sourceJar = null;
+		String destJar = null;
+		int target = VERSION_1_4;
+		int currentArg = 0;
+		boolean lazy = false;
+		boolean stripSignatures = false;
+		boolean verbose = false;
+		String verifyPath = null;
+
+		while (currentArg < args.length) {
+			String command = args[currentArg];
+			++currentArg;
+
+			if (command.equals("-source")) {
+				source = args[currentArg++];
+			} else if (command.equals("-jar")) {
+				sourceJar = args[currentArg++];
+				destJar = args[currentArg++];
+
+				if (sourceJar.equals(destJar)) {
+					System.out
+							.println("source and destination jar files can not be identical");
+					System.out.println();
+					System.exit(1);
+				}
+			} else if (command.equals("-version")) {
+				System.out.println("Retroweaver version " + getVersion());
+				System.exit(0);
+			} else if (command.equals("-target")) {
+				String verStr = args[currentArg++];
+				if (verStr.equals("1.4")) {
+					target = VERSION_1_4;
+				} else if (verStr.equals("1.3")) {
+					target = VERSION_1_3;
+				} else if (verStr.equals("1.2")) {
+					target = VERSION_1_2;
+				} else {
+					System.out.println("Invalid target version: " + verStr);
+					System.out.println();
+					System.out.println(getUsage());
+					System.exit(1);
+				}
+			} else if (command.equals("-lazy")) {
+				lazy = true;
+			} else if (command.equals("-stripSignatures")) {
+				stripSignatures = true;
+			} else if (command.equals("-verbose")) {
+				verbose = true;
+			} else if (command.equals("-verifyrefs")) {
+				verifyPath = args[currentArg++];
+			} else {
+				System.out
+						.println("I don't understand the command: " + command);
+				System.out.println();
+				System.out.println(getUsage());
+				System.exit(1);
+			}
+		}
+
+		if (source == null && sourceJar == null) {
+			System.out.println("Option \"-source\" or \"-jar\" is required.");
+			System.out.println();
+			System.out.println(getUsage());
+			System.exit(1);
+		}
+
+		if (source != null && sourceJar != null) {
+			System.out
+					.println("Only one of \"-source\" or \"-jar\" can be specified.");
+			System.out.println();
+			System.out.println(getUsage());
+			System.exit(1);
+		}
+
+		File sourcePath = null;
+
+		RetroWeaver weaver = new RetroWeaver(target);
+		weaver.setListener(new DefaultWeaveListener(verbose));
+		weaver.setLazy(lazy);
+		weaver.setStripSignatures(stripSignatures);
+
+		if (verifyPath != null) {
+			List<String> paths = new ArrayList<String>();
+			StringTokenizer st = new StringTokenizer(verifyPath,
+					File.pathSeparator);
+			while (st.hasMoreTokens()) {
+				paths.add(st.nextToken());
+			}
+			RefVerifier rv = new RefVerifier(target, new EmptyVisitor(), paths,
+					new DefaultRefVerifierListener(verbose));
+			weaver.setVerifier(rv);
+		}
+
+		try {
+			if (source != null) {
+				sourcePath = new File(source);
+
+				weaver.weave(sourcePath);
+			} else {
+				weaver.weaveJarFile(sourceJar, destJar);
+			}
+		} catch (Exception e) {
+			throw new RetroWeaverException("Weaving failed", e);
+		}
+	}
+
+	private static String getUsage() {
+		return "Usage: Weaver <options>"
+				+ nl
+				+ " Options: "
+				+ nl
+				+ " -source <source dir>"
+				+ nl
+				+ " -jar <source jar> <target jar>"
+				+ nl
+				+ " -target <target VM version> (one of {1.4, 1.3, 1.2}, default is 1.4)"
+				+ nl + " -verifyrefs <classpath>" + nl
+				+ " -stripSignatures (strip generic signatures, off by default)" + nl
+				+ " -verbose (message for each processed class)" + nl
+				+ " -version (display version number and exit)" + nl + nl
+				+ "One of \"-source\" or \"-jar\" is required.";
+	}
+
+}

Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/ant/RetroWeaverTask.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/ant/RetroWeaverTask.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/ant/RetroWeaverTask.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/ant/RetroWeaverTask.java Thu Jul  6 15:08:30 2006
@@ -0,0 +1,402 @@
+package com.rc.retroweaver.ant;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.ExitStatusException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+import com.rc.retroweaver.RefVerifier;
+import com.rc.retroweaver.RetroWeaver;
+import com.rc.retroweaver.event.VerifierListener;
+import com.rc.retroweaver.event.WeaveListener;
+
+/**
+ * An Ant task for running RetroWeaver on a set of class files.
+ */
+public class RetroWeaverTask extends Task {
+
+	////////////////////////////////////////////////////////////////////////////////
+	//	Constants and variables.
+
+	/**
+	 * The destination directory for processd classes, or <code>null</code> for in place
+	 * processing.
+	 */
+	private File itsDestDir;
+
+	/**
+	 * Indicates if an error should cause the script to fail. Default to <code>true</code>.
+	 */
+	private boolean itsFailOnError = true;
+
+	/**
+	 * The set of files to be weaved.
+	 */
+	private List<FileSet> itsFileSets = new ArrayList<FileSet>();
+
+	private List<DirSet> itsDirSets = new ArrayList<DirSet>();
+
+	private String inputJar;
+
+	private String outputJar;
+
+	/**
+	 * Indicates if classes should only be processed if their current version differ from the target version. Initially <code>true</code>.
+	 */
+	private boolean itsLazy = true;
+
+	/**
+	 * Indicates whether the generic signatures should be stripped. Default to <code>false</code>.
+	 */
+	private boolean stripSignatures;
+
+	/**
+	 * Indicates if each processed class should be logged. Initially set to <code>false</code>.
+	 */
+	private boolean itsVerbose = false;
+
+	/**
+	 * The classpath to use to verify the weaved result
+	 */
+	private Path verifyClasspath;
+
+	private boolean verify = true;
+
+	/**
+	 * The class file version number.
+	 */
+	private int itsVersion = 48;
+
+	/**
+	 * The class file version number.
+	 */
+	private static final Map<String, Integer> itsVersionMap = new HashMap<String, Integer>();
+
+	/**
+	 * Initialize the version map.
+	 */
+	static {
+		itsVersionMap.put("1.2", 46);
+		itsVersionMap.put("1.3", 47);
+		itsVersionMap.put("1.4", 48);
+		itsVersionMap.put("1.5", 49);
+	}
+
+	////////////////////////////////////////////////////////////////////////////////
+	//	Property accessors and mutators.
+
+	/**
+	 * Set the destination directory for processed classes. Unless specified the classes
+	 * are processed in place.
+	 * @param pDir The destination directory. 
+	 */
+	public void setDestDir(File pDir) {
+		if (!pDir.isDirectory())
+			throw new BuildException(
+					"The destination directory doesn't exist: " + pDir,
+					getLocation());
+
+		itsDestDir = pDir;
+	}
+
+	/**
+	 * Specify if an error should cause the script to fail. Default to <code>true</code>.
+	 *
+	 * @param pFailOnError <code>true</code> to fail, <code>false</code> to keep going.
+	 */
+	public void setFailOnError(boolean pFailOnError) {
+		itsFailOnError = pFailOnError;
+	}
+
+	/**
+	 * Add a set of files to be weaved.
+	 * @param pSet The fileset.
+	 */
+	public void addFileSet(FileSet pFileSet) {
+		itsFileSets.add(pFileSet);
+	}
+
+	public void addDirSet(DirSet pFileSet) {
+		itsDirSets.add(pFileSet);
+	}
+
+	/**
+	 * Specify if classes should only be processed if their current version differ from the target version. Initially <code>true</code>.
+	 * @param pLazy <code>true</code> for lazy processing.
+	 */
+	public void setLazy(boolean pLazy) {
+		itsLazy = pLazy;
+	}
+
+	/**
+	 * Set the source directory containing classes to process. This is a shortcut to
+	 * using an embedded fileset with the specified base directory and which includes
+	 * all class files.
+	 * @param pDir The directory. 
+	 */
+	public void setSrcDir(File pDir) {
+		FileSet fileSet = new FileSet();
+		fileSet.setDir(pDir);
+		fileSet.setIncludes("**/*.class");
+
+		addFileSet(fileSet);
+	}
+
+	/**
+	 * Specify if each processed class should be logged. Initially set to <code>false</code>.
+	 * @param pVerbose <code>true</code> for verbose processing.
+	 */
+	public void setVerbose(boolean pVerbose) {
+		itsVerbose = pVerbose;
+	}
+
+	/**
+	 * Set the target class file version. Initially set to "1.4".
+	 * @param target The JDK target version, e&nbsp;g "1.3". 
+	 */
+	public void setTarget(String target) {
+		Integer v = itsVersionMap.get(target);
+		if (v == null)
+			throw new BuildException("Unknown target: " + target, getLocation());
+		itsVersion = v;
+	}
+
+	/**
+	 * Set the classpath to be used for verification.
+	 * Retroweaver will report any references to fields/methods/classes which don't appear
+	 * on refClassPath.
+	 * @param classpath an Ant Path object containing the compilation classpath.
+	 */
+	public void setClasspath(Path classpath) {
+		if (verifyClasspath == null) {
+			verifyClasspath = classpath;
+		} else {
+			verifyClasspath.append(classpath);
+		}
+	}
+
+	/**
+	 * Gets the classpath to be used for verification.
+	 * @return the class path
+	 public Path getClasspath() {
+	 return verifyClasspath;
+	 }
+
+	 /**
+	 * Adds a path to the classpath.
+	 * @return a class path to be configured
+	 */
+	public Path createClasspath() {
+		if (verifyClasspath == null) {
+			verifyClasspath = new Path(getProject());
+		}
+		return verifyClasspath.createPath();
+	}
+
+	/**
+	 * Adds a reference to a classpath defined elsewhere.
+	 * @param r a reference to a classpath
+	 */
+	public void setClasspathRef(Reference r) {
+		createClasspath().setRefid(r);
+	}
+
+	/**
+	 * Turn off verification if desired
+	 * @return is verification enabled?
+	 */
+	public void setVerify(boolean newVerify) {
+		verify = newVerify;
+	}
+
+	/**
+	 * Turn off verification if desired
+	 * @return is verification enabled?
+	 */
+	public boolean doVerify() {
+		return verify;
+	}
+
+	////////////////////////////////////////////////////////////////////////////////
+	//	Operations.
+
+	/**
+	 * Run the RetroWeaver task.
+	 * @throws BuildException If a build exception occurs.
+	 */
+	public void execute() throws BuildException {
+
+		for (DirSet set : itsDirSets) {
+			File baseDir = set.getDir(getProject());
+			DirectoryScanner scanner = set.getDirectoryScanner(getProject());
+
+			// create a non recursive file set for each included directory
+			for (String fileName : scanner.getIncludedDirectories()) {
+				FileSet fileSet = new FileSet();
+				fileSet.setDir(new File(baseDir, fileName));
+				fileSet.setIncludes("*.class");
+				addFileSet(fileSet);
+			}
+		}
+
+		//	Check arguments.
+
+		boolean hasFileSet = itsFileSets.size() != 0 || itsDirSets.size() != 0;
+
+		if (inputJar != null) {
+			if (outputJar == null)
+				throw new BuildException("'outputjar' must be set.");
+
+			if (hasFileSet)
+				throw new BuildException(
+						"'inputjar' is incompatible with filesets and dirsets");
+		} else if (!hasFileSet)
+			throw new BuildException(
+					"Either attribute 'srcdir' or 'inputjar' must be used or atleast one fileset or dirset must be embedded.",
+					getLocation());
+
+		//	Create and configure the weaver.
+
+		RetroWeaver weaver = new RetroWeaver(itsVersion);
+		weaver.setLazy(itsLazy);
+		weaver.setStripSignatures(stripSignatures);
+
+		//	Set up a listener.
+		weaver.setListener(new WeaveListener() {
+			public void weavingStarted(String msg) {
+				getProject().log(RetroWeaverTask.this, msg, Project.MSG_INFO);
+			}
+
+			public void weavingCompleted(String msg) {
+				getProject().log(RetroWeaverTask.this, msg, Project.MSG_INFO);
+			}
+
+			public void weavingPath(String pPath) {
+				if (itsVerbose)
+					getProject().log(RetroWeaverTask.this, "Weaving " + pPath,
+							Project.MSG_INFO);
+			}
+		});
+
+		if (verifyClasspath != null && doVerify()) {
+
+			List<String> refPath = new ArrayList<String>();
+
+			for (String pathItem : verifyClasspath.list()) {
+				refPath.add(pathItem);
+			}
+			if (itsDestDir != null) {
+				refPath.add(itsDestDir.getPath());
+			}
+
+			RefVerifier rv = new RefVerifier(itsVersion, new EmptyVisitor(), refPath, new VerifierListener() {
+				public void verifyPathStarted(String msg) {
+					getProject().log(RetroWeaverTask.this, msg,
+							Project.MSG_INFO);
+				}
+
+				public void verifyClassStarted(String msg) {
+					if (itsVerbose)
+						getProject().log(RetroWeaverTask.this, msg,
+								Project.MSG_INFO);
+				}
+
+				public void acceptWarning(String msg) {
+					getProject().log(RetroWeaverTask.this, msg,
+							Project.MSG_WARN);
+				}
+
+				public void displaySummary(int warningCount) {
+					String msg = "Verification complete, " + warningCount
+							+ " warning(s).";
+					getProject().log(RetroWeaverTask.this, msg,
+							Project.MSG_WARN);
+
+					if (itsFailOnError)
+						throw new ExitStatusException(Integer
+								.toString(warningCount)
+								+ " warning(s)", 1);
+				}
+			});
+			weaver.setVerifier(rv);
+		}
+
+		try {
+			if (inputJar != null) {
+				weaver.weaveJarFile(inputJar, outputJar);
+			} else {
+				//	Weave the files in the filesets.
+
+				//	Process each fileset.
+				String[][] fileSets = new String[itsFileSets.size()][];
+				File[] baseDirs = new File[itsFileSets.size()];
+				int i = 0;
+				for (FileSet fileSet : itsFileSets) {
+					//	Create a directory scanner for the fileset.
+					File baseDir = fileSet.getDir(getProject());
+					DirectoryScanner scanner = fileSet
+							.getDirectoryScanner(getProject());
+					fileSets[i] = scanner.getIncludedFiles();
+					baseDirs[i++] = baseDir;
+				}
+
+				weaver.weave(baseDirs, fileSets, itsDestDir);
+			}
+		}
+		catch (BuildException ex) {
+			throw ex;
+		}
+		catch (Exception ex) {
+			// unexpected exception
+			ex.printStackTrace();
+			throw new BuildException(ex, getLocation());
+		}
+	}
+
+	/**
+	 * @return Returns the inputJar.
+	 */
+	public String getInputJar() {
+		return inputJar;
+	}
+
+	/**
+	 * @param inputJar The inputJar to set.
+	 */
+	public void setInputJar(String inputJar) {
+		this.inputJar = inputJar;
+	}
+
+	/**
+	 * @return Returns the outputJar.
+	 */
+	public String getOutputJar() {
+		return outputJar;
+	}
+
+	/**
+	 * @param outputJar The outputJar to set.
+	 */
+	public void setOutputJar(String outputJar) {
+		this.outputJar = outputJar;
+	}
+
+	/**
+	 * @param stripSignatures The stripSignatures to set.
+	 */
+	public void setStripSignatures(boolean stripSignatures) {
+		this.stripSignatures = stripSignatures;
+	}
+}

Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/VerifierListener.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/VerifierListener.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/VerifierListener.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/VerifierListener.java Thu Jul  6 15:08:30 2006
@@ -0,0 +1,13 @@
+package com.rc.retroweaver.event;
+
+public interface VerifierListener {
+
+	void verifyPathStarted(String msg);
+
+	void verifyClassStarted(String msg);
+
+	void acceptWarning(String msg);
+
+	void displaySummary(int warningCount);
+
+}

Added: incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/WeaveListener.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/WeaveListener.java?rev=419720&view=auto
==============================================================================
--- incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/WeaveListener.java (added)
+++ incubator/abdera/java/trunk/build/tools/retroweaver/src/com/rc/retroweaver/event/WeaveListener.java Thu Jul  6 15:08:30 2006
@@ -0,0 +1,13 @@
+package com.rc.retroweaver.event;
+
+/**
+ * A callback interface to indicate weaving status.
+ */
+public interface WeaveListener {
+	void weavingStarted(String msg);
+
+	void weavingPath(String sourcePath);
+
+	void weavingCompleted(String msg);
+
+}