FELIX-4607 initial implementation of configuration with nested annotations/interfaces. Only supports arrays or single elements, not collections. Does not support subtypes.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1618545 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 121e79d..474e359 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
@@ -271,7 +271,7 @@
{
param[i] = Annotations.toObject(parameterTypes[i],
(Map<String, Object>) ap.getComponentContext().getProperties(),
- ap.getComponentContext().getBundleContext().getBundle());
+ ap.getComponentContext().getBundleContext().getBundle(), m_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 1d69628..d182974 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
@@ -18,37 +18,178 @@
*/
package org.apache.felix.scr.impl.helper;
+import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.swing.text.Keymap;
+
import org.osgi.framework.Bundle;
public class Annotations
{
- static public <T> T toObject(Class<T> clazz, Map<String, Object> props, Bundle b )
+ static public <T> T toObject(Class<T> clazz, Map<String, Object> props, Bundle b, boolean supportsInterfaces )
{
Map<String, Object> m = new HashMap<String, Object>();
Method[] methods = clazz.getDeclaredMethods();
+ Map<String, Method> complexFields = new HashMap<String, Method>();
for ( Method method: methods )
{
String name = method.getName();
- name = fixup(name);
- Object raw = props.get(name);
- Object cooked = Coercions.coerce( method.getReturnType(), raw, b );
+ String key = fixup(name);
+ Object raw = props.get(key);
+ Class<?> returnType = method.getReturnType();
+ Object cooked;
+ if ( returnType.isInterface() || returnType.isAnnotation())
+ {
+ complexFields.put(key, method);
+ continue;
+ }
+ if (returnType.isArray())
+ {
+ Class<?> componentType = returnType.getComponentType();
+ if ( componentType.isInterface() || componentType.isAnnotation())
+ {
+ complexFields.put(key, method);
+ continue;
+ }
+ cooked = coerceToArray(componentType, raw, b);
+ }
+ else
+ {
+ cooked = Coercions.coerce( returnType, raw, b );
+ }
m.put( name, cooked );
}
+ if (!complexFields.isEmpty())
+ {
+ if (!supportsInterfaces )
+ {
+ //error
+ return null;//??
+ }
+ Map<String, List<Map<String, Object>>> nested = extractSubMaps(complexFields.keySet(), props);
+ for (Map.Entry<String, Method> entry: complexFields.entrySet())
+ {
+ List<Map<String, Object>> proplist = nested.get(entry.getKey());
+ Method method = entry.getValue();
+ Class<?> returnType = method.getReturnType();
+ if (returnType.isArray())
+ {
+ Class<?> componentType = returnType.getComponentType();
+ Object result = Array.newInstance(componentType, proplist.size());
+ for (int i = 0; i < proplist.size(); i++)
+ {
+ Map<String, Object> rawElement = proplist.get(i);
+ Object cooked = toObject(componentType, rawElement, b, supportsInterfaces);
+ Array.set(result, i, cooked);
+ }
+ m.put(method.getName(), result);
+ }
+ else
+ {
+ if (!proplist.isEmpty())
+ {
+ Object cooked = toObject(returnType, proplist.get(0), b, supportsInterfaces);
+ m.put(method.getName(), cooked);
+ }
+ }
+ }
+ }
InvocationHandler h = new Handler(m);
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] { clazz }, h);
}
+ private static Map<String, List<Map<String, Object>>> extractSubMaps(Collection<String> keys, Map<String, Object> map)
+ {
+ Map<String, List<Map<String, Object>>> result = new HashMap<String, List<Map<String, Object>>>();
+ //Form a regexp to recognize all the keys as prefixes in the map keys.
+ StringBuilder b = new StringBuilder("(");
+ for (String key: keys)
+ {
+ b.append(key).append("|");
+ }
+ b.deleteCharAt(b.length() -1);
+ b.append(")\\.([0-9]*)\\.(.*)");
+ Pattern p = Pattern.compile(b.toString());
+ for (Map.Entry<String, Object> entry: map.entrySet())
+ {
+ String longKey = entry.getKey();
+ Matcher m = p.matcher(longKey);
+ if (m.matches())
+ {
+ String key = m.group(1);
+ int index = Integer.parseInt(m.group(2));
+ String subkey = m.group(3);
+ List<Map<String, Object>> subMapsForKey = result.get(key);
+ if (subMapsForKey == null)
+ {
+ subMapsForKey = new ArrayList<Map<String, Object>>();
+ result.put(key, subMapsForKey);
+ }
+ //make sure there is room for the possible new submap
+ for (int i = subMapsForKey.size(); i <= index; i++)
+ {
+ subMapsForKey.add(new HashMap<String, Object>());
+ }
+ Map<String, Object> subMap = subMapsForKey.get(index);
+ subMap.put(subkey, entry.getValue());
+ }
+ }
+ return result;
+ }
+
+ private static Object coerceToArray(Class<?> componentType, Object raw, Bundle bundle)
+ {
+ if (raw == null)
+ {
+ return null;
+ }
+ 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 = Coercions.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 = Coercions.coerce(componentType, rawElement, bundle);
+ Array.set(result, i++, cooked);
+ }
+ return result;
+
+ }
+ Object cooked = Coercions.coerce(componentType, raw, bundle);
+ Object result = Array.newInstance(componentType, 1);
+ Array.set(result, 0, cooked);
+ return result;
+
+ }
+
private static final Pattern p = Pattern.compile("(\\$\\$)|(\\$)|(__)|(_)");
static String fixup(String name)
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 2fff2f6..8f18f09 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,44 +45,6 @@
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/test/java/org/apache/felix/scr/impl/helper/AnnotationTest.java b/scr/src/test/java/org/apache/felix/scr/impl/helper/AnnotationTest.java
index f1c8327..dbc286c 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
@@ -79,10 +79,15 @@
{
Map<String, Object> values = allValues();
- Object o = Annotations.toObject( A1.class, values, mockBundle());
+ Object o = Annotations.toObject( A1.class, values, mockBundle(), false);
assertTrue("expected an A1", o instanceof A1);
A1 a = (A1) o;
+ checkA1(a);
+ }
+
+ private void checkA1(A1 a)
+ {
assertEquals(true, a.bool());
assertEquals((byte)12, a.byt());
assertEquals(String.class, a.clas());
@@ -99,7 +104,7 @@
{
Map<String, Object> values = arrayValues();
- Object o = Annotations.toObject( A1.class, values, mockBundle());
+ Object o = Annotations.toObject( A1.class, values, mockBundle(), false);
assertTrue("expected an A1", o instanceof A1);
A1 a = (A1) o;
@@ -135,7 +140,7 @@
{
Map<String, Object> values = new HashMap<String, Object>();
- Object o = Annotations.toObject( A1.class, values, mockBundle());
+ Object o = Annotations.toObject( A1.class, values, mockBundle(), false);
assertTrue("expected an A1", o instanceof A1);
A1 a = (A1) o;
@@ -168,7 +173,7 @@
{
Map<String, Object> values = allValues();
- Object o = Annotations.toObject( A2.class, values, mockBundle());
+ Object o = Annotations.toObject( A2.class, values, mockBundle(), false);
assertTrue("expected an A2", o instanceof A2);
A2 a = (A2) o;
@@ -201,7 +206,7 @@
{
Map<String, Object> values = new HashMap<String, Object>();
- Object o = Annotations.toObject( A1Arrays.class, values, mockBundle());
+ Object o = Annotations.toObject( A1Arrays.class, values, mockBundle(), false);
assertTrue("expected an A1Arrays", o instanceof A1Arrays);
A1Arrays a = (A1Arrays) o;
@@ -221,7 +226,7 @@
{
Map<String, Object> values = allValues();
- Object o = Annotations.toObject( A1Arrays.class, values, mockBundle());
+ Object o = Annotations.toObject( A1Arrays.class, values, mockBundle(), false);
assertTrue("expected an A1Arrays", o instanceof A1Arrays);
A1Arrays a = (A1Arrays) o;
@@ -297,7 +302,7 @@
private void doA1ArrayTest(Map<String, Object> values) throws ClassNotFoundException
{
- Object o = Annotations.toObject( A1Arrays.class, values, mockBundle());
+ Object o = Annotations.toObject( A1Arrays.class, values, mockBundle(), false);
assertTrue("expected an A1Arrays", o instanceof A1Arrays);
A1Arrays a = (A1Arrays) o;
@@ -313,5 +318,94 @@
assertArrayEquals(new String[] {}, a.string());
}
+ public @interface B1 {
+ boolean bool();
+ byte byt();
+ Class<?> clas();
+ E1 e1();
+ double doubl();
+ float floa();
+ int integer();
+ long lon();
+ short shor();
+ String string();
+ A1 a1();
+ A1[] a1array();
+ }
+
+ public void testB1() throws Exception
+ {
+ Map<String, Object> values = b1Values();
+
+ Object o = Annotations.toObject( B1.class, values, mockBundle(), true);
+ assertTrue("expected an B1 " + o, o instanceof B1);
+ B1 b = (B1) o;
+ checkB1(b);
+ }
+
+ private void checkB1(B1 b)
+ {
+ checkA1(b.a1());
+ assertEquals(3, b.a1array().length);
+ checkA1(b.a1array()[0]);
+ checkA1(b.a1array()[1]);
+ checkA1(b.a1array()[2]);
+ }
+
+ private Map<String, Object> b1Values()
+ {
+ Map<String, Object> a1Values = allValues();
+ Map<String, Object> values = allValues();
+ nest(values, "a1", 0, a1Values);
+ nest(values, "a1array", 0, a1Values);
+ nest(values, "a1array", 1, a1Values);
+ nest(values, "a1array", 2, a1Values);
+ return values;
+ }
+
+ private void nest(Map<String, Object> values, String key, int i,
+ Map<String, Object> a1Values)
+ {
+ for (Map.Entry<String, Object> entry: a1Values.entrySet())
+ {
+ values.put(key + "." + i + "." + entry.getKey(), entry.getValue());
+ }
+ }
+
+ public @interface C1 {
+ boolean bool();
+ byte byt();
+ Class<?> clas();
+ E1 e1();
+ double doubl();
+ float floa();
+ int integer();
+ long lon();
+ short shor();
+ String string();
+ B1 b1();
+ B1[] b1array();
+ }
+
+ public void testC1() throws Exception
+ {
+ Map<String, Object> b1Values = b1Values();
+ Map<String, Object> values = allValues();
+ nest(values, "b1", 0, b1Values);
+ nest(values, "b1array", 0, b1Values);
+ nest(values, "b1array", 1, b1Values);
+ nest(values, "b1array", 2, b1Values);
+
+ Object o = Annotations.toObject( C1.class, values, mockBundle(), true);
+ assertTrue("expected an B1 " + o, o instanceof C1);
+ C1 c = (C1) o;
+ checkB1(c.b1());
+ assertEquals(3, c.b1array().length);
+ checkB1(c.b1array()[0]);
+ checkB1(c.b1array()[1]);
+ checkB1(c.b1array()[2]);
+
+ }
+
}