You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by tc...@apache.org on 2012/04/10 23:37:21 UTC

svn commit: r1312005 - in /commons/proper/bcel/trunk/src: main/java/org/apache/bcel/classfile/ test/java/org/apache/bcel/ test/java/org/apache/bcel/data/

Author: tcurdt
Date: Tue Apr 10 21:37:21 2012
New Revision: 1312005

URL: http://svn.apache.org/viewvc?rev=1312005&view=rev
Log:
applied patch from Charles Honton, https://issues.apache.org/bugzilla/show_bug.cgi?id=52979

Modified:
    commons/proper/bcel/trunk/src/main/java/org/apache/bcel/classfile/Method.java
    commons/proper/bcel/trunk/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java
    commons/proper/bcel/trunk/src/test/java/org/apache/bcel/GeneratingAnnotatedClassesTestCase.java
    commons/proper/bcel/trunk/src/test/java/org/apache/bcel/data/AnnotatedWithCombinedAnnotation.java

Modified: commons/proper/bcel/trunk/src/main/java/org/apache/bcel/classfile/Method.java
URL: http://svn.apache.org/viewvc/commons/proper/bcel/trunk/src/main/java/org/apache/bcel/classfile/Method.java?rev=1312005&r1=1312004&r2=1312005&view=diff
==============================================================================
--- commons/proper/bcel/trunk/src/main/java/org/apache/bcel/classfile/Method.java (original)
+++ commons/proper/bcel/trunk/src/main/java/org/apache/bcel/classfile/Method.java Tue Apr 10 21:37:21 2012
@@ -50,6 +50,8 @@ public final class Method extends FieldO
         }
     };
 
+    // annotations defined on the parameters of a method
+    private ParameterAnnotationEntry[] parameterAnnotationEntries;
 
     /**
      * Empty constructor, all attributes have to be defined via `setXXX'
@@ -256,4 +258,14 @@ public final class Method extends FieldO
     public int hashCode() {
         return _cmp.hashCode(this);
     }
+
+    /**
+     * @return Annotations on the parameters of a method
+     */
+    public ParameterAnnotationEntry[] getParameterAnnotationEntries() {
+        if (parameterAnnotationEntries == null) {
+            parameterAnnotationEntries = ParameterAnnotationEntry.createParameterAnnotationEntries(getAttributes());
+        }
+        return parameterAnnotationEntries;
+    }
 }

Modified: commons/proper/bcel/trunk/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java
URL: http://svn.apache.org/viewvc/commons/proper/bcel/trunk/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java?rev=1312005&r1=1312004&r2=1312005&view=diff
==============================================================================
--- commons/proper/bcel/trunk/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java (original)
+++ commons/proper/bcel/trunk/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java Tue Apr 10 21:37:21 2012
@@ -20,11 +20,15 @@ package org.apache.bcel.classfile;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 import org.apache.bcel.Constants;
 
 /**
  * represents one parameter annotation in the parameter annotation table
- * 
+ *
  * @version $Id: ParameterAnnotationEntry
  * @author  <A HREF="mailto:dbrosius@qis.net">D. Brosius</A>
  * @since 5.3
@@ -44,7 +48,7 @@ public class ParameterAnnotationEntry im
         annotation_table_length = (file.readUnsignedShort());
         annotation_table = new AnnotationEntry[annotation_table_length];
         for (int i = 0; i < annotation_table_length; i++) {
-//        	 TODO isRuntimeVisible
+            // TODO isRuntimeVisible
             annotation_table[i] = AnnotationEntry.read(file, constant_pool, false);
         }
     }
@@ -58,7 +62,7 @@ public class ParameterAnnotationEntry im
      * @param v Visitor object
      */
     public void accept( Visitor v ) {
-        //	    v.visitParameterAnnotationEntry(this);
+        // v.visitParameterAnnotationEntry(this);
     }
 
 
@@ -76,12 +80,24 @@ public class ParameterAnnotationEntry im
     public AnnotationEntry[] getAnnotationEntries() {
         return annotation_table;
     }
-    
+
     public void dump(DataOutputStream dos) throws IOException {
         dos.writeShort(annotation_table_length);
         for(int i = 0; i < annotation_table_length; i++) {
             annotation_table[i].dump(dos);
         }
     }
+
+  public static ParameterAnnotationEntry[] createParameterAnnotationEntries(Attribute[] attrs) {
+      // Find attributes that contain parameter annotation data
+      List<ParameterAnnotationEntry> accumulatedAnnotations = new ArrayList<ParameterAnnotationEntry>(attrs.length);
+      for (Attribute attribute : attrs) {
+          if (attribute instanceof ParameterAnnotations) {
+              ParameterAnnotations runtimeAnnotations = (ParameterAnnotations)attribute;
+              Collections.addAll(accumulatedAnnotations, runtimeAnnotations.getParameterAnnotationEntries());
+          }
+      }
+      return accumulatedAnnotations.toArray(new ParameterAnnotationEntry[accumulatedAnnotations.size()]);
+  }
 }
-    
+

Modified: commons/proper/bcel/trunk/src/test/java/org/apache/bcel/GeneratingAnnotatedClassesTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/bcel/trunk/src/test/java/org/apache/bcel/GeneratingAnnotatedClassesTestCase.java?rev=1312005&r1=1312004&r2=1312005&view=diff
==============================================================================
--- commons/proper/bcel/trunk/src/test/java/org/apache/bcel/GeneratingAnnotatedClassesTestCase.java (original)
+++ commons/proper/bcel/trunk/src/test/java/org/apache/bcel/GeneratingAnnotatedClassesTestCase.java Tue Apr 10 21:37:21 2012
@@ -13,7 +13,7 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- * 
+ *
  */
 package org.apache.bcel;
 
@@ -21,11 +21,15 @@ import java.io.File;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+
 import org.apache.bcel.classfile.AnnotationEntry;
+import org.apache.bcel.classfile.ArrayElementValue;
 import org.apache.bcel.classfile.ElementValue;
 import org.apache.bcel.classfile.ElementValuePair;
 import org.apache.bcel.classfile.JavaClass;
 import org.apache.bcel.classfile.Method;
+import org.apache.bcel.classfile.ParameterAnnotationEntry;
+import org.apache.bcel.classfile.SimpleElementValue;
 import org.apache.bcel.generic.ALOAD;
 import org.apache.bcel.generic.ASTORE;
 import org.apache.bcel.generic.AnnotationElementValueGen;
@@ -51,575 +55,647 @@ import org.apache.bcel.util.SyntheticRep
 
 /**
  * The program that some of the tests generate looks like this:
- * 
+ *
  * <pre>
  * public class HelloWorld
  * {
- * 	public static void main(String[] argv)
- * 	{
- * 		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
- * 		String name = null;
- * 
- * 		try
- * 		{
- * 			name = &quot;Andy&quot;;
- * 		}
- * 		catch (IOException e)
- * 		{
- * 			return;
- * 		}
- * 		System.out.println(&quot;Hello, &quot; + name);
- * 	}
+ *  public static void main(String[] argv)
+ *  {
+ *      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+ *      String name = null;
+ *
+ *      try
+ *      {
+ *          name = &quot;Andy&quot;;
+ *      }
+ *      catch (IOException e)
+ *      {
+ *          return;
+ *      }
+ *      System.out.println(&quot;Hello, &quot; + name);
+ *  }
  * }
  * </pre>
  */
 public class GeneratingAnnotatedClassesTestCase extends AbstractTestCase
 {
-	/**
-	 * Steps in the test:
-	 * <ol>
-	 * <li>Programmatically construct the HelloWorld program</li>
-	 * <li>Add two simple annotations at the class level</li>
-	 * <li>Save the class to disk</li>
-	 * <li>Reload the class using the 'static' variant of the BCEL classes</li>
-	 * <li>Check the attributes are OK</li>
-	 * </ol>
-	 */
-	public void testGenerateClassLevelAnnotations()
-			throws ClassNotFoundException
-	{
-		// Create HelloWorld
-		ClassGen cg = createClassGen("HelloWorld");
-		cg.setMajor(49);
-		cg.setMinor(0);
-		ConstantPoolGen cp = cg.getConstantPool();
-		InstructionList il = new InstructionList();
-		cg.addAnnotationEntry(createSimpleVisibleAnnotation(cp));
-		cg.addAnnotationEntry(createSimpleInvisibleAnnotation(cp));
-		buildClassContents(cg, cp, il);
-		//System.out.println(cg.getJavaClass().toString());
-		dumpClass(cg, "HelloWorld.class");
-		JavaClass jc = getClassFrom(".", "HelloWorld");
-		AnnotationEntry[] as = jc.getAnnotationEntries();
-		assertTrue("Should be two AnnotationEntries but found " + as.length,
-				as.length == 2);
-		AnnotationEntry one = as[0];
-		AnnotationEntry two = as[1];
-		// TODO L??;
-		assertTrue(
-				"Name of annotation 1 should be LSimpleAnnotation; but it is "
-						+ as[0].getAnnotationType(), as[0].getAnnotationType()
-						.equals("LSimpleAnnotation;"));
-		assertTrue(
-				"Name of annotation 2 should be LSimpleAnnotation; but it is "
-						+ as[1].getAnnotationType(), as[1].getAnnotationType()
-						.equals("LSimpleAnnotation;"));
-		ElementValuePair[] vals = as[0].getElementValuePairs();
-		ElementValuePair nvp = vals[0];
-		assertTrue(
-				"Name of element in SimpleAnnotation should be 'id' but it is "
-						+ nvp.getNameString(), nvp.getNameString().equals("id"));
-		ElementValue ev = nvp.getValue();
-		assertTrue("Type of element value should be int but it is "
-				+ ev.getElementValueType(),
-				ev.getElementValueType() == ElementValue.PRIMITIVE_INT);
-		assertTrue("Value of element should be 4 but it is "
-				+ ev.stringifyValue(), ev.stringifyValue().equals("4"));
-		assertTrue(createTestdataFile("HelloWorld.class").delete());
-	}
-
-	/**
-	 * Just check that we can dump a class that has a method annotation on it
-	 * and it is still there when we read it back in
-	 */
-	public void testGenerateMethodLevelAnnotations1()
-			throws ClassNotFoundException
-	{
-		// Create HelloWorld
-		ClassGen cg = createClassGen("HelloWorld");
-		ConstantPoolGen cp = cg.getConstantPool();
-		InstructionList il = new InstructionList();
-		buildClassContentsWithAnnotatedMethods(cg, cp, il);
-		// Check annotation is OK
-		int i = cg.getMethods()[0].getAnnotationEntries().length;
-		assertTrue(
-				"Prior to dumping, main method should have 1 annotation but has "
-						+ i, i == 1);
-		dumpClass(cg, "temp1" + File.separator + "HelloWorld.class");
-		JavaClass jc2 = getClassFrom("temp1", "HelloWorld");
-		// Check annotation is OK
-		i = jc2.getMethods()[0].getAnnotationEntries().length;
-		assertTrue("JavaClass should say 1 annotation on main method but says "
-				+ i, i == 1);
-		ClassGen cg2 = new ClassGen(jc2);
-		// Check it now it is a ClassGen
-		Method[] m = cg2.getMethods();
-		i = m[0].getAnnotationEntries().length;
-		assertTrue("The main 'Method' should have one annotation but has " + i,
-				i == 1);
-		MethodGen mg = new MethodGen(m[0], cg2.getClassName(), cg2
-				.getConstantPool());
-		// Check it finally when the Method is changed to a MethodGen
-		i = mg.getAnnotationEntries().length;
-		assertTrue("The main 'MethodGen' should have one annotation but has "
-				+ i, i == 1);
-		assertTrue(wipe("temp1" + File.separator + "HelloWorld.class"));
-	}
-
-	/**
-	 * Going further than the last test - when we reload the method back in,
-	 * let's change it (adding a new annotation) and then store that, read it
-	 * back in and verify both annotations are there !
-	 */
-	public void testGenerateMethodLevelAnnotations2()
-			throws ClassNotFoundException
-	{
-		// Create HelloWorld
-		ClassGen cg = createClassGen("HelloWorld");
-		ConstantPoolGen cp = cg.getConstantPool();
-		InstructionList il = new InstructionList();
-		buildClassContentsWithAnnotatedMethods(cg, cp, il);
-		dumpClass(cg, "temp2", "HelloWorld.class");
-		JavaClass jc2 = getClassFrom("temp2", "HelloWorld");
-		ClassGen cg2 = new ClassGen(jc2);
-		// Main method after reading the class back in
-		Method mainMethod1 = jc2.getMethods()[0];
-		assertTrue("The 'Method' should have one annotations but has "
-				+ mainMethod1.getAnnotationEntries().length, mainMethod1
-				.getAnnotationEntries().length == 1);
-		MethodGen mainMethod2 = new MethodGen(mainMethod1, cg2.getClassName(),
-				cg2.getConstantPool());
-		assertTrue("The 'MethodGen' should have one annotations but has "
-				+ mainMethod2.getAnnotationEntries().length, mainMethod2
-				.getAnnotationEntries().length == 1);
-		mainMethod2.addAnnotationEntry(createFruitAnnotation(cg2
-				.getConstantPool(), "Pear"));
-		cg2.removeMethod(mainMethod1);
-		cg2.addMethod(mainMethod2.getMethod());
-		dumpClass(cg2, "temp3", "HelloWorld.class");
-		JavaClass jc3 = getClassFrom("temp3", "HelloWorld");
-		ClassGen cg3 = new ClassGen(jc3);
-		Method mainMethod3 = cg3.getMethods()[1];
-		int i = mainMethod3.getAnnotationEntries().length;
-		assertTrue("The 'Method' should now have two annotations but has " + i,
-				i == 2);
-		assertTrue(wipe("temp2", "HelloWorld.class"));
-		assertTrue(wipe("temp3", "HelloWorld.class"));
-	}
-
-	// J5TODO: Need to add deleteFile calls to many of these tests
-	/**
-	 * Transform simple class from an immutable to a mutable object.
-	 */
-	public void testTransformClassToClassGen_SimpleTypes()
-			throws ClassNotFoundException
-	{
-		JavaClass jc = getTestClass("org.apache.bcel.data.SimpleAnnotatedClass");
-		ClassGen cgen = new ClassGen(jc);
-		// Check annotations are correctly preserved
-		AnnotationEntryGen[] annotations = cgen.getAnnotationEntries();
-		assertTrue("Expected one annotation but found " + annotations.length,
-				annotations.length == 1);
-	}
-
-	/**
-	 * Transform simple class from an immutable to a mutable object. The class
-	 * is annotated with an annotation that uses an enum.
-	 */
-	public void testTransformClassToClassGen_EnumType()
-			throws ClassNotFoundException
-	{
-		JavaClass jc = getTestClass("org.apache.bcel.data.AnnotatedWithEnumClass");
-		ClassGen cgen = new ClassGen(jc);
-		// Check annotations are correctly preserved
-		AnnotationEntryGen[] annotations = cgen.getAnnotationEntries();
-		assertTrue("Expected one annotation but found " + annotations.length,
-				annotations.length == 1);
-	}
-
-	/**
-	 * Transform simple class from an immutable to a mutable object. The class
-	 * is annotated with an annotation that uses an array of SimpleAnnotations.
-	 */
-	public void testTransformClassToClassGen_ArrayAndAnnotationTypes()
-			throws ClassNotFoundException
-	{
-		JavaClass jc = getTestClass("org.apache.bcel.data.AnnotatedWithCombinedAnnotation");
-		ClassGen cgen = new ClassGen(jc);
-		// Check annotations are correctly preserved
-		AnnotationEntryGen[] annotations = cgen.getAnnotationEntries();
-		assertTrue("Expected one annotation but found " + annotations.length,
-				annotations.length == 1);
-		AnnotationEntryGen a = annotations[0];
-		assertTrue("That annotation should only have one value but has "
-				+ a.getValues().size(), a.getValues().size() == 1);
-		ElementValuePairGen nvp = a.getValues().get(0);
-		ElementValueGen value = nvp.getValue();
-		assertTrue("Value should be ArrayElementValueGen but is " + value,
-				value instanceof ArrayElementValueGen);
-		ArrayElementValueGen arrayValue = (ArrayElementValueGen) value;
-		assertTrue("Array value should be size one but is "
-				+ arrayValue.getElementValuesSize(), arrayValue
-				.getElementValuesSize() == 1);
-		ElementValueGen innerValue = arrayValue.getElementValues().get(0);
-		assertTrue(
-				"Value in the array should be AnnotationElementValueGen but is "
-						+ innerValue,
-				innerValue instanceof AnnotationElementValueGen);
-		AnnotationElementValueGen innerAnnotationValue = (AnnotationElementValueGen) innerValue;
-		assertTrue("Should be called Lorg/apache/bcel/data/SimpleAnnotation; but is called: "
-				+ innerAnnotationValue.getAnnotation().getTypeName(),
-				innerAnnotationValue.getAnnotation().getTypeSignature().equals(
-						"Lorg/apache/bcel/data/SimpleAnnotation;"));
-	}
-
-	/**
-	 * Transform complex class from an immutable to a mutable object.
-	 */
-	public void testTransformComplexClassToClassGen()
-			throws ClassNotFoundException
-	{
-		JavaClass jc = getTestClass("org.apache.bcel.data.ComplexAnnotatedClass");
-		ClassGen cgen = new ClassGen(jc);
-		// Check annotations are correctly preserved
-		AnnotationEntryGen[] annotations = cgen.getAnnotationEntries();
-		assertTrue("Expected one annotation but found " + annotations.length,
-				annotations.length == 1);
-		List<?> l = annotations[0].getValues();
-		boolean found = false;
-		for (Iterator<?> iter = l.iterator(); iter.hasNext();)
-		{
-			ElementValuePairGen element = (ElementValuePairGen) iter.next();
-			if (element.getNameString().equals("dval"))
-			{
-				if (((SimpleElementValueGen) element.getValue())
-						.stringifyValue().equals("33.4"))
-					found = true;
-			}
-		}
-		assertTrue("Did not find double annotation value with value 33.4",
-				found);
-	}
-
-	/**
-	 * Load a class in and modify it with a new attribute - A SimpleAnnotation
-	 * annotation
-	 */
-	public void testModifyingClasses1() throws ClassNotFoundException
-	{
-		JavaClass jc = getTestClass("org.apache.bcel.data.SimpleAnnotatedClass");
-		ClassGen cgen = new ClassGen(jc);
-		ConstantPoolGen cp = cgen.getConstantPool();
-		cgen.addAnnotationEntry(createFruitAnnotation(cp, "Pineapple"));
-		assertTrue("Should now have two annotations but has "
-				+ cgen.getAnnotationEntries().length, cgen
-				.getAnnotationEntries().length == 2);
-		dumpClass(cgen, "SimpleAnnotatedClass.class");
-		assertTrue(wipe("SimpleAnnotatedClass.class"));
-	}
-
-	/**
-	 * Load a class in and modify it with a new attribute - A ComplexAnnotation
-	 * annotation
-	 */
-	public void testModifyingClasses2() throws ClassNotFoundException
-	{
-		JavaClass jc = getTestClass("org.apache.bcel.data.SimpleAnnotatedClass");
-		ClassGen cgen = new ClassGen(jc);
-		ConstantPoolGen cp = cgen.getConstantPool();
-		cgen.addAnnotationEntry(createCombinedAnnotation(cp));
-		assertTrue("Should now have two annotations but has "
-				+ cgen.getAnnotationEntries().length, cgen
-				.getAnnotationEntries().length == 2);
-		dumpClass(cgen, "SimpleAnnotatedClass.class");
-		JavaClass jc2 = getClassFrom(".", "SimpleAnnotatedClass");
-		jc2.getAnnotationEntries();
-		assertTrue(wipe("SimpleAnnotatedClass.class"));
-		// System.err.println(jc2.toString());
-	}
-
-	private void dumpClass(ClassGen cg, String fname)
-	{
-		try
-		{
-			File f = createTestdataFile(fname);
-			cg.getJavaClass().dump(f);
-		}
-		catch (java.io.IOException e)
-		{
-			System.err.println(e);
-		}
-	}
-
-	private void dumpClass(ClassGen cg, String dir, String fname)
-	{
-		dumpClass(cg, dir + File.separator + fname);
-	}
-
-	private void buildClassContentsWithAnnotatedMethods(ClassGen cg,
-			ConstantPoolGen cp, InstructionList il)
-	{
-		// Create method 'public static void main(String[]argv)'
-		MethodGen mg = createMethodGen("main", il, cp);
-		InstructionFactory factory = new InstructionFactory(cg);
-		mg.addAnnotationEntry(createSimpleVisibleAnnotation(mg
-				.getConstantPool()));
-		// We now define some often used types:
-		ObjectType i_stream = new ObjectType("java.io.InputStream");
-		ObjectType p_stream = new ObjectType("java.io.PrintStream");
-		// Create variables in and name : We call the constructors, i.e.,
-		// execute BufferedReader(InputStreamReader(System.in)) . The reference
-		// to the BufferedReader object stays on top of the stack and is stored
-		// in the newly allocated in variable.
-		il.append(factory.createNew("java.io.BufferedReader"));
-		il.append(InstructionConstants.DUP); // Use predefined constant
-		il.append(factory.createNew("java.io.InputStreamReader"));
-		il.append(InstructionConstants.DUP);
-		il.append(factory.createFieldAccess("java.lang.System", "in", i_stream,
-				Constants.GETSTATIC));
-		il.append(factory.createInvoke("java.io.InputStreamReader", "<init>",
-				Type.VOID, new Type[] { i_stream }, Constants.INVOKESPECIAL));
-		il.append(factory.createInvoke("java.io.BufferedReader", "<init>",
-				Type.VOID, new Type[] { new ObjectType("java.io.Reader") },
-				Constants.INVOKESPECIAL));
-		LocalVariableGen lg = mg.addLocalVariable("in", new ObjectType(
-				"java.io.BufferedReader"), null, null);
-		int in = lg.getIndex();
-		lg.setStart(il.append(new ASTORE(in))); // "in" valid from here
-		// Create local variable name and initialize it to null
-		lg = mg.addLocalVariable("name", Type.STRING, null, null);
-		int name = lg.getIndex();
-		il.append(InstructionConstants.ACONST_NULL);
-		lg.setStart(il.append(new ASTORE(name))); // "name" valid from here
-		// Create try-catch block: We remember the start of the block, read a
-		// line from the standard input and store it into the variable name .
-		// InstructionHandle try_start = il.append(factory.createFieldAccess(
-		// "java.lang.System", "out", p_stream, Constants.GETSTATIC));
-		// il.append(new PUSH(cp, "Please enter your name> "));
-		// il.append(factory.createInvoke("java.io.PrintStream", "print",
-		// Type.VOID, new Type[] { Type.STRING },
-		// Constants.INVOKEVIRTUAL));
-		// il.append(new ALOAD(in));
-		// il.append(factory.createInvoke("java.io.BufferedReader", "readLine",
-		// Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
-		InstructionHandle try_start = il.append(new PUSH(cp, "Andy"));
-		il.append(new ASTORE(name));
-		// Upon normal execution we jump behind exception handler, the target
-		// address is not known yet.
-		GOTO g = new GOTO(null);
-		InstructionHandle try_end = il.append(g);
-		// We add the exception handler which simply returns from the method.
-		LocalVariableGen var_ex = mg.addLocalVariable("ex", Type
-				.getType("Ljava.io.IOException;"), null, null);
-		int var_ex_slot = var_ex.getIndex();
-		InstructionHandle handler = il.append(new ASTORE(var_ex_slot));
-		var_ex.setStart(handler);
-		var_ex.setEnd(il.append(InstructionConstants.RETURN));
-		mg.addExceptionHandler(try_start, try_end, handler, new ObjectType(
-				"java.io.IOException"));
-		// "Normal" code continues, now we can set the branch target of the GOTO
-		// .
-		InstructionHandle ih = il.append(factory.createFieldAccess(
-				"java.lang.System", "out", p_stream, Constants.GETSTATIC));
-		g.setTarget(ih);
-		// Printing "Hello": String concatenation compiles to StringBuffer
-		// operations.
-		il.append(factory.createNew(Type.STRINGBUFFER));
-		il.append(InstructionConstants.DUP);
-		il.append(new PUSH(cp, "Hello, "));
-		il
-				.append(factory.createInvoke("java.lang.StringBuffer",
-						"<init>", Type.VOID, new Type[] { Type.STRING },
-						Constants.INVOKESPECIAL));
-		il.append(new ALOAD(name));
-		il.append(factory.createInvoke("java.lang.StringBuffer", "append",
-				Type.STRINGBUFFER, new Type[] { Type.STRING },
-				Constants.INVOKEVIRTUAL));
-		il.append(factory.createInvoke("java.lang.StringBuffer", "toString",
-				Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
-		il
-				.append(factory.createInvoke("java.io.PrintStream", "println",
-						Type.VOID, new Type[] { Type.STRING },
-						Constants.INVOKEVIRTUAL));
-		il.append(InstructionConstants.RETURN);
-		// Finalization: Finally, we have to set the stack size, which normally
-		// would have to be computed on the fly and add a default constructor
-		// method to the class, which is empty in this case.
-		mg.setMaxStack();
-		mg.setMaxLocals();
-		cg.addMethod(mg.getMethod());
-		il.dispose(); // Allow instruction handles to be reused
-		cg.addEmptyConstructor(Constants.ACC_PUBLIC);
-	}
-
-	private void buildClassContents(ClassGen cg, ConstantPoolGen cp,
-			InstructionList il)
-	{
-		// Create method 'public static void main(String[]argv)'
-		MethodGen mg = createMethodGen("main", il, cp);
-		InstructionFactory factory = new InstructionFactory(cg);
-		// We now define some often used types:
-		ObjectType i_stream = new ObjectType("java.io.InputStream");
-		ObjectType p_stream = new ObjectType("java.io.PrintStream");
-		// Create variables in and name : We call the constructors, i.e.,
-		// execute BufferedReader(InputStreamReader(System.in)) . The reference
-		// to the BufferedReader object stays on top of the stack and is stored
-		// in the newly allocated in variable.
-		il.append(factory.createNew("java.io.BufferedReader"));
-		il.append(InstructionConstants.DUP); // Use predefined constant
-		il.append(factory.createNew("java.io.InputStreamReader"));
-		il.append(InstructionConstants.DUP);
-		il.append(factory.createFieldAccess("java.lang.System", "in", i_stream,
-				Constants.GETSTATIC));
-		il.append(factory.createInvoke("java.io.InputStreamReader", "<init>",
-				Type.VOID, new Type[] { i_stream }, Constants.INVOKESPECIAL));
-		il.append(factory.createInvoke("java.io.BufferedReader", "<init>",
-				Type.VOID, new Type[] { new ObjectType("java.io.Reader") },
-				Constants.INVOKESPECIAL));
-		LocalVariableGen lg = mg.addLocalVariable("in", new ObjectType(
-				"java.io.BufferedReader"), null, null);
-		int in = lg.getIndex();
-		lg.setStart(il.append(new ASTORE(in))); // "in" valid from here
-		// Create local variable name and initialize it to null
-		lg = mg.addLocalVariable("name", Type.STRING, null, null);
-		int name = lg.getIndex();
-		il.append(InstructionConstants.ACONST_NULL);
-		lg.setStart(il.append(new ASTORE(name))); // "name" valid from here
-		// Create try-catch block: We remember the start of the block, read a
-		// line from the standard input and store it into the variable name .
-		// InstructionHandle try_start = il.append(factory.createFieldAccess(
-		// "java.lang.System", "out", p_stream, Constants.GETSTATIC));
-		// il.append(new PUSH(cp, "Please enter your name> "));
-		// il.append(factory.createInvoke("java.io.PrintStream", "print",
-		// Type.VOID, new Type[] { Type.STRING },
-		// Constants.INVOKEVIRTUAL));
-		// il.append(new ALOAD(in));
-		// il.append(factory.createInvoke("java.io.BufferedReader", "readLine",
-		// Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
-		InstructionHandle try_start = il.append(new PUSH(cp, "Andy"));
-		il.append(new ASTORE(name));
-		// Upon normal execution we jump behind exception handler, the target
-		// address is not known yet.
-		GOTO g = new GOTO(null);
-		InstructionHandle try_end = il.append(g);
-		// We add the exception handler which simply returns from the method.
-		LocalVariableGen var_ex = mg.addLocalVariable("ex", Type
-				.getType("Ljava.io.IOException;"), null, null);
-		int var_ex_slot = var_ex.getIndex();
-		InstructionHandle handler = il.append(new ASTORE(var_ex_slot));
-		var_ex.setStart(handler);
-		var_ex.setEnd(il.append(InstructionConstants.RETURN));
-		mg.addExceptionHandler(try_start, try_end, handler, new ObjectType(
-				"java.io.IOException"));
-		// "Normal" code continues, now we can set the branch target of the GOTO
-		// .
-		InstructionHandle ih = il.append(factory.createFieldAccess(
-				"java.lang.System", "out", p_stream, Constants.GETSTATIC));
-		g.setTarget(ih);
-		// Printing "Hello": String concatenation compiles to StringBuffer
-		// operations.
-		il.append(factory.createNew(Type.STRINGBUFFER));
-		il.append(InstructionConstants.DUP);
-		il.append(new PUSH(cp, "Hello, "));
-		il
-				.append(factory.createInvoke("java.lang.StringBuffer",
-						"<init>", Type.VOID, new Type[] { Type.STRING },
-						Constants.INVOKESPECIAL));
-		il.append(new ALOAD(name));
-		il.append(factory.createInvoke("java.lang.StringBuffer", "append",
-				Type.STRINGBUFFER, new Type[] { Type.STRING },
-				Constants.INVOKEVIRTUAL));
-		il.append(factory.createInvoke("java.lang.StringBuffer", "toString",
-				Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
-		il
-				.append(factory.createInvoke("java.io.PrintStream", "println",
-						Type.VOID, new Type[] { Type.STRING },
-						Constants.INVOKEVIRTUAL));
-		il.append(InstructionConstants.RETURN);
-		// Finalization: Finally, we have to set the stack size, which normally
-		// would have to be computed on the fly and add a default constructor
-		// method to the class, which is empty in this case.
-		mg.setMaxStack();
-		mg.setMaxLocals();
-		cg.addMethod(mg.getMethod());
-		il.dispose(); // Allow instruction handles to be reused
-		cg.addEmptyConstructor(Constants.ACC_PUBLIC);
-	}
-
-	private JavaClass getClassFrom(String where, String clazzname)
-			throws ClassNotFoundException
-	{
-		// System.out.println(where);
-		SyntheticRepository repos = createRepos(where);
-		return repos.loadClass(clazzname);
-	}
-
-	// helper methods
-	private ClassGen createClassGen(String classname)
-	{
-		return new ClassGen(classname, "java.lang.Object", "<generated>",
-				Constants.ACC_PUBLIC | Constants.ACC_SUPER, null);
-	}
-
-	private MethodGen createMethodGen(String methodname, InstructionList il,
-			ConstantPoolGen cp)
-	{
-		return new MethodGen(Constants.ACC_STATIC | Constants.ACC_PUBLIC, // access
-				// flags
-				Type.VOID, // return type
-				new Type[] { new ArrayType(Type.STRING, 1) }, // argument
-				// types
-				new String[] { "argv" }, // arg names
-				methodname, "HelloWorld", // method, class
-				il, cp);
-	}
-
-	public AnnotationEntryGen createSimpleVisibleAnnotation(ConstantPoolGen cp)
-	{
-		SimpleElementValueGen evg = new SimpleElementValueGen(
-				ElementValueGen.PRIMITIVE_INT, cp, 4);
-		ElementValuePairGen nvGen = new ElementValuePairGen("id", evg, cp);
-		ObjectType t = new ObjectType("SimpleAnnotation");
-		List<ElementValuePairGen> elements = new ArrayList<ElementValuePairGen>();
-		elements.add(nvGen);
-		AnnotationEntryGen a = new AnnotationEntryGen(t, elements, true, cp);
-		return a;
-	}
-
-	public AnnotationEntryGen createFruitAnnotation(ConstantPoolGen cp,
-			String aFruit)
-	{
-		SimpleElementValueGen evg = new SimpleElementValueGen(
-				ElementValueGen.STRING, cp, aFruit);
-		ElementValuePairGen nvGen = new ElementValuePairGen("fruit", evg, cp);
-		ObjectType t = new ObjectType("SimpleStringAnnotation");
-		List<ElementValuePairGen> elements = new ArrayList<ElementValuePairGen>();
-		elements.add(nvGen);
-		return new AnnotationEntryGen(t, elements, true, cp);
-	}
-
-	public AnnotationEntryGen createCombinedAnnotation(ConstantPoolGen cp)
-	{
-		// Create an annotation instance
-		AnnotationEntryGen a = createSimpleVisibleAnnotation(cp);
-		ArrayElementValueGen array = new ArrayElementValueGen(cp);
-		array.addElement(new AnnotationElementValueGen(a, cp));
-		ElementValuePairGen nvp = new ElementValuePairGen("value", array, cp);
-		List<ElementValuePairGen> elements = new ArrayList<ElementValuePairGen>();
-		elements.add(nvp);
-		return new AnnotationEntryGen(new ObjectType("CombinedAnnotation"),
-				elements, true, cp);
-	}
-
-	public AnnotationEntryGen createSimpleInvisibleAnnotation(ConstantPoolGen cp)
-	{
-		SimpleElementValueGen evg = new SimpleElementValueGen(
-				ElementValueGen.PRIMITIVE_INT, cp, 4);
-		ElementValuePairGen nvGen = new ElementValuePairGen("id", evg, cp);
-		ObjectType t = new ObjectType("SimpleAnnotation");
-		List<ElementValuePairGen> elements = new ArrayList<ElementValuePairGen>();
-		elements.add(nvGen);
-		AnnotationEntryGen a = new AnnotationEntryGen(t, elements, false, cp);
-		return a;
-	}
+    /**
+     * Steps in the test:
+     * <ol>
+     * <li>Programmatically construct the HelloWorld program</li>
+     * <li>Add two simple annotations at the class level</li>
+     * <li>Save the class to disk</li>
+     * <li>Reload the class using the 'static' variant of the BCEL classes</li>
+     * <li>Check the attributes are OK</li>
+     * </ol>
+     */
+    public void testGenerateClassLevelAnnotations()
+            throws ClassNotFoundException
+    {
+        // Create HelloWorld
+        ClassGen cg = createClassGen("HelloWorld");
+        cg.setMajor(49);
+        cg.setMinor(0);
+        ConstantPoolGen cp = cg.getConstantPool();
+        InstructionList il = new InstructionList();
+        cg.addAnnotationEntry(createSimpleVisibleAnnotation(cp));
+        cg.addAnnotationEntry(createSimpleInvisibleAnnotation(cp));
+        buildClassContents(cg, cp, il);
+        //System.out.println(cg.getJavaClass().toString());
+        dumpClass(cg, "HelloWorld.class");
+        JavaClass jc = getClassFrom(".", "HelloWorld");
+        AnnotationEntry[] as = jc.getAnnotationEntries();
+        assertTrue("Should be two AnnotationEntries but found " + as.length,
+                as.length == 2);
+        AnnotationEntry one = as[0];
+        AnnotationEntry two = as[1];
+        // TODO L??;
+        assertTrue(
+                "Name of annotation 1 should be LSimpleAnnotation; but it is "
+                        + as[0].getAnnotationType(), as[0].getAnnotationType()
+                        .equals("LSimpleAnnotation;"));
+        assertTrue(
+                "Name of annotation 2 should be LSimpleAnnotation; but it is "
+                        + as[1].getAnnotationType(), as[1].getAnnotationType()
+                        .equals("LSimpleAnnotation;"));
+        ElementValuePair[] vals = as[0].getElementValuePairs();
+        ElementValuePair nvp = vals[0];
+        assertTrue(
+                "Name of element in SimpleAnnotation should be 'id' but it is "
+                        + nvp.getNameString(), nvp.getNameString().equals("id"));
+        ElementValue ev = nvp.getValue();
+        assertTrue("Type of element value should be int but it is "
+                + ev.getElementValueType(),
+                ev.getElementValueType() == ElementValue.PRIMITIVE_INT);
+        assertTrue("Value of element should be 4 but it is "
+                + ev.stringifyValue(), ev.stringifyValue().equals("4"));
+        assertTrue(createTestdataFile("HelloWorld.class").delete());
+    }
+
+    /**
+     * Just check that we can dump a class that has a method annotation on it
+     * and it is still there when we read it back in
+     */
+    public void testGenerateMethodLevelAnnotations1()
+            throws ClassNotFoundException
+    {
+        // Create HelloWorld
+        ClassGen cg = createClassGen("HelloWorld");
+        ConstantPoolGen cp = cg.getConstantPool();
+        InstructionList il = new InstructionList();
+        buildClassContentsWithAnnotatedMethods(cg, cp, il);
+        // Check annotation is OK
+        int i = cg.getMethods()[0].getAnnotationEntries().length;
+        assertTrue(
+                "Prior to dumping, main method should have 1 annotation but has "
+                        + i, i == 1);
+        dumpClass(cg, "temp1" + File.separator + "HelloWorld.class");
+        JavaClass jc2 = getClassFrom("temp1", "HelloWorld");
+        // Check annotation is OK
+        i = jc2.getMethods()[0].getAnnotationEntries().length;
+        assertTrue("JavaClass should say 1 annotation on main method but says "
+                + i, i == 1);
+        ClassGen cg2 = new ClassGen(jc2);
+        // Check it now it is a ClassGen
+        Method[] m = cg2.getMethods();
+        i = m[0].getAnnotationEntries().length;
+        assertTrue("The main 'Method' should have one annotation but has " + i,
+                i == 1);
+        MethodGen mg = new MethodGen(m[0], cg2.getClassName(), cg2
+                .getConstantPool());
+        // Check it finally when the Method is changed to a MethodGen
+        i = mg.getAnnotationEntries().length;
+        assertTrue("The main 'MethodGen' should have one annotation but has "
+                + i, i == 1);
+        assertTrue(wipe("temp1" + File.separator + "HelloWorld.class"));
+    }
+
+    /**
+     * Going further than the last test - when we reload the method back in,
+     * let's change it (adding a new annotation) and then store that, read it
+     * back in and verify both annotations are there !
+     */
+    public void testGenerateMethodLevelAnnotations2()
+            throws ClassNotFoundException
+    {
+        // Create HelloWorld
+        ClassGen cg = createClassGen("HelloWorld");
+        ConstantPoolGen cp = cg.getConstantPool();
+        InstructionList il = new InstructionList();
+        buildClassContentsWithAnnotatedMethods(cg, cp, il);
+        dumpClass(cg, "temp2", "HelloWorld.class");
+        JavaClass jc2 = getClassFrom("temp2", "HelloWorld");
+        ClassGen cg2 = new ClassGen(jc2);
+        // Main method after reading the class back in
+        Method mainMethod1 = jc2.getMethods()[0];
+        assertTrue("The 'Method' should have one annotations but has "
+                + mainMethod1.getAnnotationEntries().length, mainMethod1
+                .getAnnotationEntries().length == 1);
+        MethodGen mainMethod2 = new MethodGen(mainMethod1, cg2.getClassName(),
+                cg2.getConstantPool());
+        assertTrue("The 'MethodGen' should have one annotations but has "
+                + mainMethod2.getAnnotationEntries().length, mainMethod2
+                .getAnnotationEntries().length == 1);
+        mainMethod2.addAnnotationEntry(createFruitAnnotation(cg2
+                .getConstantPool(), "Pear"));
+        cg2.removeMethod(mainMethod1);
+        cg2.addMethod(mainMethod2.getMethod());
+        dumpClass(cg2, "temp3", "HelloWorld.class");
+        JavaClass jc3 = getClassFrom("temp3", "HelloWorld");
+        ClassGen cg3 = new ClassGen(jc3);
+        Method mainMethod3 = cg3.getMethods()[1];
+        int i = mainMethod3.getAnnotationEntries().length;
+        assertTrue("The 'Method' should now have two annotations but has " + i,
+                i == 2);
+        assertTrue(wipe("temp2", "HelloWorld.class"));
+        assertTrue(wipe("temp3", "HelloWorld.class"));
+    }
+
+    // J5TODO: Need to add deleteFile calls to many of these tests
+    /**
+     * Transform simple class from an immutable to a mutable object.
+     */
+    public void testTransformClassToClassGen_SimpleTypes()
+            throws ClassNotFoundException
+    {
+        JavaClass jc = getTestClass("org.apache.bcel.data.SimpleAnnotatedClass");
+        ClassGen cgen = new ClassGen(jc);
+        // Check annotations are correctly preserved
+        AnnotationEntryGen[] annotations = cgen.getAnnotationEntries();
+        assertTrue("Expected one annotation but found " + annotations.length,
+                annotations.length == 1);
+    }
+
+    /**
+     * Transform simple class from an immutable to a mutable object. The class
+     * is annotated with an annotation that uses an enum.
+     */
+    public void testTransformClassToClassGen_EnumType()
+            throws ClassNotFoundException
+    {
+        JavaClass jc = getTestClass("org.apache.bcel.data.AnnotatedWithEnumClass");
+        ClassGen cgen = new ClassGen(jc);
+        // Check annotations are correctly preserved
+        AnnotationEntryGen[] annotations = cgen.getAnnotationEntries();
+        assertTrue("Expected one annotation but found " + annotations.length,
+                annotations.length == 1);
+    }
+
+    /**
+     * Transform simple class from an immutable to a mutable object. The class
+     * is annotated with an annotation that uses an array of SimpleAnnotations.
+     */
+    public void testTransformClassToClassGen_ArrayAndAnnotationTypes()
+            throws ClassNotFoundException
+    {
+        JavaClass jc = getTestClass("org.apache.bcel.data.AnnotatedWithCombinedAnnotation");
+        ClassGen cgen = new ClassGen(jc);
+        // Check annotations are correctly preserved
+        AnnotationEntryGen[] annotations = cgen.getAnnotationEntries();
+        assertTrue("Expected one annotation but found " + annotations.length,
+                annotations.length == 1);
+        AnnotationEntryGen a = annotations[0];
+        assertTrue("That annotation should only have one value but has "
+                + a.getValues().size(), a.getValues().size() == 1);
+        ElementValuePairGen nvp = a.getValues().get(0);
+        ElementValueGen value = nvp.getValue();
+        assertTrue("Value should be ArrayElementValueGen but is " + value,
+                value instanceof ArrayElementValueGen);
+        ArrayElementValueGen arrayValue = (ArrayElementValueGen) value;
+        assertTrue("Array value should be size one but is "
+                + arrayValue.getElementValuesSize(), arrayValue
+                .getElementValuesSize() == 1);
+        ElementValueGen innerValue = arrayValue.getElementValues().get(0);
+        assertTrue(
+                "Value in the array should be AnnotationElementValueGen but is "
+                        + innerValue,
+                innerValue instanceof AnnotationElementValueGen);
+        AnnotationElementValueGen innerAnnotationValue = (AnnotationElementValueGen) innerValue;
+        assertTrue("Should be called Lorg/apache/bcel/data/SimpleAnnotation; but is called: "
+                + innerAnnotationValue.getAnnotation().getTypeName(),
+                innerAnnotationValue.getAnnotation().getTypeSignature().equals(
+                        "Lorg/apache/bcel/data/SimpleAnnotation;"));
+
+        // check the three methods
+        Method[] methods = cgen.getMethods();
+        assertEquals(3, methods.length);
+        for(Method method : methods)
+        {
+            String methodName= method.getName();
+            if(methodName.equals("<init>"))
+            {
+                assertMethodAnnotations(method, 0, 1);
+                assertParameterAnnotations(method, 0, 1);
+            }
+            else if(methodName.equals("methodWithArrayOfZeroAnnotations"))
+            {
+                assertMethodAnnotations(method, 1, 0);
+            }
+            else if(methodName.equals("methodWithArrayOfTwoAnnotations"))
+            {
+                assertMethodAnnotations(method, 1, 2);
+            }
+            else
+            {
+                fail("unexpected method "+method.getName());
+            }
+        }
+    }
+
+    private void assertMethodAnnotations(Method method, int expectedNumberAnnotations, int nExpectedArrayValues)
+    {
+        String methodName= method.getName();
+        AnnotationEntry[] annos= method.getAnnotationEntries();
+        assertEquals("For "+methodName, expectedNumberAnnotations, annos.length);
+        if(expectedNumberAnnotations!=0)
+        {
+            assertArrayElementValue(nExpectedArrayValues, annos[0]);
+        }
+    }
+
+    private void assertArrayElementValue(int nExpectedArrayValues, AnnotationEntry anno)
+    {
+        ElementValuePair elementValuePair = anno.getElementValuePairs()[0];
+        assertEquals("value", elementValuePair.getNameString());
+        ArrayElementValue ev = (ArrayElementValue) elementValuePair.getValue();
+        ElementValue[] eva = ev.getElementValuesArray();
+        assertEquals(nExpectedArrayValues, eva.length);
+    }
+
+    private void assertParameterAnnotations(Method method, int... expectedNumberOfParmeterAnnotations)
+    {
+        String methodName= "For "+method.getName();
+        ParameterAnnotationEntry[] parameterAnnotations= method.getParameterAnnotationEntries();
+        assertEquals(methodName, expectedNumberOfParmeterAnnotations.length, parameterAnnotations.length);
+
+        int i= 0;
+        for(ParameterAnnotationEntry parameterAnnotation : parameterAnnotations)
+        {
+            AnnotationEntry[] annos= parameterAnnotation.getAnnotationEntries();
+            int expectedLength = expectedNumberOfParmeterAnnotations[i++];
+            assertEquals(methodName+" parameter "+i, expectedLength, annos.length);
+            if(expectedLength!=0)
+            {
+                assertSimpleElementValue(annos[0]);
+            }
+        }
+    }
+
+    private void assertSimpleElementValue(AnnotationEntry anno)
+    {
+        ElementValuePair elementValuePair = anno.getElementValuePairs()[0];
+        assertEquals("id", elementValuePair.getNameString());
+        SimpleElementValue ev = (SimpleElementValue)elementValuePair.getValue();
+        assertEquals(42, ev.getValueInt());
+    }
+
+    /**
+     * Transform complex class from an immutable to a mutable object.
+     */
+    public void testTransformComplexClassToClassGen()
+            throws ClassNotFoundException
+    {
+        JavaClass jc = getTestClass("org.apache.bcel.data.ComplexAnnotatedClass");
+        ClassGen cgen = new ClassGen(jc);
+        // Check annotations are correctly preserved
+        AnnotationEntryGen[] annotations = cgen.getAnnotationEntries();
+        assertTrue("Expected one annotation but found " + annotations.length,
+                annotations.length == 1);
+        List<?> l = annotations[0].getValues();
+        boolean found = false;
+        for (Iterator<?> iter = l.iterator(); iter.hasNext();)
+        {
+            ElementValuePairGen element = (ElementValuePairGen) iter.next();
+            if (element.getNameString().equals("dval"))
+            {
+                if (((SimpleElementValueGen) element.getValue())
+                        .stringifyValue().equals("33.4"))
+                    found = true;
+            }
+        }
+        assertTrue("Did not find double annotation value with value 33.4",
+                found);
+    }
+
+    /**
+     * Load a class in and modify it with a new attribute - A SimpleAnnotation
+     * annotation
+     */
+    public void testModifyingClasses1() throws ClassNotFoundException
+    {
+        JavaClass jc = getTestClass("org.apache.bcel.data.SimpleAnnotatedClass");
+        ClassGen cgen = new ClassGen(jc);
+        ConstantPoolGen cp = cgen.getConstantPool();
+        cgen.addAnnotationEntry(createFruitAnnotation(cp, "Pineapple"));
+        assertTrue("Should now have two annotations but has "
+                + cgen.getAnnotationEntries().length, cgen
+                .getAnnotationEntries().length == 2);
+        dumpClass(cgen, "SimpleAnnotatedClass.class");
+        assertTrue(wipe("SimpleAnnotatedClass.class"));
+    }
+
+    /**
+     * Load a class in and modify it with a new attribute - A ComplexAnnotation
+     * annotation
+     */
+    public void testModifyingClasses2() throws ClassNotFoundException
+    {
+        JavaClass jc = getTestClass("org.apache.bcel.data.SimpleAnnotatedClass");
+        ClassGen cgen = new ClassGen(jc);
+        ConstantPoolGen cp = cgen.getConstantPool();
+        cgen.addAnnotationEntry(createCombinedAnnotation(cp));
+        assertTrue("Should now have two annotations but has "
+                + cgen.getAnnotationEntries().length, cgen
+                .getAnnotationEntries().length == 2);
+        dumpClass(cgen, "SimpleAnnotatedClass.class");
+        JavaClass jc2 = getClassFrom(".", "SimpleAnnotatedClass");
+        jc2.getAnnotationEntries();
+        assertTrue(wipe("SimpleAnnotatedClass.class"));
+        // System.err.println(jc2.toString());
+    }
+
+    private void dumpClass(ClassGen cg, String fname)
+    {
+        try
+        {
+            File f = createTestdataFile(fname);
+            cg.getJavaClass().dump(f);
+        }
+        catch (java.io.IOException e)
+        {
+            System.err.println(e);
+        }
+    }
+
+    private void dumpClass(ClassGen cg, String dir, String fname)
+    {
+        dumpClass(cg, dir + File.separator + fname);
+    }
+
+    private void buildClassContentsWithAnnotatedMethods(ClassGen cg,
+            ConstantPoolGen cp, InstructionList il)
+    {
+        // Create method 'public static void main(String[]argv)'
+        MethodGen mg = createMethodGen("main", il, cp);
+        InstructionFactory factory = new InstructionFactory(cg);
+        mg.addAnnotationEntry(createSimpleVisibleAnnotation(mg
+                .getConstantPool()));
+        // We now define some often used types:
+        ObjectType i_stream = new ObjectType("java.io.InputStream");
+        ObjectType p_stream = new ObjectType("java.io.PrintStream");
+        // Create variables in and name : We call the constructors, i.e.,
+        // execute BufferedReader(InputStreamReader(System.in)) . The reference
+        // to the BufferedReader object stays on top of the stack and is stored
+        // in the newly allocated in variable.
+        il.append(factory.createNew("java.io.BufferedReader"));
+        il.append(InstructionConstants.DUP); // Use predefined constant
+        il.append(factory.createNew("java.io.InputStreamReader"));
+        il.append(InstructionConstants.DUP);
+        il.append(factory.createFieldAccess("java.lang.System", "in", i_stream,
+                Constants.GETSTATIC));
+        il.append(factory.createInvoke("java.io.InputStreamReader", "<init>",
+                Type.VOID, new Type[] { i_stream }, Constants.INVOKESPECIAL));
+        il.append(factory.createInvoke("java.io.BufferedReader", "<init>",
+                Type.VOID, new Type[] { new ObjectType("java.io.Reader") },
+                Constants.INVOKESPECIAL));
+        LocalVariableGen lg = mg.addLocalVariable("in", new ObjectType(
+                "java.io.BufferedReader"), null, null);
+        int in = lg.getIndex();
+        lg.setStart(il.append(new ASTORE(in))); // "in" valid from here
+        // Create local variable name and initialize it to null
+        lg = mg.addLocalVariable("name", Type.STRING, null, null);
+        int name = lg.getIndex();
+        il.append(InstructionConstants.ACONST_NULL);
+        lg.setStart(il.append(new ASTORE(name))); // "name" valid from here
+        // Create try-catch block: We remember the start of the block, read a
+        // line from the standard input and store it into the variable name .
+        // InstructionHandle try_start = il.append(factory.createFieldAccess(
+        // "java.lang.System", "out", p_stream, Constants.GETSTATIC));
+        // il.append(new PUSH(cp, "Please enter your name> "));
+        // il.append(factory.createInvoke("java.io.PrintStream", "print",
+        // Type.VOID, new Type[] { Type.STRING },
+        // Constants.INVOKEVIRTUAL));
+        // il.append(new ALOAD(in));
+        // il.append(factory.createInvoke("java.io.BufferedReader", "readLine",
+        // Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
+        InstructionHandle try_start = il.append(new PUSH(cp, "Andy"));
+        il.append(new ASTORE(name));
+        // Upon normal execution we jump behind exception handler, the target
+        // address is not known yet.
+        GOTO g = new GOTO(null);
+        InstructionHandle try_end = il.append(g);
+        // We add the exception handler which simply returns from the method.
+        LocalVariableGen var_ex = mg.addLocalVariable("ex", Type
+                .getType("Ljava.io.IOException;"), null, null);
+        int var_ex_slot = var_ex.getIndex();
+        InstructionHandle handler = il.append(new ASTORE(var_ex_slot));
+        var_ex.setStart(handler);
+        var_ex.setEnd(il.append(InstructionConstants.RETURN));
+        mg.addExceptionHandler(try_start, try_end, handler, new ObjectType(
+                "java.io.IOException"));
+        // "Normal" code continues, now we can set the branch target of the GOTO
+        // .
+        InstructionHandle ih = il.append(factory.createFieldAccess(
+                "java.lang.System", "out", p_stream, Constants.GETSTATIC));
+        g.setTarget(ih);
+        // Printing "Hello": String concatenation compiles to StringBuffer
+        // operations.
+        il.append(factory.createNew(Type.STRINGBUFFER));
+        il.append(InstructionConstants.DUP);
+        il.append(new PUSH(cp, "Hello, "));
+        il
+                .append(factory.createInvoke("java.lang.StringBuffer",
+                        "<init>", Type.VOID, new Type[] { Type.STRING },
+                        Constants.INVOKESPECIAL));
+        il.append(new ALOAD(name));
+        il.append(factory.createInvoke("java.lang.StringBuffer", "append",
+                Type.STRINGBUFFER, new Type[] { Type.STRING },
+                Constants.INVOKEVIRTUAL));
+        il.append(factory.createInvoke("java.lang.StringBuffer", "toString",
+                Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
+        il
+                .append(factory.createInvoke("java.io.PrintStream", "println",
+                        Type.VOID, new Type[] { Type.STRING },
+                        Constants.INVOKEVIRTUAL));
+        il.append(InstructionConstants.RETURN);
+        // Finalization: Finally, we have to set the stack size, which normally
+        // would have to be computed on the fly and add a default constructor
+        // method to the class, which is empty in this case.
+        mg.setMaxStack();
+        mg.setMaxLocals();
+        cg.addMethod(mg.getMethod());
+        il.dispose(); // Allow instruction handles to be reused
+        cg.addEmptyConstructor(Constants.ACC_PUBLIC);
+    }
+
+    private void buildClassContents(ClassGen cg, ConstantPoolGen cp,
+            InstructionList il)
+    {
+        // Create method 'public static void main(String[]argv)'
+        MethodGen mg = createMethodGen("main", il, cp);
+        InstructionFactory factory = new InstructionFactory(cg);
+        // We now define some often used types:
+        ObjectType i_stream = new ObjectType("java.io.InputStream");
+        ObjectType p_stream = new ObjectType("java.io.PrintStream");
+        // Create variables in and name : We call the constructors, i.e.,
+        // execute BufferedReader(InputStreamReader(System.in)) . The reference
+        // to the BufferedReader object stays on top of the stack and is stored
+        // in the newly allocated in variable.
+        il.append(factory.createNew("java.io.BufferedReader"));
+        il.append(InstructionConstants.DUP); // Use predefined constant
+        il.append(factory.createNew("java.io.InputStreamReader"));
+        il.append(InstructionConstants.DUP);
+        il.append(factory.createFieldAccess("java.lang.System", "in", i_stream,
+                Constants.GETSTATIC));
+        il.append(factory.createInvoke("java.io.InputStreamReader", "<init>",
+                Type.VOID, new Type[] { i_stream }, Constants.INVOKESPECIAL));
+        il.append(factory.createInvoke("java.io.BufferedReader", "<init>",
+                Type.VOID, new Type[] { new ObjectType("java.io.Reader") },
+                Constants.INVOKESPECIAL));
+        LocalVariableGen lg = mg.addLocalVariable("in", new ObjectType(
+                "java.io.BufferedReader"), null, null);
+        int in = lg.getIndex();
+        lg.setStart(il.append(new ASTORE(in))); // "in" valid from here
+        // Create local variable name and initialize it to null
+        lg = mg.addLocalVariable("name", Type.STRING, null, null);
+        int name = lg.getIndex();
+        il.append(InstructionConstants.ACONST_NULL);
+        lg.setStart(il.append(new ASTORE(name))); // "name" valid from here
+        // Create try-catch block: We remember the start of the block, read a
+        // line from the standard input and store it into the variable name .
+        // InstructionHandle try_start = il.append(factory.createFieldAccess(
+        // "java.lang.System", "out", p_stream, Constants.GETSTATIC));
+        // il.append(new PUSH(cp, "Please enter your name> "));
+        // il.append(factory.createInvoke("java.io.PrintStream", "print",
+        // Type.VOID, new Type[] { Type.STRING },
+        // Constants.INVOKEVIRTUAL));
+        // il.append(new ALOAD(in));
+        // il.append(factory.createInvoke("java.io.BufferedReader", "readLine",
+        // Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
+        InstructionHandle try_start = il.append(new PUSH(cp, "Andy"));
+        il.append(new ASTORE(name));
+        // Upon normal execution we jump behind exception handler, the target
+        // address is not known yet.
+        GOTO g = new GOTO(null);
+        InstructionHandle try_end = il.append(g);
+        // We add the exception handler which simply returns from the method.
+        LocalVariableGen var_ex = mg.addLocalVariable("ex", Type
+                .getType("Ljava.io.IOException;"), null, null);
+        int var_ex_slot = var_ex.getIndex();
+        InstructionHandle handler = il.append(new ASTORE(var_ex_slot));
+        var_ex.setStart(handler);
+        var_ex.setEnd(il.append(InstructionConstants.RETURN));
+        mg.addExceptionHandler(try_start, try_end, handler, new ObjectType(
+                "java.io.IOException"));
+        // "Normal" code continues, now we can set the branch target of the GOTO
+        // .
+        InstructionHandle ih = il.append(factory.createFieldAccess(
+                "java.lang.System", "out", p_stream, Constants.GETSTATIC));
+        g.setTarget(ih);
+        // Printing "Hello": String concatenation compiles to StringBuffer
+        // operations.
+        il.append(factory.createNew(Type.STRINGBUFFER));
+        il.append(InstructionConstants.DUP);
+        il.append(new PUSH(cp, "Hello, "));
+        il
+                .append(factory.createInvoke("java.lang.StringBuffer",
+                        "<init>", Type.VOID, new Type[] { Type.STRING },
+                        Constants.INVOKESPECIAL));
+        il.append(new ALOAD(name));
+        il.append(factory.createInvoke("java.lang.StringBuffer", "append",
+                Type.STRINGBUFFER, new Type[] { Type.STRING },
+                Constants.INVOKEVIRTUAL));
+        il.append(factory.createInvoke("java.lang.StringBuffer", "toString",
+                Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
+        il
+                .append(factory.createInvoke("java.io.PrintStream", "println",
+                        Type.VOID, new Type[] { Type.STRING },
+                        Constants.INVOKEVIRTUAL));
+        il.append(InstructionConstants.RETURN);
+        // Finalization: Finally, we have to set the stack size, which normally
+        // would have to be computed on the fly and add a default constructor
+        // method to the class, which is empty in this case.
+        mg.setMaxStack();
+        mg.setMaxLocals();
+        cg.addMethod(mg.getMethod());
+        il.dispose(); // Allow instruction handles to be reused
+        cg.addEmptyConstructor(Constants.ACC_PUBLIC);
+    }
+
+    private JavaClass getClassFrom(String where, String clazzname)
+            throws ClassNotFoundException
+    {
+        // System.out.println(where);
+        SyntheticRepository repos = createRepos(where);
+        return repos.loadClass(clazzname);
+    }
+
+    // helper methods
+    private ClassGen createClassGen(String classname)
+    {
+        return new ClassGen(classname, "java.lang.Object", "<generated>",
+                Constants.ACC_PUBLIC | Constants.ACC_SUPER, null);
+    }
+
+    private MethodGen createMethodGen(String methodname, InstructionList il,
+            ConstantPoolGen cp)
+    {
+        return new MethodGen(Constants.ACC_STATIC | Constants.ACC_PUBLIC, // access
+                // flags
+                Type.VOID, // return type
+                new Type[] { new ArrayType(Type.STRING, 1) }, // argument
+                // types
+                new String[] { "argv" }, // arg names
+                methodname, "HelloWorld", // method, class
+                il, cp);
+    }
+
+    public AnnotationEntryGen createSimpleVisibleAnnotation(ConstantPoolGen cp)
+    {
+        SimpleElementValueGen evg = new SimpleElementValueGen(
+                ElementValueGen.PRIMITIVE_INT, cp, 4);
+        ElementValuePairGen nvGen = new ElementValuePairGen("id", evg, cp);
+        ObjectType t = new ObjectType("SimpleAnnotation");
+        List<ElementValuePairGen> elements = new ArrayList<ElementValuePairGen>();
+        elements.add(nvGen);
+        AnnotationEntryGen a = new AnnotationEntryGen(t, elements, true, cp);
+        return a;
+    }
+
+    public AnnotationEntryGen createFruitAnnotation(ConstantPoolGen cp,
+            String aFruit)
+    {
+        SimpleElementValueGen evg = new SimpleElementValueGen(
+                ElementValueGen.STRING, cp, aFruit);
+        ElementValuePairGen nvGen = new ElementValuePairGen("fruit", evg, cp);
+        ObjectType t = new ObjectType("SimpleStringAnnotation");
+        List<ElementValuePairGen> elements = new ArrayList<ElementValuePairGen>();
+        elements.add(nvGen);
+        return new AnnotationEntryGen(t, elements, true, cp);
+    }
+
+    public AnnotationEntryGen createCombinedAnnotation(ConstantPoolGen cp)
+    {
+        // Create an annotation instance
+        AnnotationEntryGen a = createSimpleVisibleAnnotation(cp);
+        ArrayElementValueGen array = new ArrayElementValueGen(cp);
+        array.addElement(new AnnotationElementValueGen(a, cp));
+        ElementValuePairGen nvp = new ElementValuePairGen("value", array, cp);
+        List<ElementValuePairGen> elements = new ArrayList<ElementValuePairGen>();
+        elements.add(nvp);
+        return new AnnotationEntryGen(new ObjectType("CombinedAnnotation"),
+                elements, true, cp);
+    }
+
+    public AnnotationEntryGen createSimpleInvisibleAnnotation(ConstantPoolGen cp)
+    {
+        SimpleElementValueGen evg = new SimpleElementValueGen(
+                ElementValueGen.PRIMITIVE_INT, cp, 4);
+        ElementValuePairGen nvGen = new ElementValuePairGen("id", evg, cp);
+        ObjectType t = new ObjectType("SimpleAnnotation");
+        List<ElementValuePairGen> elements = new ArrayList<ElementValuePairGen>();
+        elements.add(nvGen);
+        AnnotationEntryGen a = new AnnotationEntryGen(t, elements, false, cp);
+        return a;
+    }
 }
\ No newline at end of file

Modified: commons/proper/bcel/trunk/src/test/java/org/apache/bcel/data/AnnotatedWithCombinedAnnotation.java
URL: http://svn.apache.org/viewvc/commons/proper/bcel/trunk/src/test/java/org/apache/bcel/data/AnnotatedWithCombinedAnnotation.java?rev=1312005&r1=1312004&r2=1312005&view=diff
==============================================================================
--- commons/proper/bcel/trunk/src/test/java/org/apache/bcel/data/AnnotatedWithCombinedAnnotation.java (original)
+++ commons/proper/bcel/trunk/src/test/java/org/apache/bcel/data/AnnotatedWithCombinedAnnotation.java Tue Apr 10 21:37:21 2012
@@ -13,7 +13,7 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- * 
+ *
  */
 
 package org.apache.bcel.data;
@@ -21,4 +21,14 @@ package org.apache.bcel.data;
 @CombinedAnnotation( { @SimpleAnnotation(id = 4) })
 public class AnnotatedWithCombinedAnnotation
 {
+    public AnnotatedWithCombinedAnnotation(int param1, @SimpleAnnotation(id=42) int param2) {
+    }
+
+    @CombinedAnnotation( {})
+    public void methodWithArrayOfZeroAnnotations() {
+    }
+
+    @CombinedAnnotation( { @SimpleAnnotation(id=1, fruit="apples"), @SimpleAnnotation(id= 2, fruit="oranges")})
+    public void methodWithArrayOfTwoAnnotations() {
+    }
 }