FELIX-4403 preliminary annotation support, does not handle arrays
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1616078 13f79535-47bb-0310-9956-ffa450edef68
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
new file mode 100644
index 0000000..8e5b3e8
--- /dev/null
+++ b/scr/src/main/java/org/apache/felix/scr/impl/helper/Annotations.java
@@ -0,0 +1,67 @@
+/*
+ * 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.scr.impl.helper;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.Bundle;
+
+public class Annotations
+{
+
+ public <T> T toObject(Class<T> clazz, Map<String, Object> props, Bundle b )
+ {
+ Map<String, Object> m = new HashMap<String, Object>();
+
+ Method[] methods = clazz.getDeclaredMethods();
+ for ( Method method: methods )
+ {
+ String name = method.getName();
+ //fix up name
+ Object raw = props.get(name);
+ Object cooked = Coercions.coerce( method.getReturnType(), raw, b );
+ m.put( name, cooked );
+ }
+
+ InvocationHandler h = new Handler(m);
+ return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] { clazz }, h);
+ }
+
+ private static class Handler implements InvocationHandler
+ {
+
+ private final Map<String, Object> values;
+
+ public Handler(Map<String, Object> values)
+ {
+ this.values = values;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+ {
+ return values.get(method.getName());
+ }
+
+ }
+
+}
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 0b747b4..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
@@ -43,6 +43,52 @@
private static final long long0 = 0;
private static final short short0 = 0;
+ public static Object coerce(Class<?> type, Object raw, Bundle bundle )
+ {
+ if (type == Byte.class || type == byte.class)
+ {
+ return coerceToByte(raw);
+ }
+ if (type == Boolean.class || type == boolean.class)
+ {
+ return coerceToBoolean(raw);
+ }
+ if (type == Class.class)
+ {
+ return coerceToClass(raw, bundle);
+ }
+ if (type == Double.class || type == double.class)
+ {
+ return coerceToDouble(raw);
+ }
+ if (type.isEnum())
+ {
+ Class clazz = type; //TODO is there a better way to do ? enum creation?
+ return coerceToEnum(raw, clazz);
+ }
+ if (type == Float.class || type == float.class)
+ {
+ return coerceToFloat(raw);
+ }
+ if (type == Integer.class || type == int.class)
+ {
+ return coerceToInteger(raw);
+ }
+ if (type == Long.class || type == long.class)
+ {
+ return coerceToLong(raw);
+ }
+ if (type == Short.class || type == short.class)
+ {
+ return coerceToShort(raw);
+ }
+ if (type == String.class)
+ {
+ return coerceToString(raw);
+ }
+ throw new ComponentException ("unexpected output type " + type);
+ }
+
public static byte coerceToByte(Object o)
{
o = multipleToSingle( o, byte0 );
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
new file mode 100644
index 0000000..a515d65
--- /dev/null
+++ b/scr/src/test/java/org/apache/felix/scr/impl/helper/AnnotationTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.scr.impl.helper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.easymock.EasyMock;
+import org.osgi.framework.Bundle;
+
+import junit.framework.TestCase;
+
+public class AnnotationTest extends TestCase
+{
+ public enum E1 {a, b, c}
+ public @interface A1 {
+ boolean bool();
+ byte byt();
+ Class<?> clas();
+ E1 e1();
+ double doubl();
+ float floa();
+ int integer();
+ long lon();
+ short shor();
+ String string();
+ }
+
+ private Bundle mockBundle() throws ClassNotFoundException
+ {
+ Bundle b = EasyMock.createMock(Bundle.class);
+ EasyMock.expect(b.loadClass(String.class.getName())).andReturn((Class) String.class).anyTimes();
+ EasyMock.replay(b);
+ return b;
+ }
+
+ public void testA1() throws Exception
+ {
+ Map<String, Object> values = allValues();
+
+ Object o = new 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("3", a.string());
+ }
+
+ private Map<String, Object> allValues()
+ {
+ Map<String, Object> values = new HashMap();
+ values.put("bool", "true");
+ values.put("byt", 12l);
+ values.put("clas", String.class.getName());
+ values.put("e1", E1.a.toString());
+ values.put("doubl", "3.14");
+ values.put("floa", 500l);
+ values.put("integer", 3.0d);
+ values.put("lon", "12345678");
+ values.put("shor", 3l);
+ values.put("string", 3);
+ return values;
+ }
+
+ public @interface A2 {
+ boolean bool() default true;
+ byte byt() default 5;
+ Class<?> clas() default Integer.class;
+ E1 e1() default E1.b;
+ double doubl() default -2;
+ float floa() default -4;
+ int integer() default -5;
+ long lon() default Long.MIN_VALUE;
+ short shor() default -8;
+ String string() default "default";
+ }
+
+ public void testA2AllValues() throws Exception
+ {
+ Map<String, Object> values = allValues();
+
+ Object o = new Annotations().toObject( A2.class, values, mockBundle());
+ assertTrue("expected an A2", o instanceof A2);
+
+ A2 a = (A2) 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("3", a.string());
+ }
+
+}