Fix FELIX-4253 Add methods from inner classes in the metadata collected during the manipulation

Parse the inner class metadata stored in the manifest.

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1527749 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-metadata-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ComponentWithInnerClasses.java b/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-metadata-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ComponentWithInnerClasses.java
new file mode 100644
index 0000000..7eb4dbd
--- /dev/null
+++ b/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-metadata-test/src/main/java/org/apache/felix/ipojo/runtime/core/components/ComponentWithInnerClasses.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, 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.felix.ipojo.runtime.core.components;
+
+import org.apache.felix.ipojo.annotations.Component;
+
+/**
+ **
+ * A component containing inner classes.
+ */
+@Component
+public class ComponentWithInnerClasses{
+
+    public String doSomething() {
+        MyInnerWithANativeMethod nat = new MyInnerWithANativeMethod();
+        MyInnerClass inn = new MyInnerClass();
+        Runnable compute = new Runnable() {
+
+            public void run() {
+                // ...
+            }
+        };
+        return "foo";
+    }
+
+    private String foo = "foo";
+
+    private class MyInnerWithANativeMethod {
+
+        public String foo() {
+            return ComponentWithInnerClasses.this.foo;
+        }
+
+        public native void baz();
+
+    }
+
+    public static class MyStaticInnerClass {
+
+        public static String foo() {
+            return "foo";
+        }
+
+        public String bar() {
+            return "bar";
+        }
+
+        public native void baz();
+    }
+
+    private class MyInnerClass {
+        public String foo() {
+            return ComponentWithInnerClasses.this.foo;
+        }
+    }
+
+
+}
diff --git a/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-metadata-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestManipulationMetadata.java b/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-metadata-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestManipulationMetadata.java
index d469c59..bdb314c 100644
--- a/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-metadata-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestManipulationMetadata.java
+++ b/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-metadata-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestManipulationMetadata.java
@@ -25,11 +25,14 @@
 import org.apache.felix.ipojo.parser.ParseUtils;

 import org.apache.felix.ipojo.runtime.core.services.BarService;

 import org.apache.felix.ipojo.runtime.core.services.FooService;

+import org.junit.Assert;

 import org.junit.Test;

 import org.ow2.chameleon.testing.helpers.BaseTest;

 

 import static junit.framework.Assert.assertEquals;

-import static org.junit.Assert.*;

+import static junit.framework.Assert.assertNotNull;

+import static org.junit.Assert.assertFalse;

+import static org.junit.Assert.fail;

 

 /**

  * Check manipulation metadata written in the manifest.

@@ -199,6 +202,45 @@
         assertEquals("Check return", method.getAttribute("return"), "java.lang.Object");

     }

 

+    @Test

+    public void testInnerClasses() {

+        String comp_name = "org.apache.felix.ipojo.runtime.core.components.ComponentWithInnerClasses";

+        Element manipulation = getManipulationForComponent(comp_name);

+        Element[] inners = manipulation.getElements("inner");

+        assertEquals(inners.length, 3);

+

+        Element inner = getInnerClassMetadataByName(inners, "MyInnerWithANativeMethod");

+        Assert.assertNotNull(inner);

+        Assert.assertNotNull(getMethodByName(inner.getElements("method"), "foo"));

+

+        inner = getInnerClassMetadataByName(inners, "MyInnerClass");

+        assertNotNull(inner);

+        assertNotNull(getMethodByName(inner.getElements("method"), "foo"));

+

+        inner = getInnerClassMetadataByName(inners, "1");

+        assertNotNull(inner);

+        assertNotNull(getMethodByName(inner.getElements("method"), "run"));

+    }

+

+    private static Element getInnerClassMetadataByName(Element[] inners, String name) {

+        for (Element element : inners) {

+            if (name.equals(element.getAttribute("name"))) {

+                return element;

+            }

+        }

+        return null;

+    }

+

+    private static Element getMethodByName(Element[] methods, String name) {

+        for (Element element : methods) {

+            if (name.equals(element.getAttribute("name"))) {

+                return element;

+            }

+        }

+        return null;

+    }

+

+

     private Element getManipulationForComponent(Element metadata, String comp_name) {

         Element[] comps = metadata.getElements("component");

         for (Element comp : comps) {

diff --git a/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-metadata-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestManipulationMetadataAPI.java b/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-metadata-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestManipulationMetadataAPI.java
index 87907d0..ddd60ac 100644
--- a/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-metadata-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestManipulationMetadataAPI.java
+++ b/ipojo/manipulator/manipulator-it/ipojo-manipulator-manipulation-metadata-test/src/test/java/org/apache/felix/ipojo/runtime/core/TestManipulationMetadataAPI.java
@@ -28,7 +28,10 @@
 import org.junit.Test;

 import org.ow2.chameleon.testing.helpers.BaseTest;

 

+import java.util.Arrays;

+

 import static junit.framework.Assert.assertEquals;

+import static junit.framework.Assert.assertNotNull;

 import static org.junit.Assert.*;

 

 public class TestManipulationMetadataAPI extends BaseTest {

@@ -101,6 +104,34 @@
     }

 

     @Test

+    public void testInnerClasses() {

+        String comp_name = "org.apache.felix.ipojo.runtime.core.components.ComponentWithInnerClasses";

+        PojoMetadata metadata = getManipulationMetadataForComponent(comp_name);

+        assertEquals(metadata.getInnerClasses().length, 3);

+        assertNotNull(metadata.getMethodsFromInnerClass("MyInnerWithANativeMethod"));

+        assertNotNull(

+                getMethodMetadata(metadata.getMethodsFromInnerClass("MyInnerWithANativeMethod"),

+                        "foo"));

+

+        assertNotNull(

+                getMethodMetadata(metadata.getMethodsFromInnerClass("MyInnerClass"),

+                        "foo"));

+

+        assertNotNull(

+                getMethodMetadata(metadata.getMethodsFromInnerClass("1"),

+                        "run"));

+    }

+

+    public static MethodMetadata getMethodMetadata(MethodMetadata[] methods, String name) {

+        for (MethodMetadata m : methods) {

+            if (m.getMethodName().equals(name)) {

+                return m;

+            }

+        }

+        return null;

+    }

+

+    @Test

     public void testFields() {

         PojoMetadata manip = FooProviderTypeDyn;

 

@@ -275,7 +306,6 @@
         return null;

     }

 

-

     private PojoMetadata getManipulationMetadataForComponent(String comp_name) {

         String header = (String) getTestBundle().getHeaders().get("iPOJO-Components");

         Element elem = null;

diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/PojoMetadata.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/PojoMetadata.java
index 8b06832..973ba49 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/PojoMetadata.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/parser/PojoMetadata.java
@@ -21,6 +21,8 @@
 import org.apache.felix.ipojo.ConfigurationException;
 import org.apache.felix.ipojo.metadata.Element;
 
+import java.util.*;
+
 /**
  * Manipulation Metadata allows getting information about the implementation class
  * without using reflection such as implemented interfaces, super class,
@@ -44,13 +46,23 @@
     /**
      * The list of methods.
      */
-    private MethodMetadata[] m_methods = new MethodMetadata[0];
+    private List<MethodMetadata> m_methods = new ArrayList<MethodMetadata>();
 
     /**
      * The Super class (if <code>null</code> for {@link Object}).
      */
     private String m_super;
 
+    /**
+     * The manipulated class name.
+     */
+    private String m_className;
+
+    /**
+     * The inner classes and their methods.
+     */
+    private Map<String, List<MethodMetadata>> m_innerClasses = new HashMap<String, List<MethodMetadata>>();
+
 
     /**
      * Creates Pojo metadata.
@@ -65,29 +77,80 @@
             throw new ConfigurationException("The component " + metadata.getAttribute("classname") + " has no manipulation metadata");
         }
         Element manip = elems[0];
+        m_className = manip.getAttribute("classname");
         m_super = manip.getAttribute("super");
+
         Element[] fields = manip.getElements("field");
-        for (int i = 0; fields != null && i < fields.length; i++) {
-            FieldMetadata field = new FieldMetadata(fields[i]);
-            addField(field);
+        if (fields != null) {
+            for (Element field : fields) {
+                addField(new FieldMetadata(field));
+            }
         }
+
         Element[] methods = manip.getElements("method");
-        for (int i = 0; methods != null && i < methods.length; i++) {
-            MethodMetadata method = new MethodMetadata(methods[i]);
-            addMethod(method);
+        if (methods != null) {
+            for (Element method : methods) {
+                m_methods.add(new MethodMetadata(method));
+            }
         }
+
         Element[] itfs = manip.getElements("interface");
-        for (int i = 0; itfs != null && i < itfs.length; i++) {
-            addInterface(itfs[i].getAttribute("name"));
+        if (itfs != null) {
+            for (Element itf : itfs) {
+                addInterface(itf.getAttribute("name"));
+            }
+        }
+
+        Element[] inners = manip.getElements("inner");
+        if (inners != null) {
+            for (Element inner : inners) {
+                String name = inner.getAttribute("name");
+                List<MethodMetadata> list = m_innerClasses.get(name);
+                if (list == null) {
+                    list = new ArrayList<MethodMetadata>();
+                    m_innerClasses.put(name, list);
+                }
+                methods = inner.getElements("method");
+                if (methods != null) {
+                    for (Element m : methods) {
+                        list.add(new MethodMetadata(m));
+                    }
+                }
+            }
         }
     }
 
-    public MethodMetadata[] getMethods() { return m_methods; }
+    public MethodMetadata[] getMethods() { return m_methods.toArray(new MethodMetadata[m_methods.size()]); }
 
     public FieldMetadata[] getFields() { return m_fields; }
 
     public String[] getInterfaces() { return m_interfaces; }
 
+    public String getClassName() { return m_className; }
+
+    /**
+     * Gets the inner classes from the manipulated class
+     * @return the list of the inner class names.
+     */
+    public String[] getInnerClasses() {
+        Set<String> classes = m_innerClasses.keySet();
+        return classes.toArray(new String[classes.size()]);
+    }
+
+    /**
+     * Gets the methods from the given inner class.
+     * @param inner the inner class name
+     * @return the list of method, empty if none.
+     */
+    public MethodMetadata[] getMethodsFromInnerClass(String inner) {
+        List<MethodMetadata> methods = m_innerClasses.get(inner);
+        if (inner != null) {
+            return methods.toArray(new MethodMetadata[methods.size()]);
+        } else {
+            return new MethodMetadata[0];
+        }
+    }
+
     /**
      * Gets the field metadata for the given name.
      * @param name : the name of the field
@@ -137,8 +200,8 @@
      * @return the method metadata object or <code>null</code> if not found
      */
     public MethodMetadata getMethod(String name) {
-        for (int i = 0; i < m_methods.length; i++) {
-            if (m_methods[i].getMethodName().equalsIgnoreCase(name)) { return m_methods[i]; }
+        for (MethodMetadata metadata : m_methods) {
+            if (metadata.getMethodName().equalsIgnoreCase(name)) { return metadata; }
         }
         return null;
     }
@@ -152,20 +215,13 @@
      * @return the Method Metadata array or an empty array if not found
      */
     public MethodMetadata[] getMethods(String name) {
-        MethodMetadata[] mms = new MethodMetadata[0];
-        for (int i = 0; i < m_methods.length; i++) {
-            if (m_methods[i].getMethodName().equalsIgnoreCase(name)) {
-                if (mms.length > 0) {
-                    MethodMetadata[] newInstances = new MethodMetadata[mms.length + 1];
-                    System.arraycopy(mms, 0, newInstances, 0, mms.length);
-                    newInstances[mms.length] = m_methods[i];
-                    mms = newInstances;
-                } else {
-                    mms = new MethodMetadata[] { m_methods[i] };
-                }
+        List<MethodMetadata> list = new ArrayList<MethodMetadata>();
+        for (MethodMetadata metadata : m_methods) {
+            if (metadata.getMethodName().equalsIgnoreCase(name)) {
+                list.add(metadata);
             }
         }
-        return mms;
+        return list.toArray(new MethodMetadata[list.size()]);
     }
 
     /**
@@ -186,15 +242,15 @@
      * @return the Method Metadata or <code>null</code> if not found
      */
     public MethodMetadata getMethod(String name, String[] types) {
-        for (int i = 0; i < m_methods.length; i++) {
-            if (m_methods[i].getMethodName().equalsIgnoreCase(name) && m_methods[i].getMethodArguments().length == types.length) {
+        for (MethodMetadata metadata : m_methods) {
+            if (metadata.getMethodName().equalsIgnoreCase(name) && metadata.getMethodArguments().length == types.length) {
                 int argIndex = 0;
                 for (; argIndex < types.length; argIndex++) {
-                    if (! types[argIndex].equals(m_methods[i].getMethodArguments()[argIndex])) {
+                    if (! types[argIndex].equals(metadata.getMethodArguments()[argIndex])) {
                         break;
                     }
                 }
-                if (argIndex == types.length) { return m_methods[i]; } // No mismatch detected.
+                if (argIndex == types.length) { return metadata; } // No mismatch detected.
             }
         }
         return null;
@@ -210,23 +266,6 @@
     }
 
      /**
-     * Adds a method to the list.
-     * This method is used during the creation of the {@link PojoMetadata}
-     * object.
-     * @param method the Method Metadata to add.
-     */
-    private void addMethod(MethodMetadata method) {
-        if (m_methods.length > 0) {
-            MethodMetadata[] newInstances = new MethodMetadata[m_methods.length + 1];
-            System.arraycopy(m_methods, 0, newInstances, 0, m_methods.length);
-            newInstances[m_methods.length] = method;
-            m_methods = newInstances;
-        } else {
-            m_methods = new MethodMetadata[] { method };
-        }
-    }
-
-     /**
      * Adds a field to the list.
      * This method is used during the creation of the {@link PojoMetadata}
      * object.