Fix FELIX-2622 Support static service properties that are not mirrored into fields
Introduce a new StaticServiceProperty annotation:
@Component
@Provides(
		specifications= {FooService.class, BarService.class},
		properties= {
				@StaticServiceProperty(name="prop1", value="prop1", type="java.lang.String"),
				@StaticServiceProperty(name="prop2", type="java.lang.String"),
				@StaticServiceProperty(name="props", value="{prop1, prop2}", type="string[]"),
				@StaticServiceProperty(name="mandatory1", mandatory=true, type="string")
		})
		
To improve the error management, we can't use the ServiceProperty annotation as the type attribute is mandatory.		


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1001175 13f79535-47bb-0310-9956-ffa450edef68
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 a2780d7..c58c3ef 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
@@ -1,4 +1,4 @@
-/* 

+/*

  * 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

@@ -27,16 +27,16 @@
 */

 @Target(ElementType.TYPE)

 public @interface Provides {

-    

+

     /**

      * Set the provided specifications.

      * Default : all implemented interfaces

      */

     Class[] specifications() default { };

-    

+

     /**

      * Set the service object creation strategy.

-     * Two value are possible: SINGLETON, SERVICE, METHOD, INSTANCE or the strategy class name. 

+     * Two value are possible: SINGLETON, SERVICE, METHOD, INSTANCE or the strategy class name.

      * SERVICE means OSGi Service Factory.

      * METHOD delegates the creation to the factory-method of the component

      * INSTANCE creates one service object per requiring instance

@@ -44,4 +44,14 @@
      * Default : SINGLETON

      */

     String strategy() default "SINGLETON";

+

+    /**

+     * Allows adding static properties to the service.

+     * Nested properties are static service properties,

+     * so <b>must</b> contain the name and the value as

+     * they are not attached to a field.

+     * The array contains {@link StaticServiceProperty} elements.

+     * Default : No service properties

+     */

+    StaticServiceProperty[] properties() default {};

 }

diff --git a/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/StaticServiceProperty.java b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/StaticServiceProperty.java
new file mode 100644
index 0000000..f9ac981
--- /dev/null
+++ b/ipojo/annotations/src/main/java/org/apache/felix/ipojo/annotations/StaticServiceProperty.java
@@ -0,0 +1,51 @@
+/*

+ * 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 static service property.

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

+ */

+public @interface StaticServiceProperty {

+

+    /**

+     * Set the property name.

+     * This name is mandatory for static properties.

+     */

+    String name();

+

+    /**

+     * Set the property value.

+     * Default : empty

+     */

+    String value() default "";

+

+    /**

+     * Is the property mandatory?

+     * Default: false

+     */

+    boolean mandatory() default false;

+

+    /**

+     * Set the type.

+     * This value is required only for static properties.

+     */

+    String type();

+}

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 2d4e7d8..31905d6 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
@@ -291,106 +291,6 @@
     }

     

     /**

-     * Parses a Property annotation.

-     */

-    private static final class PropertyAnnotationParser extends EmptyVisitor implements AnnotationVisitor {

-        

-        /**

-         * Parent element element.

-         */

-        private Element m_parent;

-        

-        /**

-         * Field name. 

-         */

-        private String m_field;

-        

-        /**

-         * Property name. 

-         */

-        private String m_name;

-        

-        /**

-         * Property value.  

-         */

-        private String m_value;

-        

-        /**

-         * Property mandatory aspect.

-         */

-        private String m_mandatory;

-        

-        

-        /**

-         * Constructor.

-         * @param parent : parent element.

-         * @param field : field name.

-         */

-        private PropertyAnnotationParser(String field, Element parent) {

-            m_parent = parent;

-            m_field = field;

-        }

-

-        /**

-         * Visit one "simple" annotation.

-         * @param arg0 : annotation name

-         * @param arg1 : annotation value

-         * @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();

-                return;

-            }

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

-                m_value = arg1.toString();

-                return;

-            }

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

-                m_mandatory = arg1.toString();

-                return;

-            }

-        }

-

-        /**

-         * End of the annotation.

-         * Create a "property" element

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

-         */

-        public void visitEnd() {

-            if (m_name == null) {

-                m_name = m_field;

-            }

-            

-            Element[] props = m_parent.getElements("Property");

-            Element prop = null;

-            for (int i = 0; prop == null && props != null && i < props.length; i++) {

-                String name = props[i].getAttribute("name");

-                if (name != null && name.equals(m_name)) {

-                    prop = props[i];

-                }

-            }

-            

-            if (prop == null) {

-                prop = new Element("property", "");

-                m_parent.addElement(prop);

-                if (m_name != null) { 

-                    prop.addAttribute(new Attribute("name", m_name));

-                }

-            }

-            

-            prop.addAttribute(new Attribute("field", m_field));

-            if (m_value != null) {

-                prop.addAttribute(new Attribute("value", m_value));

-            }

-            if (m_mandatory != null) {

-                prop.addAttribute(new Attribute("mandatory", m_mandatory));

-            }

-            

-        }

-    }

-    

-    /**

      * Parses a ServiceController annotation.

      */

     private static final class ServiceControllerAnnotationParser extends EmptyVisitor implements AnnotationVisitor {

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 1543052..aaa8767 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
@@ -1,4 +1,4 @@
-/* 
+/*
  * 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
@@ -64,9 +64,9 @@
      * True if the visited class is a component type declaration (i.e. contains the @component annotation).
      */
     private boolean m_containsComponentAnnotation = false;
-    
+
     private boolean m_ignoredBecauseOfMissingComponent = false;
-    
+
     /**
      * Map of [element ids, element].
      * This map is used to easily get an already created element.
@@ -79,7 +79,7 @@
      * Stored element are added under referred element.
      */
     private Map m_elements = new HashMap();
-    
+
     /**
      * Instance declaration.
      */
@@ -94,23 +94,23 @@
     public Element getComponentTypeDeclaration() {
         return m_elem;
     }
-    
+
     public Element getInstanceDeclaration() {
         return m_instance;
     }
-    
+
     public boolean isComponentType() {
         return m_containsComponentAnnotation;
     }
-    
+
     public boolean isIgnoredBecauseOfMissingComponent() {
         return m_ignoredBecauseOfMissingComponent;
     }
-    
+
     public String getClassName() {
         return m_className;
     }
-    
+
     /**
      * Start visiting a class.
      * Initialize the getter/setter generator, add the _cm field, add the pojo interface.
@@ -165,7 +165,7 @@
         if (desc.equals("Lorg/apache/felix/ipojo/annotations/HandlerDeclaration;")) {
             return new HandlerDeclarationVisitor();
         }
-        
+
         // @Instantiate
         if (desc.equals("Lorg/apache/felix/ipojo/annotations/Instantiate;")) {
             return new InstantiateVisitor();
@@ -220,7 +220,7 @@
             m_ignoredBecauseOfMissingComponent = true;
             return;
         }
-        
+
         // Recompute the tree
         Set elems = getElements().keySet();
         Iterator it = elems.iterator();
@@ -308,6 +308,14 @@
         public AnnotationVisitor visitArray(String arg0) {
             if (arg0.equals("specifications")) {
                 return new InterfaceArrayVisitor();
+            } else if (arg0.equals("properties")) {
+            	// Create a new simple visitor to visit the nested ServiceProperty annotations
+            	// Collected properties are collected in m_prov
+            	return new EmptyVisitor() {
+            		public AnnotationVisitor visitAnnotation(String ignored, String desc) {
+                    	return new PropertyAnnotationParser(m_prov);
+        			}
+            	};
             } else {
                 return null;
             }
@@ -354,17 +362,18 @@
 
         }
 
+
     }
-    
+
     /**
      * Parse the @Instantitate annotation.
      */
     private class InstantiateVisitor extends EmptyVisitor implements AnnotationVisitor {
         /**
-         * Instance name. 
+         * Instance name.
          */
         private String m_name;
-        
+
         /**
          * Visit an annotation attribute.
          * @param arg0 the attribute name
@@ -377,7 +386,7 @@
                 return;
             }
         }
-        
+
         /**
          * End of the visit. Creates the instance element.
          * @see org.objectweb.asm.commons.EmptyVisitor#visitEnd()
@@ -393,7 +402,7 @@
         }
     }
 
-    
+
     /**
      * Parse the @component annotation.
      */
diff --git a/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/PropertyAnnotationParser.java b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/PropertyAnnotationParser.java
new file mode 100644
index 0000000..548fd23
--- /dev/null
+++ b/ipojo/manipulator/src/main/java/org/apache/felix/ipojo/manipulation/annotations/PropertyAnnotationParser.java
@@ -0,0 +1,150 @@
+/*
+ * 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 org.apache.felix.ipojo.metadata.Attribute;
+import org.apache.felix.ipojo.metadata.Element;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+/**
+ * Parses a Property or ServiceProperty annotation.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+class PropertyAnnotationParser extends EmptyVisitor implements AnnotationVisitor {
+
+    /**
+     * Parent element element.
+     */
+    private Element m_parent;
+
+    /**
+     * Field name.
+     */
+    private String m_field;
+
+    /**
+     * Property name.
+     */
+    private String m_name;
+
+    /**
+     * Property value.
+     */
+    private String m_value;
+
+    /**
+     * Property mandatory aspect.
+     */
+    private String m_mandatory;
+
+    /**
+     * Property type.
+     */
+    private String m_type;
+
+
+    /**
+     * Constructor.
+     * @param parent : parent element.
+     * @param field : field name.
+     */
+    PropertyAnnotationParser(String field, Element parent) {
+        m_parent = parent;
+        m_field = field;
+    }
+
+    /**
+     * Constructor without field
+     * @param parent : parent element..
+     */
+    PropertyAnnotationParser(Element parent) {
+        m_parent = parent;
+        m_field = null;
+    }
+
+    /**
+     * Visit one "simple" annotation.
+     * @param arg0 : annotation name
+     * @param arg1 : annotation value
+     * @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();
+            return;
+        }
+        if (arg0.equals("value")) {
+            m_value = arg1.toString();
+            return;
+        }
+        if (arg0.equals("mandatory")) {
+            m_mandatory = arg1.toString();
+            return;
+        }
+        if (arg0.equals("type")) {
+        	m_type = arg1.toString();
+        	return;
+        }
+    }
+
+    /**
+     * End of the annotation.
+     * Create a "property" element
+     * @see org.objectweb.asm.AnnotationVisitor#visitEnd()
+     */
+    public void visitEnd() {
+        if (m_field != null  && m_name == null) {
+            m_name = m_field;
+        }
+
+
+        Element[] props = m_parent.getElements("Property");
+        Element prop = null;
+        for (int i = 0; prop == null && props != null && i < props.length; i++) {
+            String name = props[i].getAttribute("name");
+            if (name != null && name.equals(m_name)) {
+                prop = props[i];
+            }
+        }
+
+        if (prop == null) {
+            prop = new Element("property", "");
+            m_parent.addElement(prop);
+            if (m_name != null) {
+                prop.addAttribute(new Attribute("name", m_name));
+            }
+        }
+
+        if (m_field != null) {
+        	prop.addAttribute(new Attribute("field", m_field));
+        }
+        if (m_type != null) {
+        	prop.addAttribute(new Attribute("type", m_type));
+        }
+
+        if (m_value != null) {
+            prop.addAttribute(new Attribute("value", m_value));
+        }
+        if (m_mandatory != null) {
+            prop.addAttribute(new Attribute("mandatory", m_mandatory));
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/annotations/ServiceProdiving.java b/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/annotations/ServiceProdiving.java
index e94c603..edcd7ef 100644
--- a/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/annotations/ServiceProdiving.java
+++ b/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/annotations/ServiceProdiving.java
@@ -10,25 +10,25 @@
 import org.apache.felix.ipojo.test.scenarios.annotations.service.FooService;

 

 public class ServiceProdiving extends OSGiTestCase {

-    

+

     private IPOJOHelper helper;

-    

+

     public void setUp() {

         helper = new IPOJOHelper(this);

     }

-    

+

     public void testProvidesSimple() {

         Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.ProvidesSimple");

         Element[] provs = meta.getElements("provides");

         assertNotNull("Provides exists ", provs);

     }

-    

+

     public void testProvidesDouble() {

         Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.ProvidesDouble");

         Element[] provs = meta.getElements("provides");

         assertNotNull("Provides exists ", provs);

     }

-    

+

     public void testProvidesTriple() {

         Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.ProvidesTriple");

         Element[] provs = meta.getElements("provides");

@@ -38,7 +38,7 @@
         List list = ParseUtils.parseArraysAsList(itfs);

         assertTrue("Provides CS ", list.contains(CheckService.class.getName()));

     }

-    

+

     public void testProvidesQuatro() {

         Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.ProvidesQuatro");

         Element[] provs = meta.getElements("provides");

@@ -49,7 +49,7 @@
         assertTrue("Provides CS ", list.contains(CheckService.class.getName()));

         assertTrue("Provides Foo ", list.contains(FooService.class.getName()));

     }

-    

+

     public void testProperties() {

         Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.ProvidesProperties");

         Element[] provs = meta.getElements("provides");

@@ -73,15 +73,60 @@
         Element baa = getPropertyByName(props, "baa");

         assertEquals("Check baa field", "m_baa", baa.getAttribute("field"));

         assertEquals("Check baa name", "baa", baa.getAttribute("name"));

-        

+

         //Bar

         Element baz = getPropertyByName(props, "baz");

         assertEquals("Check baz field", "m_baz", baz.getAttribute("field"));

-        assertEquals("Check baz name", "baz", baz.getAttribute("name"));        

-        

-        

+        assertEquals("Check baz name", "baz", baz.getAttribute("name"));

     }

-    

+

+    public void testStaticProperties() {

+        Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.ProvidesStaticProperties");

+        Element[] provs = meta.getElements("provides");

+        assertNotNull("Provides exists ", provs);

+        Element prov = provs[0];

+        Element[] props = prov.getElements("property");

+        assertEquals("Number of properties", props.length, 9);

+        //Prop1

+        Element foo = getPropertyByName(props, "prop1");

+        assertNull(foo.getAttribute("field"));

+        assertEquals("prop1", foo.getAttribute("value"));

+

+        //Prop2

+        Element prop2 = getPropertyByName(props, "prop2");

+        assertNull(prop2.getAttribute("field"));

+        assertNull(prop2.getAttribute("value"));

+

+        // Props

+        Element prop = getPropertyByName(props, "props");

+        assertNull(prop.getAttribute("field"));

+        assertEquals("{prop1, prop2}", prop.getAttribute("value"));

+

+        // Mandatory

+        Element mandatory = getPropertyByName(props, "mandatory1");

+        assertNull(mandatory.getAttribute("field"));

+        assertNull(mandatory.getAttribute("value"));

+        assertEquals("true", mandatory.getAttribute("mandatory"));

+

+        //Bar

+        Element bar = getPropertyByName(props, "bar");

+        assertEquals("Check bar field", "bar", bar.getAttribute("field"));

+        assertEquals("Check bar value", "4", bar.getAttribute("value"));

+        assertEquals("Check mandatory value", "true", bar.getAttribute("mandatory"));

+        //Boo

+        Element boo = getPropertyByName(props, "boo");

+        assertEquals("Check boo field", "boo", boo.getAttribute("field"));

+        //Baa

+        Element baa = getPropertyByName(props, "baa");

+        assertEquals("Check baa field", "m_baa", baa.getAttribute("field"));

+        assertEquals("Check baa name", "baa", baa.getAttribute("name"));

+

+        //Bar

+        Element baz = getPropertyByName(props, "baz");

+        assertEquals("Check baz field", "m_baz", baz.getAttribute("field"));

+        assertEquals("Check baz name", "baz", baz.getAttribute("name"));

+    }

+

     public void testServiceController() {

         Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.PSServiceController");

         Element[] provs = meta.getElements("provides");

@@ -91,7 +136,7 @@
         assertEquals(1, provs[0].getElements("controller").length);

         assertEquals("false", provs[0].getElements("controller")[0].getAttribute("value"));

     }

-    

+

     public void testServiceControllerWithSpecification() {

         Element meta = helper.getMetadata("org.apache.felix.ipojo.test.scenarios.component.PSServiceControllerSpec");

         Element[] provs = meta.getElements("provides");

@@ -117,7 +162,7 @@
         fail("Property  " + name + " not found");

         return null;

     }

-    

-    

+

+

 

 }

diff --git a/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/component/ProvidesStaticProperties.java b/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/component/ProvidesStaticProperties.java
new file mode 100644
index 0000000..c9e02f2
--- /dev/null
+++ b/ipojo/tests/core/annotations/src/main/java/org/apache/felix/ipojo/test/scenarios/component/ProvidesStaticProperties.java
@@ -0,0 +1,74 @@
+package org.apache.felix.ipojo.test.scenarios.component;

+

+import java.util.Properties;

+

+import org.apache.felix.ipojo.annotations.Component;

+import org.apache.felix.ipojo.annotations.Provides;

+import org.apache.felix.ipojo.annotations.ServiceProperty;

+import org.apache.felix.ipojo.annotations.StaticServiceProperty;

+import org.apache.felix.ipojo.test.scenarios.annotations.service.BarService;

+import org.apache.felix.ipojo.test.scenarios.annotations.service.FooService;

+

+@Component

+@Provides(

+		specifications= {FooService.class, BarService.class},

+		properties= {

+				@StaticServiceProperty(name="prop1", value="prop1", type="java.lang.String"),

+				@StaticServiceProperty(name="prop2", type="java.lang.String"),

+				@StaticServiceProperty(name="props", value="{prop1, prop2}", type="string[]"),

+				@StaticServiceProperty(name="mandatory1", mandatory=true, type="string")

+		})

+public class ProvidesStaticProperties implements FooService, BarService {

+

+    @ServiceProperty(name = "foo")

+    public int m_foo = 0;

+

+    @ServiceProperty(value = "4", mandatory=true)

+    public int bar;

+

+    @ServiceProperty(name="baz")

+    int m_baz;

+

+    @ServiceProperty

+    public int boo;

+

+    @ServiceProperty(name="baa", value="5")

+    public int m_baa;

+

+    public boolean foo() {

+        return false;

+    }

+

+    public Properties fooProps() {

+        return null;

+    }

+

+    public boolean getBoolean() {

+        return false;

+    }

+

+    public double getDouble() {

+        return 0;

+    }

+

+    public int getInt() {

+        return 0;

+    }

+

+    public long getLong() {

+        return 0;

+    }

+

+    public Boolean getObject() {

+        return null;

+    }

+

+    public boolean bar() {

+        return false;

+    }

+

+    public Properties getProps() {

+        return null;

+    }

+

+}