New annotation processor. The manipulator manages custom annotations (provided by external handlers).

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@579994 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Attribute.java b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Attribute.java
deleted file mode 100644
index 482f693..0000000
--- a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Attribute.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/* 

- * 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.annotations;

-

-/**

-* This annotation declares an attribute.

-* This annotation must only be used in the attributes array of the Element annotation.

-* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

-*/

-public @interface Attribute {

-    

-    /**

-     * Set attribute name.

-     */

-    String name();

-    

-    /**

-     * Set attribute value.

-     */

-    String value();

-

-}

diff --git a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Controller.java b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Controller.java
index e449cd4..e4bfaeb 100644
--- a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Controller.java
+++ b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Controller.java
@@ -28,6 +28,5 @@
 @Target(ElementType.FIELD)

 public @interface Controller {

     

-    

 

 }

diff --git a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Element.java b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Element.java
deleted file mode 100644
index 904d5b9..0000000
--- a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Element.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/* 

- * 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.annotations;

-

-import java.lang.annotation.ElementType;

-import java.lang.annotation.Target;

-

-/**

-* This annotation declares a generic element.

-* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

-*/

-@Target(ElementType.TYPE)

-public @interface Element {

-    

-    /**

-     * Set element name.

-     */

-    String name();

-    

-    /**

-     * Set the element namespace.

-     * Default : no namespace.

-     */

-    String namespace() default "";

-    

-    /**

-     * Set element attributes (@Attribute).

-     * Default : no attributes.

-     */

-    Attribute[] attributes() default { };

-    

-    /**

-     * Set element sub-elements (@SubElement).

-     * Default : no sub-element.

-     */

-    SubElement[] elements() default { };

-

-}

diff --git a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Provides.java b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Provides.java
index 3ea29b6..1b25d05 100644
--- a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Provides.java
+++ b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/Provides.java
@@ -32,7 +32,7 @@
      * Set the provided specifications.

      * Default : all implemented interfaces

      */

-    String specification() default "";

+    Class[] specifications() default { };

     

     /**

      * Set the service object creation policy.

diff --git a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/SubElement.java b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/SubElement.java
deleted file mode 100644
index a0d3179..0000000
--- a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/SubElement.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/* 

- * 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.annotations;

-

-/**

-* This annotation declares a sub-attribute.

-* This annotation must only be used in the elements array of the Element annotation.

-* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

-*/

-public @interface SubElement {

-    

-    /**

-     * Set the sub-element name.

-     */

-    String name();

-    

-    /**

-     * Set the sub-element namespace.

-     * Default : no namespace.

-     */

-    String namespace() default "";

-    

-    /**

-     * Set the sub-element attributes (@Attribute).

-     * Default : no attributes.

-     */

-    Attribute[] attributes() default { };

-

-}

diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/CustomAnnotationVisitor.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/CustomAnnotationVisitor.java
new file mode 100644
index 0000000..d4534c4
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/CustomAnnotationVisitor.java
@@ -0,0 +1,237 @@
+/* 

+ * 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.manipulation.annotations;

+

+import java.lang.reflect.Array;

+

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

+import org.apache.felix.ipojo.metadata.Element;

+import org.objectweb.asm.AnnotationVisitor;

+import org.objectweb.asm.commons.EmptyVisitor;

+

+/**

+ * Collect metadata from custom annotation.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class CustomAnnotationVisitor extends EmptyVisitor implements AnnotationVisitor {

+

+    /**

+     * PArent element.

+     */

+    private Element m_elem;

+

+    /**

+     * Id attribute (if found).

+     */

+    private String m_id;

+

+    /**

+     * Parent attribute (if found).

+     */

+    private String m_parent;

+

+    /**

+     * Is the custom annotation a first-order annotation.

+     */

+    private boolean m_root;

+

+    /**

+     * MEtadata collector.

+     */

+    private MetadataCollector m_collector;

+    

+    /**

+     * Constructor.

+     * @param elem : parent element

+     * @param collector : metadata collector

+     * @param root : is the annotation a root

+     */

+    public CustomAnnotationVisitor(Element elem, MetadataCollector collector, boolean root) {

+        m_elem = elem;

+        m_root = root;

+        m_collector = collector;

+    }

+    

+    /**

+     * Check if the given annotation descriptor is an iPOJO custom annotation.

+     * A valid iPOJO custom annotation must contains 'ipojo' or 'handler' in its qualified name.

+     * @param desc : annotation descriptor

+     * @return : true if the given descriptor is an iPOJO custom annotation

+     */

+    public static boolean isCustomAnnotation(String desc) {

+        desc = desc.toLowerCase();

+        if (desc.indexOf("ipojo") != -1 || desc.indexOf("handler") != -1) {

+            return true;

+        }

+        return false;

+    }

+    

+    /**

+     * Build the element object from the given descriptor.

+     * @param desc : annotation descriptor

+     * @return : the element

+     */

+    public static Element buildElement(String desc) {

+        String s = (desc.replace('/', '.')).substring(1, desc.length() - 1);

+        int index = s.lastIndexOf('.');

+        String name = s.substring(index + 1);

+        String namespace = s.substring(0, index);

+        return new Element(name, namespace);

+    }

+

+    /**

+     * Visit an 'simple' annotation attribute.

+     * This method is used for primitive arrays too. 

+     * @param arg0 : attribute name

+     * @param arg1 : attribute value

+     * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)

+     */

+    public void visit(String arg0, Object arg1) {

+        if (arg1.getClass().isArray()) {

+            // Primitive arrays case

+            String v = null;

+            int index = Array.getLength(arg1);

+            for (int i = 0; i < index; i++) {

+                if (v == null) {

+                    v = "{" + Array.get(arg1, i);

+                } else {

+                    v += "," + Array.get(arg1, i);

+                }

+            }

+            v += "}";

+            m_elem.addAttribute(new Attribute(arg0, v));

+            return;

+        }

+        // Attributes are added as normal attributes

+        m_elem.addAttribute(new Attribute(arg0, arg1.toString()));

+        if (m_root) {

+            if (arg0.equals("id")) {

+                m_id = arg1.toString();

+            } else if (arg0.equals("parent")) {

+                m_parent = arg1.toString();

+            }

+        }

+    }

+

+    /**

+     * Visit a sub-annotation.

+     * @param arg0 : attribute name.

+     * @param arg1 : annotation description

+     * @return an annotation visitor which will visit the given annotation

+     * @see org.objectweb.asm.commons.EmptyVisitor#visitAnnotation(java.lang.String, java.lang.String)

+     */

+    public AnnotationVisitor visitAnnotation(String arg0, String arg1) {

+        // Sub annotations are mapped to sub-elements

+        Element elem = buildElement(arg1);

+        m_elem.addElement(elem);

+        return new CustomAnnotationVisitor(elem, m_collector, false);

+    }

+

+    /**

+     * Visit an array attribute.

+     * @param arg0 : attribute name

+     * @return a visitor which will visit each element of the array

+     * @see org.objectweb.asm.commons.EmptyVisitor#visitArray(java.lang.String)

+     */

+    public AnnotationVisitor visitArray(String arg0) {

+        return new SubArrayVisitor(m_elem, arg0);

+    }

+

+    /**

+     * End of the visit.

+     * All attribute was visited, we can update collectors data.

+     * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()

+     */

+    public void visitEnd() {

+        if (m_root) {

+            if (m_id != null) {

+                m_collector.getIds().put(m_id, m_elem);

+            }

+            m_collector.getElements().put(m_elem, m_parent);

+        }

+    }

+

+    private class SubArrayVisitor extends EmptyVisitor implements AnnotationVisitor {

+        /**

+         * Parent element.

+         */

+        private Element m_elem;

+

+        /**

+         * Attribute name.

+         */

+        private String m_name;

+

+        /**

+         * Attribute value.

+         * (accumulator)

+         */

+        private String m_acc;

+

+        /**

+         * Constructor.

+         * @param elem : parent element.

+         * @param name : attribute name.

+         */

+        public SubArrayVisitor(Element elem, String name) {

+            m_elem = elem;

+            m_name = name;

+        }

+

+        /**

+         * Visit a 'simple' element of the visited array.

+         * @param arg0 : null

+         * @param arg1 : element value.

+         * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)

+         */

+        public void visit(String arg0, Object arg1) {

+            if (m_acc == null) {

+                m_acc = "{" + arg1.toString();

+            } else {

+                m_acc = m_acc + "," + arg1.toString();

+            }

+        }

+

+        /**

+         * Visit an annotation element of the visited array.

+         * @param arg0 : null

+         * @param arg1 : annotation to visit

+         * @return the visitor which will visit the annotation

+         * @see org.objectweb.asm.commons.EmptyVisitor#visitAnnotation(java.lang.String, java.lang.String)

+         */

+        public AnnotationVisitor visitAnnotation(String arg0, String arg1) {

+            // Sub annotations are map to sub-elements

+            Element elem = buildElement(arg1);

+            m_elem.addElement(elem);

+            return new CustomAnnotationVisitor(elem, m_collector, false);

+        }

+

+        /**

+         * End of the visit.

+         * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()

+         */

+        public void visitEnd() {

+            if (m_acc != null) {

+                // We have analyzed an attribute

+                m_elem.addAttribute(new Attribute(m_name, m_acc + "}"));

+            }

+        }

+

+    }

+}

diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/FieldCollector.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/FieldCollector.java
index 4ecdc26..f6ecd63 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/FieldCollector.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/FieldCollector.java
@@ -33,7 +33,7 @@
     /**

      * Collected element.

      */

-    private Element m_element;

+    private MetadataCollector m_collector;

     

     /**

      * Field name. 

@@ -43,10 +43,10 @@
     /**

      * Constructor.

      * @param fieldName : field name

-     * @param elem : element on which append the collected metadata.

+     * @param collector : metadata collector.

      */

-    public FieldCollector(String fieldName, Element elem) {

-        m_element = elem;

+    public FieldCollector(String fieldName, MetadataCollector collector) {

+        m_collector = collector;

         m_field = fieldName;

     }

 

@@ -61,23 +61,41 @@
         if (arg0.equals("Lorg/apache/felix/ipojo/annotations/Requires;")) {

             return new RequiresAnnotationParser(m_field);

         }

+        if (arg0.equals("Lorg/apache/felix/ipojo/annotations/Controller;")) {

+            Element elem = new Element("controller", "");

+            elem.addAttribute(new Attribute("field", m_field));

+            m_collector.getElements().put(elem, null);

+            return null;

+        }

         if (arg0.equals("Lorg/apache/felix/ipojo/annotations/ServiceProperty;")) {

-            if (m_element.getElements("Provides", "").length == 0) {

+            if (! m_collector.getIds().containsKey("provides")) { // The provides annotation is already computed.

                 System.err.println("The component does not provide services, skip ServiceProperty for " + m_field);

                 return null;

+            } else {

+                // Get the provides element

+                Element parent = (Element) m_collector.getIds().get("provides");

+                return new PropertyAnnotationParser(m_field, parent);

             }

-            Element provides = m_element.getElements("Provides", "")[0];

-            return new PropertyAnnotationParser(provides, m_field);

+            

         }

         if (arg0.equals("Lorg/apache/felix/ipojo/annotations/Property;")) {

             Element parent = null;

-            if (m_element.getElements("Properties", "").length == 0) {

+            if (! m_collector.getIds().containsKey("properties")) {

                 parent = new Element("Properties", "");

-                m_element.addElement(parent);

+                m_collector.getIds().put("properties", parent);

+                m_collector.getElements().put(parent, null);

+            } else {

+                parent = (Element) m_collector.getIds().get("properties");

             }

-            parent = m_element.getElements("Properties", "")[0];

-            return new PropertyAnnotationParser(parent, m_field);

+            return new PropertyAnnotationParser(m_field, parent);

         }

+        

+        if (CustomAnnotationVisitor.isCustomAnnotation(arg0)) {

+            Element elem = CustomAnnotationVisitor.buildElement(arg0);

+            elem.addAttribute(new Attribute("field", m_field)); // Add a field attribute

+            return new CustomAnnotationVisitor(elem, m_collector, true);

+        }

+        

         return null;

        

     }

@@ -155,20 +173,18 @@
          * Create a "requires" element

          * @see org.objectweb.asm.AnnotationVisitor#visitEnd()

          */

-        public void visitEnd() { 

-         // Check if it is a full-determined requirement

+        public void visitEnd() {

             Element req = null;

-            Element[] reqs = m_element.getElements("requires");

-            for (int i = 0; i < reqs.length; i++) {

-                if (reqs[i].containsAttribute("id") && (reqs[i].getAttribute("id").equals(m_id) || reqs[i].getAttribute("id").equals(m_field))) {

-                    req = reqs[i];

-                    break;

-                }

+            if (m_id == null) {

+                req = (Element) m_collector.getIds().get(m_field);

+            } else {

+                req = (Element) m_collector.getIds().get(m_id);

             }

+

             if (req == null) {

-                // Add the complete requires

                 req = new Element("requires", "");

             }

+

             req.addAttribute(new Attribute("field", m_field));

             if (m_specification != null) {

                 req.addAttribute(new Attribute("interface", m_specification));

@@ -185,7 +201,15 @@
             if (m_id != null) {

                 req.addAttribute(new Attribute("id", m_id));

             }

-            m_element.addElement(req);

+            

+            if (m_id != null) { 

+                m_collector.getIds().put(m_id, req);

+            } else {

+                m_collector.getIds().put(m_field, req);

+            }

+            

+            m_collector.getElements().put(req, null);

+                

             return;

         }

     }

@@ -196,7 +220,7 @@
     private final class PropertyAnnotationParser extends EmptyVisitor implements AnnotationVisitor {

         

         /**

-         * Parent element (element on which append collected metadata).

+         * Parent element element.

          */

         private Element m_parent;

         

@@ -221,7 +245,7 @@
          * @param parent : parent element.

          * @param field : field name.

          */

-        private PropertyAnnotationParser(Element parent, String field) {

+        private PropertyAnnotationParser(String field, Element parent) {

             m_parent = parent;

             m_field = field;

         }

diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MetadataCollector.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MetadataCollector.java
index c091cb1..e2ed9ea 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MetadataCollector.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MetadataCollector.java
@@ -18,12 +18,18 @@
  */

 package org.apache.felix.ipojo.manipulation.annotations;

 

+import java.util.HashMap;

+import java.util.Iterator;

+import java.util.Map;

+import java.util.Set;

+

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

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

 import org.objectweb.asm.AnnotationVisitor;

 import org.objectweb.asm.FieldVisitor;

 import org.objectweb.asm.MethodVisitor;

 import org.objectweb.asm.Opcodes;

+import org.objectweb.asm.Type;

 import org.objectweb.asm.commons.EmptyVisitor;

 

 /**

@@ -48,6 +54,19 @@
      */

     private boolean m_containsAnnotation = false;

     

+    /**

+     * Map of [element ids, element].

+     * This map is used to easily get an already created element. 

+     */

+    private Map m_ids = new HashMap();

+    

+    /**

+     * Map of [element, referto].

+     * This map is used to recreate the element hierarchie.

+     * Stored element are added under referred element. 

+     */

+    private Map m_elements = new HashMap();

+    

     public Element getElem() {

         return m_elem;

     }

@@ -55,6 +74,7 @@
     public boolean isAnnotated() {

         return m_containsAnnotation;

     }

+    

 

     /**

      * Start visiting a class.

@@ -68,6 +88,8 @@
      * @see org.objectweb.asm.ClassAdapter#visit(int, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])

      */

     public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {

+        m_ids = new HashMap();

+        m_elements = new HashMap();

         m_className = name;

     }

 

@@ -94,13 +116,15 @@
             return new ProvidesVisitor();

         }

         

-        //@Element

-        if (desc.equals("Lorg/apache/felix/ipojo/annotations/Element;")) {

-            return new ElementVisitor(m_elem);

+        if (CustomAnnotationVisitor.isCustomAnnotation(desc)) {

+            Element elem = CustomAnnotationVisitor.buildElement(desc);

+            return new CustomAnnotationVisitor(elem, this, true);

         }

         

         return null;

     }

+    

+

 

     /**

      * Visit a field.

@@ -114,7 +138,7 @@
      * @see org.objectweb.asm.ClassAdapter#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)

      */

     public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {

-        return new FieldCollector(name, m_elem);

+        return new FieldCollector(name, this);

     }

 

     /**

@@ -129,10 +153,42 @@
      * @see org.objectweb.asm.ClassAdapter#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])

      */

     public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {

-        return new MethodCollector(name, m_elem);

+        return new MethodCollector(name, this);

     }

     

     /**

+     * End of the visit : compute final elements.

+     * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()

+     */

+    public void visitEnd() {

+        // Recompute the tree

+        Set elems = getElements().keySet();

+        Iterator it = elems.iterator();

+        while (it.hasNext()) {

+            Element current = (Element) it.next();

+            String reference = (String) getElements().get(current);

+            if (reference == null) {

+                m_elem.addElement(current);

+            } else {

+                Element ref = (Element) getIds().get(reference);

+                if (ref == null) {

+                    System.err.println("The element " + reference + " is not declared - skip the element " + current.toXMLString());

+                } else {

+                    ref.addElement(current);

+                }

+            }

+        }

+    }

+

+    protected Map getIds() {

+        return m_ids;

+    }

+

+    protected Map getElements() {

+        return m_elements;

+    }

+

+    /**

      * Parse the @provides annotation.

      */

     private class ProvidesVisitor extends EmptyVisitor implements AnnotationVisitor {

@@ -148,21 +204,64 @@
          * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)

          */

         public void visit(String arg0, Object arg1) {

-            if (arg0.equals("specifications")) {

-                m_prov.addAttribute(new Attribute("interface", arg1.toString()));

-            }

             if (arg0.equals("factory")) {

                 m_prov.addAttribute(new Attribute("factory", arg1.toString()));

             }

         }

         

         /**

+         * Visit specifications array.

+         * @param arg0 : attribute name

+         * @return a visitor visiting each element of the array.

+         * @see org.objectweb.asm.commons.EmptyVisitor#visitArray(java.lang.String)

+         */

+        public AnnotationVisitor visitArray(String arg0) {

+            if (arg0.equals("specifications")) {

+                return new InterfaceArrayVisitor();

+            } else {

+                return null;

+            }

+        }

+        

+        /**

          * End of the visit.

          * Append to the parent element the computed "provides" element.

          * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()

          */

         public void visitEnd() {

-            m_elem.addElement(m_prov);            

+            getIds().put("provides", m_prov);

+            getElements().put(m_prov, null);

+        }

+        

+        private class InterfaceArrayVisitor extends EmptyVisitor {

+            /**

+             * List of parsed interface.

+             */

+            private String m_itfs;

+            

+            /**

+             * Visit one element of the array.

+             * @param arg0 : null

+             * @param arg1 : element value.

+             * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)

+             */

+            public void visit(String arg0, Object arg1) {

+                if (m_itfs == null) {

+                    m_itfs = "{" + ((Type) arg1).getClassName();

+                } else {

+                    m_itfs += "," + ((Type) arg1).getClassName();

+                }

+            }

+            

+            /**

+             * End of the array visit.

+             * Add the attribute to 'provides' element.

+             * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()

+             */

+            public void visitEnd() {

+                m_prov.addAttribute(new Attribute("interface", m_itfs + "}"));

+            }

+            

         }

         

     }

@@ -250,164 +349,9 @@
             if (m_propagation != null) {

                 Element props = new Element("properties", "");

                 props.addAttribute(new Attribute("propagation", m_propagation));

-                m_elem.addElement(props);

+                getIds().put("properties", props);

+                getElements().put(props, null);

             }

         }        

     }

-

-    /**

-     * Parse the @Element & @SubElement annotations. 

-     */

-    private class ElementVisitor extends EmptyVisitor implements AnnotationVisitor {

-        

-        /**

-         * Element name.

-         */

-        private String m_name;

-        

-        /**

-         * Element namespace. 

-         */

-        private String m_namespace;

-        

-        /**

-         * Parent Element. 

-         */

-        private Element m_parent;

-        

-        /**

-         * Accumulator element to store temporary attributes and sub-elements.

-         */

-        private Element m_accu = new Element("accu", "");

-        

-        /**

-         * Constructor.

-         * @param parent : parent element.

-         */

-        public ElementVisitor(Element parent) {

-            m_parent = parent;

-        }

-

-        /**

-         * Visit annotation attribute.

-         * @param arg0 : name of the attribute

-         * @param arg1 : value of the attribute

-         * @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object)

-         */

-        public void visit(String arg0, Object arg1) {

-            if (arg0.equals("name")) {

-                m_name = arg1.toString();

-            }

-            if (arg0.equals("namespace")) {

-                m_namespace = arg1.toString();

-            }

-        }

-

-        

-        /**

-         * Visit array annotation attribute (attributes & elements).

-         * @param arg0 : attribute name

-         * @return the annotation visitor which will visit the content of the array

-         * @see org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String)

-         */

-        public AnnotationVisitor visitArray(String arg0) {

-            if (arg0.equals("attributes")) {

-                return new EmptyVisitor() {

-                    public AnnotationVisitor visitAnnotation(String arg0, String arg1) {

-                        return new AttributeVisitor(m_accu);

-                    }

-                    

-                };

-            }

-            if (arg0.equals("elements")) {

-                return new EmptyVisitor() {

-                    public AnnotationVisitor visitAnnotation(String arg0, String arg1) {

-                        return new ElementVisitor(m_accu);

-                    }

-                    

-                };

-            }

-            return null;

-        }

-

-        /**

-         * End of the visit.

-         * Append computed element to the parent element.

-         * @see org.objectweb.asm.AnnotationVisitor#visitEnd()

-         */

-        public void visitEnd() {

-            Element elem = null;

-            if (m_namespace != null) {

-                elem = new Element(m_name, m_namespace);

-            } else {

-                elem = new Element(m_name, "");

-            }

-

-            Attribute[] atts = m_accu.getAttributes();

-            for (int i = 0; i < atts.length; i++) {

-                elem.addAttribute(atts[i]);

-            }

-

-            Element[] elems = m_accu.getElements();

-            for (int i = 0; i < elems.length; i++) {

-                elem.addElement(elems[i]);

-            }

-

-            m_parent.addElement(elem);

-        }

-        

-    }

-    

-    /**

-     * Parse an @attribute annotation.

-     */

-    private class AttributeVisitor extends EmptyVisitor implements AnnotationVisitor {

-        /**

-         * Parent element.

-         */

-        private Element m_parent;

-

-        /**

-         * Attribute name.

-         */

-        private String m_name;

-        

-        /**

-         * Attribute value. 

-         */

-        private String m_value;

-        

-        /**

-         * Constructor.

-         * @param parent : parent element.

-         */

-        public AttributeVisitor(Element parent) {

-            m_parent = parent;

-        }

-

-        /**

-         * Visit attributes. 

-         * @param arg0 : attribute name

-         * @param arg1 : attribute value

-         * @see org.objectweb.asm.commons.EmptyVisitor#visit(java.lang.String, java.lang.Object)

-         */

-        public void visit(String arg0, Object arg1) {

-            if (arg0.equals("name")) {

-                m_name = arg1.toString();

-                return;

-            }

-            if (arg0.equals("value")) {

-                m_value = arg1.toString();

-            }

-        }

-

-        /**

-         * End of the visit.

-         * Append this current attribute to the parent element.

-         * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()

-         */

-        public void visitEnd() {

-            m_parent.addAttribute(new Attribute(m_name, m_value));

-        }        

-    }

 }

diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MethodCollector.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MethodCollector.java
index 582e5e6..2ffa9da 100644
--- a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MethodCollector.java
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/MethodCollector.java
@@ -30,9 +30,9 @@
 public class MethodCollector extends EmptyVisitor {

 

     /**

-     * Parent element (component).

+     * Parent collector.

      */

-    private Element m_element;

+    private MetadataCollector m_collector;

     

     /**

      * Method name. 

@@ -42,10 +42,10 @@
     /**

      * Constructor.

      * @param name : name of the method.

-     * @param element : parent element.

+     * @param collector : parent collector.

      */

-    public MethodCollector(String name, Element element) {

-        m_element = element;

+    public MethodCollector(String name, MetadataCollector collector) {

+        m_collector = collector;

         m_name = name;

     }

 

@@ -75,6 +75,13 @@
         if (arg0.equals("Lorg/apache/felix/ipojo/annotations/Unbind;")) {

             return processBind("unbind");

         }

+        

+        if (CustomAnnotationVisitor.isCustomAnnotation(arg0)) {

+            Element elem = CustomAnnotationVisitor.buildElement(arg0);

+            elem.addAttribute(new Attribute("method", m_name));

+            return new CustomAnnotationVisitor(elem, m_collector, true);

+        }

+        

         return null;

     }

 

@@ -95,7 +102,7 @@
         Element cb = new Element("callback", "");

         cb.addAttribute(new org.apache.felix.ipojo.metadata.Attribute("transition", "validate"));

         cb.addAttribute(new org.apache.felix.ipojo.metadata.Attribute("method", m_name));

-        m_element.addElement(cb);

+        m_collector.getElements().put(cb, null);

         return null;

     }

 

@@ -107,7 +114,7 @@
         Element cb = new Element("callback", "");

         cb.addAttribute(new org.apache.felix.ipojo.metadata.Attribute("transition", "invalidate"));

         cb.addAttribute(new org.apache.felix.ipojo.metadata.Attribute("method", m_name));

-        m_element.addElement(cb);

+        m_collector.getElements().put(cb, null);

         return null;

     }

 

@@ -116,12 +123,13 @@
      * @return the visitor parsing the visited annotation.

      */

     private AnnotationVisitor processServiceProperty() {

-        if (m_element.getElements("Provides", "").length == 0) {

+        if (! m_collector.getIds().containsKey("provides")) {

             System.err.println("the component does not provide services, skip ServiceProperty for " + m_name);

             return null;

+        } else {

+            Element provides = (Element) m_collector.getIds().get("provides");

+            return new PropertyAnnotationParser(provides, m_name);

         }

-        Element provides = m_element.getElements("Provides", "")[0];

-        return new PropertyAnnotationParser(provides, m_name);

     }

 

     /**

@@ -129,11 +137,15 @@
      * @return the visitor parsing the visited annotation.

      */

     private AnnotationVisitor processProperty() {

-        if (m_element.getElements("Properties", "").length == 0) {

-            m_element.addElement(new Element("Properties", ""));

+        Element prop = null;

+        if (! m_collector.getIds().containsKey("properties")) {

+            prop = new Element("Properties", "");

+            m_collector.getIds().put("properties", prop);

+            m_collector.getElements().put(prop, null);

+        } else {

+            prop = (Element) m_collector.getIds().get("properties");

         }

-        Element props = m_element.getElements("Properties", "")[0];

-        return new PropertyAnnotationParser(props, m_name);

+        return new PropertyAnnotationParser(prop, m_name);

     }

 

     /**

@@ -232,18 +244,7 @@
                 }

             }

             // Check if it is a full-determined requirement

-            Element req = null;

-            Element[] reqs = m_element.getElements("requires");

-            for (int i = 0; i < reqs.length; i++) {

-                if (reqs[i].containsAttribute("id") && reqs[i].getAttribute("id").equals(m_id)) {

-                    req = reqs[i];

-                    break;

-                }

-                if (reqs[i].containsAttribute("field") && reqs[i].getAttribute("field").equals(m_id)) {

-                    req = reqs[i];

-                    break;

-                }

-            }

+            Element req = (Element) m_collector.getIds().get(m_id);

             if (req == null) {

                 // Add the complete requires

                 req = new Element("requires", "");

@@ -267,7 +268,8 @@
             method.addAttribute(new Attribute("method", m_name));

             method.addAttribute(new Attribute("type", m_type));

             req.addElement(method);

-            m_element.addElement(req);

+            m_collector.getIds().put(m_id, req);

+            m_collector.getElements().put(req, null);

             return;

         }

     }