Fix FELIX-2542 - Annotations on methods parameters are not moved on public methods after manipulation

Parameter Annotations were not moved to the interceptor method during the annotation processing. This is now fixed.

As for method annotations, only annotation visible at runtime are moved, others are ignored.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@987909 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ClassChecker.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ClassChecker.java
index 3f641b9..f5ece7d 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ClassChecker.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/ClassChecker.java
@@ -285,6 +285,18 @@
             }

             return null;

         }

+

+        public AnnotationVisitor visitParameterAnnotation(int id,

+                String name, boolean visible) {

+            if (visible) {

+                AnnotationDescriptor ann = new AnnotationDescriptor(name, visible);

+                m_method.addParameterAnnotation(id, ann);

+                return ann;

+            }

+            return null;

+        }

+        

+        

     }

     

     /**

@@ -419,7 +431,7 @@
          * attributes.

          * @param mv the method visitor visiting the destination method.

          */

-        public void visit(MethodVisitor mv) {

+        public void visitAnnotation(MethodVisitor mv) {

             AnnotationVisitor av = mv.visitAnnotation(m_name, m_visible);

             for (int i = 0; i < m_simples.size(); i++) {

                 ((SimpleAttribute) m_simples.get(i)).visit(av);

@@ -437,6 +449,31 @@
         }

         

         /**

+         * Methods allowing to recreate the visited (stored) parameter annotations

+         * into the destination method.

+         * This method recreate the annotations itself and any other 

+         * attributes.

+         * @param id the paramter id

+         * @param mv the method visitor visiting the destination method.

+         */

+        public void visitParameterAnnotation(int id, MethodVisitor mv) {

+            AnnotationVisitor av = mv.visitParameterAnnotation(id, m_name, m_visible);

+            for (int i = 0; i < m_simples.size(); i++) {

+                ((SimpleAttribute) m_simples.get(i)).visit(av);

+            }

+            for (int i = 0; i < m_enums.size(); i++) {

+                ((EnumAttribute) m_enums.get(i)).visit(av);

+            }

+            for (int i = 0; i < m_nested.size(); i++) {

+                ((AnnotationDescriptor) m_nested.get(i)).visit(av);

+            }

+            for (int i = 0; i < m_arrays.size(); i++) {

+                ((ArrayAttribute) m_arrays.get(i)).visit(av);

+            }

+            av.visitEnd();

+        }

+        

+        /**

          * Method allowing to recreate the visited (stored) annotation

          * into the destination annotation. This method is used only

          * for nested annotation.

diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCodeAdapter.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCodeAdapter.java
index 06e72ed..93f508f 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCodeAdapter.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCodeAdapter.java
@@ -100,4 +100,22 @@
         }

     }

 

+    /**

+     * Visits a parameter annotation.

+     * @param id the parameter number.

+     * @param name the annotation name

+     * @param visible if te annotation visibility

+     * @return the <code>null</code> if the annotation is visible, otherwise returns

+     * {@link GeneratorAdapter#visitAnnotation(String, boolean)}

+     * @see org.objectweb.asm.MethodAdapter#visitParameterAnnotation(int, java.lang.String, boolean)

+     */

+    public AnnotationVisitor visitParameterAnnotation(int id, String name,

+            boolean visible) {

+        if (visible) {

+            return null;

+        } else {

+            return super.visitParameterAnnotation(id, name, visible);

+        }

+    }

+

 }

diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCreator.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCreator.java
index 5b851e9..279d00b 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCreator.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodCreator.java
@@ -19,6 +19,7 @@
 package org.apache.felix.ipojo.manipulation;

 

 import java.util.ArrayList;

+import java.util.HashMap;

 import java.util.Iterator;

 import java.util.List;

 import java.util.Map;

@@ -191,10 +192,10 @@
 

             Type[] args = Type.getArgumentTypes(desc);

             if (args.length == 0) {

-                generateEmptyConstructor(access, signature, exceptions, md.getAnnotations());

+                generateEmptyConstructor(access, signature, exceptions, md.getAnnotations()); // No parameters, so no annotations parameters

                 m_foundSuitableConstructor = true;

             } else if (args.length == 1 && args[0].getClassName().equals("org.osgi.framework.BundleContext")) {

-                generateBCConstructor(access, signature, exceptions, md.getAnnotations());

+                generateBCConstructor(access, signature, exceptions, md.getAnnotations()); // One parameter, so no annotations parameters

                 m_foundSuitableConstructor = true;

             } else {

                 // Do nothing, the constructor does not match.

@@ -215,9 +216,9 @@
 

         MethodDescriptor md = getMethodDescriptor(name, desc);

         if (md == null) {

-            generateMethodHeader(access, name, desc, signature, exceptions, new ArrayList(0));

+            generateMethodHeader(access, name, desc, signature, exceptions, new ArrayList(0), new HashMap());

         } else {

-            generateMethodHeader(access, name, desc, signature, exceptions, md.getAnnotations());

+            generateMethodHeader(access, name, desc, signature, exceptions, md.getAnnotations(), md.getParameterAnnotations());

         }

         

         String id = generateMethodFlag(name, desc);

@@ -312,7 +313,7 @@
         if (annotations != null) {

             for (int i = 0; i < annotations.size(); i++) {

                 AnnotationDescriptor ad = (AnnotationDescriptor) annotations.get(i);

-                ad.visit(mv);

+                ad.visitAnnotation(mv);

             }

         }

         

@@ -344,7 +345,7 @@
         if (annotations != null) {

             for (int i = 0; i < annotations.size(); i++) {

                 AnnotationDescriptor ad = (AnnotationDescriptor) annotations.get(i);

-                ad.visit(mv);

+                ad.visitAnnotation(mv);

             }

         }

 

@@ -362,8 +363,9 @@
      * @param signature : method signature.

      * @param exceptions : declared exceptions.

      * @param annotations : the annotations to move to this method.

+     * @param paramAnnotations : the parameter annotations to move to this method.

      */

-    private void generateMethodHeader(int access, String name, String desc, String signature, String[] exceptions, List annotations) {

+    private void generateMethodHeader(int access, String name, String desc, String signature, String[] exceptions, List annotations, Map paramAnnotations) {

         GeneratorAdapter mv = new GeneratorAdapter(cv.visitMethod(access, name, desc, signature, exceptions), access, name, desc); 

         

         mv.visitCode();

@@ -457,7 +459,20 @@
         if (annotations != null) {

             for (int i = 0; i < annotations.size(); i++) {

                 AnnotationDescriptor ad = (AnnotationDescriptor) annotations.get(i);

-                ad.visit(mv);

+                ad.visitAnnotation(mv);

+            }

+        }

+        

+        // Move parameter annotations

+        if (paramAnnotations != null  && ! paramAnnotations.isEmpty()) {

+            Iterator ids = paramAnnotations.keySet().iterator();

+            while(ids.hasNext()) {

+                Integer id = (Integer) ids.next();

+                List ads = (List) paramAnnotations.get(id);

+                for (int i = 0; i < ads.size(); i++) {

+                    AnnotationDescriptor ad = (AnnotationDescriptor) ads.get(i);

+                    ad.visitParameterAnnotation(id.intValue(), mv);

+                }

             }

         }

 

diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodDescriptor.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodDescriptor.java
index 2de5744..6876f06 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodDescriptor.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/MethodDescriptor.java
@@ -19,7 +19,9 @@
 package org.apache.felix.ipojo.manipulation;

 

 import java.util.ArrayList;

+import java.util.HashMap;

 import java.util.List;

+import java.util.Map;

 

 import org.apache.felix.ipojo.manipulation.ClassChecker.AnnotationDescriptor;

 import org.apache.felix.ipojo.metadata.Attribute;

@@ -58,6 +60,11 @@
      * method. 

      */

     private List m_annotations;

+    

+    /**

+     * The association argument (number) - {@link AnnotationDescriptor}. 

+     */

+    private Map/*<Integer, List<AnnotationDescriptor>>*/ m_parameterAnnotations = new HashMap/*<Integer, List<AnnotationDescriptor>>*/();

 

     /**

      * Constructor.

@@ -88,10 +95,27 @@
         m_annotations.add(ann);

     }

     

+    /**

+     * Add an annotation to the current method.

+     * @param ann annotation to add

+     */

+    public void addParameterAnnotation(int id, AnnotationDescriptor ann) {

+        List list = (List) m_parameterAnnotations.get(new Integer(id));

+        if (list == null) {

+            list = new ArrayList();

+            m_parameterAnnotations.put(new Integer(id), list);

+        }

+        list.add(ann);

+    }

+    

     public List getAnnotations() {

         return m_annotations;

     }

     

+    public Map getParameterAnnotations() {

+        return m_parameterAnnotations;

+    }

+    

     public String getDescriptor() {

         return m_desc;

     }

diff --git a/ipojo/tests/manipulator/manipulator-java5/pom.xml b/ipojo/tests/manipulator/manipulator-java5/pom.xml
index 218346f..99844e8 100644
--- a/ipojo/tests/manipulator/manipulator-java5/pom.xml
+++ b/ipojo/tests/manipulator/manipulator-java5/pom.xml
@@ -17,7 +17,7 @@
 	under the License.

 -->

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

 	<parent>

 		<groupId>ipojo.tests</groupId>

 		<artifactId>ipojo.tests</artifactId>

@@ -85,14 +85,12 @@
 			<plugin>

 				<groupId>org.apache.felix</groupId>

 				<artifactId>maven-ipojo-plugin</artifactId>

+				<version>1.7.0-SNAPSHOT</version>

 				<executions>

 					<execution>

 						<goals>

 							<goal>ipojo-bundle</goal>

 						</goals>

-						<configuration>

-							<ignoreAnnotations>true</ignoreAnnotations>

-						</configuration>

 					</execution>

 				</executions>

 			</plugin>

@@ -104,6 +102,21 @@
 					<target>1.5</target>

 				</configuration>

 			</plugin>

+

+			<plugin>

+				<groupId>org.apache.felix</groupId>

+				<artifactId>maven-junit4osgi-plugin</artifactId>

+				<version>1.1.0-SNAPSHOT</version>

+				<executions>

+					<execution>

+						<goals>

+							<goal>test</goal>

+						</goals>

+						<configuration>

+						</configuration>

+					</execution>

+				</executions>

+			</plugin>

 		</plugins>

 	</build>

 </project>

diff --git a/ipojo/tests/manipulator/manipulator-java5/src/main/java/org/apache/felix/ipojo/test/scenarios/component/Annotation.java b/ipojo/tests/manipulator/manipulator-java5/src/main/java/org/apache/felix/ipojo/test/scenarios/component/Annotation.java
index 0e21b2c..efdc265 100644
--- a/ipojo/tests/manipulator/manipulator-java5/src/main/java/org/apache/felix/ipojo/test/scenarios/component/Annotation.java
+++ b/ipojo/tests/manipulator/manipulator-java5/src/main/java/org/apache/felix/ipojo/test/scenarios/component/Annotation.java
@@ -1,5 +1,8 @@
 package org.apache.felix.ipojo.test.scenarios.component;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 import org.apache.felix.ipojo.test.scenarios.component.Marker.Type;
 
 public class Annotation {
@@ -25,5 +28,19 @@
     public Annotation() {
         
     }
+    
+    public void doSomethingWithParams(@Marker(name="marker", type=Type.BAR, 
+            sub=@SubMarker(subname="foo"),
+            arrayOfObjects={"foo", "bar", "baz"},
+            arrayOfAnnotations= {@SubMarker(subname="foo")}) String foo, 
+            @Invisible String bar, 
+            @bla @SubMarker(subname = "baz") String baz) {
+        System.out.println("Foo ...");
+    }
+    
+    @Retention(RetentionPolicy.RUNTIME)
+    @interface bla {
+        
+    }
 
 }
diff --git a/ipojo/tests/manipulator/manipulator-java5/src/main/java/org/apache/felix/ipojo/test/scenarios/manipulation/Annotation.java b/ipojo/tests/manipulator/manipulator-java5/src/main/java/org/apache/felix/ipojo/test/scenarios/manipulation/Annotation.java
index 6128a1d..81b8831 100644
--- a/ipojo/tests/manipulator/manipulator-java5/src/main/java/org/apache/felix/ipojo/test/scenarios/manipulation/Annotation.java
+++ b/ipojo/tests/manipulator/manipulator-java5/src/main/java/org/apache/felix/ipojo/test/scenarios/manipulation/Annotation.java
@@ -2,6 +2,7 @@
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
+import java.util.Arrays;
 
 import org.apache.felix.ipojo.junit4osgi.OSGiTestCase;
 import org.apache.felix.ipojo.test.scenarios.component.Marker;
@@ -99,6 +100,43 @@
         assertEquals("Check submarker", "bar", sub.subname());
     }
     
+    public void testParameterAnnotations() {
+        Method method = null;
+        try {
+            method = this.clazz.getMethod("doSomethingWithParams", new Class[] {String.class, String.class, String.class});
+        } catch (Exception e) {
+            fail("Cannot find the doSomethingWithParams method : " + e.getMessage());
+        } 
+        assertNotNull("Check method existence", method);
+        
+        java.lang.annotation.Annotation[][] annotations = method.getParameterAnnotations();
+        assertNotNull("Check annotations size - 1", annotations);
+        assertEquals("Check annotations size - 3", 3, annotations.length);
+        
+        // Check internals
+        // First parameter (foo)
+        java.lang.annotation.Annotation[] fooAnns = annotations[0];
+        assertEquals("Check fooAnns length", 1, fooAnns.length);
+        Marker marker = (Marker) fooAnns[0];
+        assertNotNull("Check marker", marker);
+        assertEquals("Check marker name", "marker", marker.name());
+        assertEquals("Check marker type", Marker.Type.BAR, marker.type());
+        assertEquals("Check sub marker attribute", "foo", marker.sub().subname());
+        assertEquals("Check objects [0]", "foo", marker.arrayOfObjects()[0]);
+        assertEquals("Check objects [1]", "bar", marker.arrayOfObjects()[1]);
+        assertEquals("Check objects [2]", "baz", marker.arrayOfObjects()[2]);
+        assertEquals("Check annotations[0]", "foo", marker.arrayOfAnnotations()[0].subname());
+        
+        // Second parameter (bar), no annotation (invisible)
+        java.lang.annotation.Annotation[] barAnns = annotations[1];
+        assertEquals("Check barAnns length", 0, barAnns.length);
+        
+        // Third parameter (baz), two annotations
+        java.lang.annotation.Annotation[] bazAnns = annotations[2];
+        System.out.println(Arrays.toString(bazAnns));
+        assertEquals("Check bazAnns length", 2, bazAnns.length);
+    }
+    
     private Marker getMarkerAnnotation(java.lang.annotation.Annotation[] annotations) {
         for (int i = 0; i < annotations.length; i++) {
             if (annotations[i].annotationType().getName().equals("org.apache.felix.ipojo.test.scenarios.component.Marker")) {