You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cz...@apache.org on 2017/03/16 08:10:53 UTC
svn commit: r1787138 - in /felix/trunk/osgi-r7/scr/src:
main/java/org/apache/felix/scr/impl/inject/Annotations.java
test/java/org/apache/felix/scr/impl/inject/AnnotationTest.java
Author: cziegeler
Date: Thu Mar 16 08:10:53 2017
New Revision: 1787138
URL: http://svn.apache.org/viewvc?rev=1787138&view=rev
Log:
FELIX-5590 : Add support for single value annotations and new key mapping
Modified:
felix/trunk/osgi-r7/scr/src/main/java/org/apache/felix/scr/impl/inject/Annotations.java
felix/trunk/osgi-r7/scr/src/test/java/org/apache/felix/scr/impl/inject/AnnotationTest.java
Modified: felix/trunk/osgi-r7/scr/src/main/java/org/apache/felix/scr/impl/inject/Annotations.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/scr/src/main/java/org/apache/felix/scr/impl/inject/Annotations.java?rev=1787138&r1=1787137&r2=1787138&view=diff
==============================================================================
--- felix/trunk/osgi-r7/scr/src/main/java/org/apache/felix/scr/impl/inject/Annotations.java (original)
+++ felix/trunk/osgi-r7/scr/src/main/java/org/apache/felix/scr/impl/inject/Annotations.java Thu Mar 16 08:10:53 2017
@@ -18,16 +18,22 @@
*/
package org.apache.felix.scr.impl.inject;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -37,18 +43,119 @@ import org.osgi.service.component.Compon
public class Annotations
{
+ /** Capture all methods defined by the annotation interface */
+ private static final Set<Method> ANNOTATION_METHODS = new HashSet<Method>();
+ static
+ {
+ for(final Method m : Annotation.class.getMethods())
+ {
+ ANNOTATION_METHODS.add(m);
+ }
+ }
+
+ /** Constant for the single element method */
+ private static final String VALUE_METHOD = "value";
+
+ /** Constant for the prefix constant. */
+ private static final String PREFIX_CONSTANT = "PREFIX_";
+
+ /**
+ * Check whether the provided type is a single element annotation.
+ * A single element annotation has a method named "value" and all
+ * other annotation methods must have a default value.
+ * @param clazz The provided type
+ * @return {@code true} if the type is a single element annotation.
+ */
+ static public boolean isSingleElementAnnotation(final Class<?> clazz)
+ {
+ boolean result = false;
+ if ( clazz.isAnnotation() )
+ {
+ result = true;
+ boolean hasValue = false;
+ for ( final Method method: clazz.getMethods() )
+ {
+ // filter out methods from Annotation
+ boolean isFromAnnotation = false;
+ for(final Method objMethod : ANNOTATION_METHODS)
+ {
+ if ( objMethod.getName().equals(method.getName())
+ && Arrays.equals(objMethod.getParameterTypes(), method.getParameterTypes()) )
+ {
+ isFromAnnotation = true;
+ break;
+ }
+ }
+ if ( isFromAnnotation )
+ {
+ continue;
+ }
+ if ( VALUE_METHOD.equals(method.getName()) )
+ {
+ hasValue = true;
+ continue;
+ }
+ if ( method.getDefaultValue() == null )
+ {
+ result = false;
+ break;
+ }
+
+ }
+ if ( result )
+ {
+ result = hasValue;
+ }
+
+ }
+ return result;
+ }
+
+ static public String getPrefix(Class<?> clazz)
+ {
+ try
+ {
+ final Field f = clazz.getField(PREFIX_CONSTANT);
+ if ( Modifier.isStatic(f.getModifiers())
+ && Modifier.isPublic(f.getModifiers())
+ && Modifier.isFinal(f.getModifiers()) )
+ {
+ final Object value = f.get(null);
+ if ( value != null )
+ {
+ return value.toString();
+ }
+ }
+ }
+ catch ( final Exception ignore)
+ {
+ // ignore
+ }
+ return null;
+ }
@SuppressWarnings("unchecked")
static public <T> T toObject(Class<T> clazz, Map<String, Object> props, Bundle b, boolean supportsInterfaces )
{
- Map<String, Object> m = new HashMap<String, Object>();
+ final boolean isSingleElementAnn = isSingleElementAnnotation(clazz);
+ final String prefix = getPrefix(clazz);
+ final Map<String, Object> m = new HashMap<String, Object>();
- Method[] methods = clazz.getMethods();
- Map<String, Method> complexFields = new HashMap<String, Method>();
- for ( Method method: methods )
+ final Map<String, Method> complexFields = new HashMap<String, Method>();
+ for ( final Method method: clazz.getMethods() )
{
- String name = method.getName();
- String key = fixup(name);
+ final String name = method.getName();
+ final String key;
+ if ( isSingleElementAnn && name.equals(VALUE_METHOD) )
+ {
+ key = mapTypeNameToKey(clazz.getSimpleName());
+ }
+ else
+ {
+ final String mapped = mapIdentifierToKey(name);
+ key = (prefix == null ? mapped : prefix.concat(mapped));
+ }
+
Object raw = props.get(key);
Class<?> returnType = method.getReturnType();
Object cooked;
@@ -211,7 +318,7 @@ public class Annotations
private static final Pattern p = Pattern.compile("(\\$_\\$)|(\\$\\$)|(\\$)|(__)|(_)");
- static String fixup(String name)
+ static String mapIdentifierToKey(String name)
{
Matcher m = p.matcher(name);
StringBuffer b = new StringBuffer();
@@ -230,6 +337,26 @@ public class Annotations
return b.toString();
}
+ static String mapTypeNameToKey(String name)
+ {
+ final StringBuilder sb = new StringBuilder();
+ boolean lastLow = false;
+ for(final char c : name.toCharArray())
+ {
+ if ( lastLow && Character.isAlphabetic(c) && Character.isUpperCase(c) )
+ {
+ sb.append('.');
+ }
+ lastLow = false;
+ if ( Character.isAlphabetic(c) && Character.isLowerCase(c))
+ {
+ lastLow = true;
+ }
+ sb.append(Character.toLowerCase(c));
+ }
+ return sb.toString();
+ }
+
private final static class Handler implements InvocationHandler
{
private final Map<String, Object> values;
Modified: felix/trunk/osgi-r7/scr/src/test/java/org/apache/felix/scr/impl/inject/AnnotationTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/scr/src/test/java/org/apache/felix/scr/impl/inject/AnnotationTest.java?rev=1787138&r1=1787137&r2=1787138&view=diff
==============================================================================
--- felix/trunk/osgi-r7/scr/src/test/java/org/apache/felix/scr/impl/inject/AnnotationTest.java (original)
+++ felix/trunk/osgi-r7/scr/src/test/java/org/apache/felix/scr/impl/inject/AnnotationTest.java Thu Mar 16 08:10:53 2017
@@ -33,25 +33,32 @@ import junit.framework.TestCase;
public class AnnotationTest extends TestCase
{
- public void testNameFixup() throws Exception
+ public void testMapIdentifierToKey() 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"));
- assertEquals("six-prop", Annotations.fixup("six$_$prop"));
- assertEquals("seven$.prop", Annotations.fixup("seven$$_$prop"));
+ assertEquals("foo", Annotations.mapIdentifierToKey("foo"));
+ assertEquals("foo", Annotations.mapIdentifierToKey("$foo"));
+ assertEquals("foo", Annotations.mapIdentifierToKey("foo$"));
+ assertEquals("$foo", Annotations.mapIdentifierToKey("$$foo"));
+ assertEquals("foobar", Annotations.mapIdentifierToKey("foo$bar"));
+ assertEquals("foo$bar", Annotations.mapIdentifierToKey("foo$$bar"));
+ assertEquals("foo.", Annotations.mapIdentifierToKey("foo_"));
+ assertEquals("foo_", Annotations.mapIdentifierToKey("foo__"));
+ assertEquals(".foo", Annotations.mapIdentifierToKey("_foo"));
+ assertEquals("_foo", Annotations.mapIdentifierToKey("__foo"));
+ assertEquals("foo.bar", Annotations.mapIdentifierToKey("foo_bar"));
+ assertEquals("foo_bar", Annotations.mapIdentifierToKey("foo__bar"));
+ assertEquals("foo$", Annotations.mapIdentifierToKey("foo$$$"));
+ assertEquals("foo_.", Annotations.mapIdentifierToKey("foo___"));
+ assertEquals("foo-.bar", Annotations.mapIdentifierToKey("foo$_$_bar"));
+ assertEquals("six-prop", Annotations.mapIdentifierToKey("six$_$prop"));
+ assertEquals("seven$.prop", Annotations.mapIdentifierToKey("seven$$_$prop"));
+ }
+
+ public void testMapTypeNameToKey() throws Exception
+ {
+ assertEquals("service.ranking", Annotations.mapTypeNameToKey("ServiceRanking"));
+ assertEquals("some_value", Annotations.mapTypeNameToKey("Some_Value"));
+ assertEquals("osgi.property", Annotations.mapTypeNameToKey("OSGiProperty"));
}
public enum E1 {a, b, c}
@@ -608,6 +615,70 @@ public class AnnotationTest extends Test
assertOdder("three", ot.odder3());
}
+ public @interface SETest1 {
+ String value();
+ }
+
+ public @interface SETest2 {
+ boolean foo() default true;
+ String value();
+ }
+
+ public @interface SETest3 {
+ boolean foo();
+ String value();
+ }
+
+ public @interface SETest4 {
+ boolean foo();
+ String values();
+ }
+
+ public @interface PrefixTest {
+ String PREFIX_ = "org.apache.";
+
+ boolean foo() default true;
+ String[] values() default {"a"};
+ String value();
+ }
+
+ public void testSingleElementAnnotation() throws Exception
+ {
+ assertFalse(Annotations.isSingleElementAnnotation(Map.class));
+ assertTrue(Annotations.isSingleElementAnnotation(SETest1.class));
+ assertTrue(Annotations.isSingleElementAnnotation(SETest2.class));
+ assertFalse(Annotations.isSingleElementAnnotation(SETest3.class));
+ assertFalse(Annotations.isSingleElementAnnotation(SETest4.class));
+ }
+
+ public void testGetPrefix() throws Exception
+ {
+ assertNull(Annotations.getPrefix(Map.class));
+ assertNull(Annotations.getPrefix(SETest1.class));
+ assertNull(Annotations.getPrefix(SETest2.class));
+ assertNull(Annotations.getPrefix(SETest3.class));
+ assertNull(Annotations.getPrefix(SETest4.class));
+ assertNull(Annotations.getPrefix(A1.class));
+ assertEquals("org.apache.", Annotations.getPrefix(PrefixTest.class));
+ }
+
+ public void testMappingWithPrefix() throws Exception
+ {
+ final Map<String, Object> values = new HashMap<String, Object>();
+ values.put("foo", false);
+ values.put("org.apache.foo", true);
+ values.put("values", "false-values");
+ values.put("org.apache.values", "true-values");
+ values.put("value", "false-value");
+ values.put("prefix.test", "true-value");
+
+ final PrefixTest o = Annotations.toObject( PrefixTest.class, values, mockBundle(), true);
+ assertEquals("true-value", o.value());
+ assertEquals(1, o.values().length);
+ assertEquals("true-values", o.values()[0]);
+ assertEquals(true, o.foo());
+ }
+
private void assertOdd(String expectedContent, Object actual) {
assertTrue("expected an Odd", actual instanceof Odd);
assertEquals("Expected Odd contents", expectedContent, ((Odd)actual).getContent());