You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by da...@apache.org on 2017/03/20 21:26:00 UTC

svn commit: r1787850 - in /felix/trunk/converter/converter/src: main/java/org/apache/felix/converter/impl/ test/java/org/apache/felix/converter/impl/

Author: davidb
Date: Mon Mar 20 21:26:00 2017
New Revision: 1787850

URL: http://svn.apache.org/viewvc?rev=1787850&view=rev
Log:
Support conversions from SingleElementAnnotations.

Modified:
    felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java
    felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/DynamicMapLikeFacade.java
    felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/Util.java
    felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java

Modified: felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java?rev=1787850&r1=1787849&r2=1787850&view=diff
==============================================================================
--- felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java (original)
+++ felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java Mon Mar 20 21:26:00 2017
@@ -450,7 +450,7 @@ public class ConvertingImpl implements C
             new InvocationHandler() {
                 @Override
                 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-                    String propName = Util.getInterfacePropertyName(method);
+                    String propName = Util.getInterfacePropertyName(method, Util.getSingleElementAnnotationKey(targetCls, proxy), proxy);
                     if (propName == null)
                         return null;
 
@@ -714,7 +714,7 @@ public class ConvertingImpl implements C
         Map result = new HashMap();
         for (Class i : obj.getClass().getInterfaces()) {
             for (Method md : i.getMethods()) {
-                handleInterfaceMethod(obj, md, new HashSet<>(), result);
+                handleInterfaceMethod(obj, i, md, new HashSet<>(), result);
             }
             if (result.size() > 0)
                 return result;
@@ -801,12 +801,12 @@ public class ConvertingImpl implements C
     }
 
     @SuppressWarnings({ "rawtypes", "unchecked" })
-    private static void handleInterfaceMethod(Object obj, Method md, Set<String> invokedMethods, Map res) {
+    private static void handleInterfaceMethod(Object obj, Class<?> intf, Method md, Set<String> invokedMethods, Map res) {
         String mn = md.getName();
         if (invokedMethods.contains(mn))
             return; // method with this name already invoked
 
-        String propName = Util.getInterfacePropertyName(md);
+        String propName = Util.getInterfacePropertyName(md, Util.getSingleElementAnnotationKey(intf, obj), obj);
         if (propName == null)
             return;
 

Modified: felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/DynamicMapLikeFacade.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/DynamicMapLikeFacade.java?rev=1787850&r1=1787849&r2=1787850&view=diff
==============================================================================
--- felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/DynamicMapLikeFacade.java (original)
+++ felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/DynamicMapLikeFacade.java Mon Mar 20 21:26:00 2017
@@ -267,7 +267,7 @@ class DynamicInterfaceFacade extends Dyn
 
     private Map<String, Set<Method>> getKeys() {
         if (keys == null)
-            keys = Util.getInterfaceKeys(convertingImpl.sourceClass);
+            keys = Util.getInterfaceKeys(convertingImpl.sourceClass, backingObject);
 
         return keys;
     }

Modified: felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/Util.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/Util.java?rev=1787850&r1=1787849&r2=1787850&view=diff
==============================================================================
--- felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/Util.java (original)
+++ felix/trunk/converter/converter/src/main/java/org/apache/felix/converter/impl/Util.java Mon Mar 20 21:26:00 2017
@@ -140,11 +140,12 @@ class Util {
         return unMangleName(getPrefix(f.getDeclaringClass()), f.getName());
     }
 
-    static Map<String, Set<Method>> getInterfaceKeys(Class<?> intf) {
+    static Map<String, Set<Method>> getInterfaceKeys(Class<?> intf, Object object) {
         Map<String, Set<Method>> keys = new LinkedHashMap<>();
 
+        String seank = getSingleElementAnnotationKey(intf, object);
         for (Method md : intf.getMethods()) {
-            String name = getInterfacePropertyName(md);
+            String name = getInterfacePropertyName(md, seank, object);
             if (name != null) {
                 Set<Method> set = keys.get(name);
                 if (set == null) {
@@ -171,17 +172,87 @@ class Util {
         return keys;
     }
 
-    static String getInterfacePropertyName(Method md) {
+    static String getSingleElementAnnotationKey(Class<?> intf, Object obj) {
+        Class<?> ann = getAnnotationType(intf, obj);
+        if (ann == null)
+            return null;
+
+        boolean valueFound = false;
+        for (Method md : ann.getDeclaredMethods()) {
+            if ("value".equals(md.getName())) {
+                valueFound = true;
+                continue;
+            }
+
+            if (md.getDefaultValue() == null) {
+                // All elements bar value must have a default
+                return null;
+            }
+        }
+
+        if (!valueFound) {
+            // Single Element Annotation must have a value element.
+            return null;
+        }
+
+        return toSingleElementAnnotationKey(ann.getSimpleName());
+    }
+
+    private static Class<?> getAnnotationType(Class<?> intf, Object obj) {
+        try {
+            Method md = intf.getMethod("annotationType");
+            Object res = md.invoke(obj);
+            if (res instanceof Class)
+                return (Class<?>) res;
+        } catch (Exception e) {
+        }
+        return null;
+    }
+
+    private static String toSingleElementAnnotationKey(String simpleName) {
+        StringBuilder sb = new StringBuilder();
+
+        boolean capitalSeen = true;
+        for (char c : simpleName.toCharArray()) {
+            if (!capitalSeen) {
+                if (Character.isUpperCase(c)) {
+                    capitalSeen = true;
+                    sb.append('.');
+                }
+            } else {
+                if (Character.isLowerCase(c)) {
+                    capitalSeen = false;
+                }
+            }
+            sb.append(Character.toLowerCase(c));
+        }
+
+        return sb.toString();
+    }
+
+    static String getInterfacePropertyName(Method md, String singleElementAnnotationKey, Object object) {
         if (md.getReturnType().equals(Void.class))
             return null; // not an accessor
 
         if (md.getParameterTypes().length > 1)
             return null; // not an accessor
 
+        if ("value".equals(md.getName()) && md.getParameterTypes().length == 0 && singleElementAnnotationKey != null)
+            return singleElementAnnotationKey;
+
         if (Object.class.equals(md.getDeclaringClass()) ||
             Annotation.class.equals(md.getDeclaringClass()))
             return null; // do not use any methods on the Object or Annotation class as a accessor
 
+        if ("annotationType".equals(md.getName())) {
+            try {
+                Object cls = md.invoke(object);
+                if (cls instanceof Class && ((Class<?>) cls).isAnnotation())
+                    return null;
+            } catch (Exception e) {
+            }
+        }
+
         if (md.getDeclaringClass().getSimpleName().startsWith("$Proxy")) {
             // TODO is there a better way to do this?
             if (isInheritedMethodInProxy(md, Object.class) ||

Modified: felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java?rev=1787850&r1=1787849&r2=1787850&view=diff
==============================================================================
--- felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java (original)
+++ felix/trunk/converter/converter/src/test/java/org/apache/felix/converter/impl/ConverterMapTest.java Mon Mar 20 21:26:00 2017
@@ -16,6 +16,7 @@
  */
 package org.apache.felix.converter.impl;
 
+import java.lang.annotation.Annotation;
 import java.math.BigInteger;
 import java.net.URL;
 import java.text.SimpleDateFormat;
@@ -258,6 +259,37 @@ public class ConverterMapTest {
         }
     }
 
+    @Test
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void testSingleElementAnnotation() {
+    	class MySingleElementAnnotation implements SingleElementAnnotation {
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return SingleElementAnnotation.class;
+            }
+
+            @Override
+            public String[] value() {
+                return new String[] {"hi", "there"};
+            }
+
+            @Override
+            public long somethingElse() {
+                return 42;
+            }
+    	};
+    	MySingleElementAnnotation sea = new MySingleElementAnnotation();
+    	Map m = converter.convert(sea).to(Map.class);
+    	Map expected = new HashMap();
+    	expected.put("single.element.annotation", new String[] {"hi", "there"});
+    	expected.put("somethingElse", 42);
+    	assertEquals(2, m.size());
+    	assertArrayEquals(new String[] {"hi", "there"}, (String []) m.get("single.element.annotation"));
+    	assertEquals(42L, m.get("somethingElse"));
+
+    	// TODO support conversion in the other direction too.
+    }
+
     @SuppressWarnings({ "rawtypes", "unchecked" })
     @Test
     public void testCopyMap() {
@@ -281,4 +313,9 @@ public class ConverterMapTest {
         int bar() default 42;
         boolean za_za();
     }
+
+    @interface SingleElementAnnotation {
+    	String[] value();
+    	long somethingElse() default -87;
+    }
 }