FELIX-4403 Implement array support in annotation elements
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1616080 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/ActivateMethod.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/ActivateMethod.java
index 1d7e7db..121e79d 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/ActivateMethod.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/ActivateMethod.java
@@ -40,13 +40,18 @@
protected static final Class<?> BUNDLE_CONTEXT_CLASS = BundleContext.class;
protected static final Class<?> INTEGER_CLASS = Integer.class;
- protected final boolean m_supportsInterfaces = false; //TODO configure
+ protected final boolean m_supportsInterfaces;
public ActivateMethod( final String methodName,
- final boolean methodRequired, final Class<?> componentClass, final DSVersion dsVersion, final boolean configurableServiceProperties )
+ final boolean methodRequired,
+ final Class<?> componentClass,
+ final DSVersion dsVersion,
+ final boolean configurableServiceProperties,
+ boolean supportsInterfaces )
{
super( methodName, methodRequired, componentClass, dsVersion, configurableServiceProperties );
+ m_supportsInterfaces = supportsInterfaces;
}
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/Annotations.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/Annotations.java
index ae7fbf3..1d69628 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/Annotations.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/Annotations.java
@@ -23,6 +23,8 @@
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.osgi.framework.Bundle;
@@ -37,7 +39,7 @@
for ( Method method: methods )
{
String name = method.getName();
- //fix up name
+ name = fixup(name);
Object raw = props.get(name);
Object cooked = Coercions.coerce( method.getReturnType(), raw, b );
m.put( name, cooked );
@@ -47,6 +49,26 @@
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] { clazz }, h);
}
+ private static final Pattern p = Pattern.compile("(\\$\\$)|(\\$)|(__)|(_)");
+
+ static String fixup(String name)
+ {
+ Matcher m = p.matcher(name);
+ StringBuffer b = new StringBuffer();
+ while (m.find())
+ {
+ String replacement = "";//null;
+ if (m.group(1) != null) replacement = "\\$";
+ if (m.group(2) != null) replacement = "";
+ if (m.group(3) != null) replacement = "_";
+ if (m.group(4) != null) replacement = ".";
+
+ m.appendReplacement(b, replacement);
+ }
+ m.appendTail(b);
+ return b.toString();
+ }
+
private static class Handler implements InvocationHandler
{
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/Coercions.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/Coercions.java
index 8f18f09..2fff2f6 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/Coercions.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/Coercions.java
@@ -45,6 +45,44 @@
public static Object coerce(Class<?> type, Object raw, Bundle bundle )
{
+ if (type.isArray())
+ {
+ if (raw == null)
+ {
+ return null;
+ }
+ Class<?> componentType = type.getComponentType();
+ if (raw.getClass().isArray())
+ {
+ int size = Array.getLength(raw);
+ Object result = Array.newInstance(componentType, size);
+ for (int i = 0; i < size; i++)
+ {
+ Object rawElement = Array.get(raw, i);
+ Object cooked = coerce(componentType, rawElement, bundle);
+ Array.set(result, i, cooked);
+ }
+ return result;
+ }
+ if (raw instanceof Collection)
+ {
+ Collection raws = (Collection) raw;
+ int size = raws.size();
+ Object result = Array.newInstance(componentType, size);
+ int i = 0;
+ for (Object rawElement: raws)
+ {
+ Object cooked = coerce(componentType, rawElement, bundle);
+ Array.set(result, i++, cooked);
+ }
+ return result;
+
+ }
+ Object cooked = coerce(componentType, raw, bundle);
+ Object result = Array.newInstance(componentType, 1);
+ Array.set(result, 0, cooked);
+ return result;
+ }
if (type == Byte.class || type == byte.class)
{
return coerceToByte(raw);
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/ComponentMethods.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/ComponentMethods.java
index c6489f2..c97e43c 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/ComponentMethods.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/ComponentMethods.java
@@ -47,12 +47,13 @@
}
DSVersion dsVersion = componentMetadata.getDSVersion();
boolean configurableServiceProperties = componentMetadata.isConfigurableServiceProperties();
+ boolean supportsInterfaces = componentMetadata.isConfigureWithInterfaces();
m_activateMethod = new ActivateMethod( componentMetadata.getActivate(), componentMetadata
- .isActivateDeclared(), implementationObjectClass, dsVersion, configurableServiceProperties );
+ .isActivateDeclared(), implementationObjectClass, dsVersion, configurableServiceProperties, supportsInterfaces );
m_deactivateMethod = new DeactivateMethod( componentMetadata.getDeactivate(),
- componentMetadata.isDeactivateDeclared(), implementationObjectClass, dsVersion, configurableServiceProperties );
+ componentMetadata.isDeactivateDeclared(), implementationObjectClass, dsVersion, configurableServiceProperties, supportsInterfaces );
- m_modifiedMethod = new ModifiedMethod( componentMetadata.getModified(), implementationObjectClass, dsVersion, configurableServiceProperties );
+ m_modifiedMethod = new ModifiedMethod( componentMetadata.getModified(), implementationObjectClass, dsVersion, configurableServiceProperties, supportsInterfaces );
for ( ReferenceMetadata referenceMetadata: componentMetadata.getDependencies() )
{
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/DeactivateMethod.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/DeactivateMethod.java
index 6040dbd..386e51d 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/DeactivateMethod.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/DeactivateMethod.java
@@ -30,11 +30,10 @@
return true;
}
-
public DeactivateMethod( final String methodName,
- final boolean methodRequired, final Class<?> componentClass, final DSVersion dsVersion, final boolean configurableServiceProperties )
+ final boolean methodRequired, final Class<?> componentClass, final DSVersion dsVersion, final boolean configurableServiceProperties, boolean supportsInterfaces )
{
- super( methodName, methodRequired, componentClass, dsVersion, configurableServiceProperties );
+ super( methodName, methodRequired, componentClass, dsVersion, configurableServiceProperties, supportsInterfaces );
}
protected String getMethodNamePrefix()
diff --git a/scr/src/main/java/org/apache/felix/scr/impl/helper/ModifiedMethod.java b/scr/src/main/java/org/apache/felix/scr/impl/helper/ModifiedMethod.java
index 107832b..56ce711 100644
--- a/scr/src/main/java/org/apache/felix/scr/impl/helper/ModifiedMethod.java
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/ModifiedMethod.java
@@ -25,9 +25,9 @@
{
public ModifiedMethod( final String methodName,
- final Class<?> componentClass, final DSVersion dsVersion, final boolean configurableServiceProperties )
+ final Class<?> componentClass, final DSVersion dsVersion, final boolean configurableServiceProperties, boolean supportsInterfaces )
{
- super( methodName, methodName != null, componentClass, dsVersion, configurableServiceProperties );
+ super( methodName, methodName != null, componentClass, dsVersion, configurableServiceProperties, supportsInterfaces );
}
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/helper/ActivateMethodTest.java b/scr/src/test/java/org/apache/felix/scr/impl/helper/ActivateMethodTest.java
index 93214af..39a4c2e 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/helper/ActivateMethodTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/helper/ActivateMethodTest.java
@@ -294,7 +294,7 @@
{
ComponentContainer<?> container = newContainer();
SingleComponentManager<?> icm = new SingleComponentManager( container, new ComponentMethods() );
- ActivateMethod am = new ActivateMethod( methodName, methodName != null, obj.getClass(), version, false );
+ ActivateMethod am = new ActivateMethod( methodName, methodName != null, obj.getClass(), version, false, false );
am.invoke( obj, new ActivatorParameter( m_ctx, -1 ), null, icm );
Method m = am.getMethod();
assertNotNull( m );
@@ -365,7 +365,7 @@
{
ComponentContainer container = newContainer();
SingleComponentManager icm = new SingleComponentManager( container, new ComponentMethods() );
- ActivateMethod am = new ActivateMethod( methodName, methodName != null, obj.getClass(), version, false );
+ ActivateMethod am = new ActivateMethod( methodName, methodName != null, obj.getClass(), version, false, false );
am.invoke( obj, new ActivatorParameter( m_ctx, -1 ), null, icm );
Method m = am.getMethod();
assertNull( m );
@@ -397,7 +397,7 @@
}
public void testMethodSorting() throws Exception
{
- ActivateMethod am = new ActivateMethod( "a", true, Sort.class, DSVersion.DS11, false );
+ ActivateMethod am = new ActivateMethod( "a", true, Sort.class, DSVersion.DS11, false, false );
List<Method> ms = am.getSortedMethods(Sort.class);
assertEquals(8, ms.size());
assertEquals(1, ms.get(0).getParameterTypes().length);
diff --git a/scr/src/test/java/org/apache/felix/scr/impl/helper/AnnotationTest.java b/scr/src/test/java/org/apache/felix/scr/impl/helper/AnnotationTest.java
index a2d228c..f1c8327 100644
--- a/scr/src/test/java/org/apache/felix/scr/impl/helper/AnnotationTest.java
+++ b/scr/src/test/java/org/apache/felix/scr/impl/helper/AnnotationTest.java
@@ -18,7 +18,11 @@
*/
package org.apache.felix.scr.impl.helper;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.easymock.EasyMock;
@@ -28,6 +32,26 @@
public class AnnotationTest extends TestCase
{
+
+ public void testNameFixup() throws Exception
+ {
+ assertEquals("foo", Annotations.fixup("foo"));
+ assertEquals("foo", Annotations.fixup("$foo"));
+ assertEquals("foo", Annotations.fixup("foo$"));
+ assertEquals("$foo", Annotations.fixup("$$foo"));
+ assertEquals("foobar", Annotations.fixup("foo$bar"));
+ assertEquals("foo$bar", Annotations.fixup("foo$$bar"));
+ assertEquals("foo.", Annotations.fixup("foo_"));
+ assertEquals("foo_", Annotations.fixup("foo__"));
+ assertEquals(".foo", Annotations.fixup("_foo"));
+ assertEquals("_foo", Annotations.fixup("__foo"));
+ assertEquals("foo.bar", Annotations.fixup("foo_bar"));
+ assertEquals("foo_bar", Annotations.fixup("foo__bar"));
+ assertEquals("foo$", Annotations.fixup("foo$$$"));
+ assertEquals("foo_.", Annotations.fixup("foo___"));
+ assertEquals("foo..bar", Annotations.fixup("foo$_$_bar"));
+ }
+
public enum E1 {a, b, c}
public @interface A1 {
boolean bool();
@@ -46,6 +70,7 @@
{
Bundle b = EasyMock.createMock(Bundle.class);
EasyMock.expect(b.loadClass(String.class.getName())).andReturn((Class) String.class).anyTimes();
+ EasyMock.expect(b.loadClass(Integer.class.getName())).andReturn((Class) Integer.class).anyTimes();
EasyMock.replay(b);
return b;
}
@@ -70,6 +95,26 @@
assertEquals("3", a.string());
}
+ public void testA1FromArray() throws Exception
+ {
+ Map<String, Object> values = arrayValues();
+
+ Object o = Annotations.toObject( A1.class, values, mockBundle());
+ assertTrue("expected an A1", o instanceof A1);
+
+ A1 a = (A1) o;
+ assertEquals(true, a.bool());
+ assertEquals((byte)12, a.byt());
+ assertEquals(String.class, a.clas());
+ assertEquals(E1.a, a.e1());
+ assertEquals(3.14d, a.doubl());
+ assertEquals(500f, a.floa());
+ assertEquals(3, a.integer());
+ assertEquals(12345678l, a.lon());
+ assertEquals((short)3, a.shor());
+ assertEquals(null, a.string());
+ }
+
private Map<String, Object> allValues()
{
Map<String, Object> values = new HashMap();
@@ -86,6 +131,26 @@
return values;
}
+ public void testA1NoValues() throws Exception
+ {
+ Map<String, Object> values = new HashMap<String, Object>();
+
+ Object o = Annotations.toObject( A1.class, values, mockBundle());
+ assertTrue("expected an A1", o instanceof A1);
+
+ A1 a = (A1) o;
+ assertEquals(false, a.bool());
+ assertEquals((byte)0, a.byt());
+ assertEquals(null, a.clas());
+ assertEquals(null, a.e1());
+ assertEquals(0d, a.doubl());
+ assertEquals(0f, a.floa());
+ assertEquals(0, a.integer());
+ assertEquals(0l, a.lon());
+ assertEquals((short)0, a.shor());
+ assertEquals(null, a.string());
+ }
+
public @interface A2 {
boolean bool() default true;
byte byt() default 5;
@@ -118,5 +183,135 @@
assertEquals((short)3, a.shor());
assertEquals("3", a.string());
}
+
+ public @interface A1Arrays {
+ boolean[] bool();
+ byte[] byt();
+ Class<?>[] clas();
+ E1[] e1();
+ double[] doubl();
+ float[] floa();
+ int[] integer();
+ long[] lon();
+ short[] shor();
+ String[] string();
+ }
+
+ public void testA1ArraysNoValues() throws Exception
+ {
+ Map<String, Object> values = new HashMap<String, Object>();
+
+ Object o = Annotations.toObject( A1Arrays.class, values, mockBundle());
+ assertTrue("expected an A1Arrays", o instanceof A1Arrays);
+
+ A1Arrays a = (A1Arrays) o;
+ assertEquals(null, a.bool());
+ assertEquals(null, a.byt());
+ assertEquals(null, a.clas());
+ assertEquals(null, a.e1());
+ assertEquals(null, a.doubl());
+ assertEquals(null, a.floa());
+ assertEquals(null, a.integer());
+ assertEquals(null, a.lon());
+ assertEquals(null, a.shor());
+ assertEquals(null, a.string());
+ }
+
+ public void testA1Array() throws Exception
+ {
+ Map<String, Object> values = allValues();
+
+ Object o = Annotations.toObject( A1Arrays.class, values, mockBundle());
+ assertTrue("expected an A1Arrays", o instanceof A1Arrays);
+
+ A1Arrays a = (A1Arrays) o;
+ assertArrayEquals(new boolean[] {true}, a.bool());
+ assertArrayEquals(new byte[] {(byte)12}, a.byt());
+ assertArrayEquals(new Class<?>[] {String.class}, a.clas());
+ assertArrayEquals(new E1[] {E1.a}, a.e1());
+ assertArrayEquals(new double[] {3.14d}, a.doubl());
+ assertArrayEquals(new float[] {500f}, a.floa());
+ assertArrayEquals(new int[] {3}, a.integer());
+ assertArrayEquals(new long[] {12345678l}, a.lon());
+ assertArrayEquals(new short[] {(short)3}, a.shor());
+ assertArrayEquals(new String[] {"3"}, a.string());
+ }
+
+ private void assertArrayEquals(Object a, Object b)
+ {
+ assertTrue(a.getClass().isArray());
+ assertTrue(b.getClass().isArray());
+ assertEquals("wrong length", Array.getLength(a), Array.getLength(b));
+ assertEquals("wrong type", a.getClass().getComponentType(), b.getClass().getComponentType());
+ for (int i = 0; i < Array.getLength(a); i++)
+ {
+ assertEquals("different value at " + i, Array.get(a, i), Array.get(b, i));
+ }
+
+ }
+
+ private Map<String, Object> arrayValues()
+ {
+ Map<String, Object> values = new HashMap();
+ values.put("bool", new boolean[] {true, false});
+ values.put("byt", new byte[] {12, 3});
+ values.put("clas", new String[] {String.class.getName(), Integer.class.getName()});
+ values.put("e1", new String[] {E1.a.name(), E1.b.name()});
+ values.put("doubl", new double[] {3.14, 2.78, 9});
+ values.put("floa", new float[] {500, 37.44f});
+ values.put("integer", new int[] {3, 6, 9});
+ values.put("lon", new long[] {12345678l, -1});
+ values.put("shor", new short[] {3, 88});
+ values.put("string", new String[] {});
+ return values;
+ }
+
+ public void testA1ArrayFromArray() throws Exception
+ {
+ Map<String, Object> values = arrayValues();
+
+ doA1ArrayTest(values);
+ }
+
+ public void testA1ArrayFromCollection() throws Exception
+ {
+ Map<String, Object> values = arrayValues();
+ Map<String, Object> collectionValues = new HashMap<String, Object>();
+ for (Map.Entry<String, Object> entry: values.entrySet())
+ {
+ collectionValues.put(entry.getKey(), toList(entry.getValue()));
+ }
+
+ doA1ArrayTest(collectionValues);
+ }
+
+ private List<?> toList(Object value)
+ {
+ List result = new ArrayList();
+ for (int i = 0; i < Array.getLength(value); i++)
+ {
+ result.add(Array.get(value, i));
+ }
+ return result;
+ }
+
+ private void doA1ArrayTest(Map<String, Object> values) throws ClassNotFoundException
+ {
+ Object o = Annotations.toObject( A1Arrays.class, values, mockBundle());
+ assertTrue("expected an A1Arrays", o instanceof A1Arrays);
+
+ A1Arrays a = (A1Arrays) o;
+ assertArrayEquals(new boolean[] {true, false}, a.bool());
+ assertArrayEquals(new byte[] {12, 3}, a.byt());
+ assertArrayEquals(new Class<?>[] {String.class, Integer.class}, a.clas());
+ assertArrayEquals(new E1[] {E1.a, E1.b}, a.e1());
+ assertArrayEquals(new double[] {3.14, 2.78, 9}, a.doubl());
+ assertArrayEquals(new float[] {500f, 37.44f}, a.floa());
+ assertArrayEquals(new int[] {3, 6, 9}, a.integer());
+ assertArrayEquals(new long[] {12345678l, -1}, a.lon());
+ assertArrayEquals(new short[] {(short)3, 88}, a.shor());
+ assertArrayEquals(new String[] {}, a.string());
+ }
+
}