You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by ma...@apache.org on 2010/07/24 15:42:05 UTC

svn commit: r978873 - in /incubator/aries/trunk/blueprint/blueprint-core/src: main/java/org/apache/aries/blueprint/container/ main/java/org/apache/aries/blueprint/di/ main/java/org/apache/aries/blueprint/utils/ test/java/org/apache/aries/blueprint/ tes...

Author: mahrwald
Date: Sat Jul 24 13:42:04 2010
New Revision: 978873

URL: http://svn.apache.org/viewvc?rev=978873&view=rev
Log:
ARIES-366: Allow overloaded setter methods

Added:
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/AmbiguousPojo.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/utils/ReflectionUtilsTest.java
Modified:
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/AggregateConverter.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintRepository.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ExecutionContext.java
    incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/WiringTest.java
    incubator/aries/trunk/blueprint/blueprint-core/src/test/resources/test-wiring.xml

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/AggregateConverter.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/AggregateConverter.java?rev=978873&r1=978872&r2=978873&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/AggregateConverter.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/AggregateConverter.java Sat Jul 24 13:42:04 2010
@@ -111,11 +111,13 @@ public class AggregateConverter implemen
             return true;
         }
         
-        // TODO
-        if (fromValue instanceof String) {
-            //
+        // TODO implement better logic ?!
+        try {
+            convert(fromValue, toType);
+            return true;
+        } catch (Exception e) {
+            return false;
         }
-        return false;
     }
 
     public Object convert(final Object fromValue, final ReifiedType type) throws Exception {

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java?rev=978873&r1=978872&r2=978873&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BeanRecipe.java Sat Jul 24 13:42:04 2010
@@ -813,24 +813,26 @@ public class BeanRecipe extends Abstract
                 throw new ComponentDefinitionException("No getter for " + names[i] + " property on bean " + getName() + " when setting property " + propertyName + " on class " + clazz.getName());
             }
         }
+        
+        // Instantiate value
+        if (propertyValue instanceof Recipe) {
+            propertyValue = ((Recipe) propertyValue).create();
+        }
+
         final PropertyDescriptor pd = getPropertyDescriptor(clazz, names[names.length - 1]);
         if (pd.allowsSet()) {
             // convert the value to type of setter/field
-            Type type = pd.getGenericType();
-            // Instanciate value
-            if (propertyValue instanceof Recipe) {
-                propertyValue = ((Recipe) propertyValue).create();
-            }
-            try {
-                propertyValue = convert(propertyValue, type);
-            } catch (Exception e) {
-                    String valueType = propertyValue == null ? "null" : propertyValue.getClass().getName();
-                String memberType = type instanceof Class ? ((Class) type).getName() : type.toString();
-                throw new ComponentDefinitionException("Unable to convert property value" +
-                        " from " + valueType +
-                        " to " + memberType +
-                        " for injection " + pd, e);
-            }
+//            Type type = pd.getGenericType(propertyValue);
+//            try {
+//                propertyValue = convert(propertyValue, type);
+//            } catch (Exception e) {
+//                    String valueType = propertyValue == null ? "null" : propertyValue.getClass().getName();
+//                String memberType = type instanceof Class ? ((Class) type).getName() : type.toString();
+//                throw new ComponentDefinitionException("Unable to convert property value" +
+//                        " from " + valueType +
+//                        " to " + memberType +
+//                        " for injection " + pd, e);
+//            }
             try {
                 pd.set(instance, propertyValue, blueprintContainer.getAccessControlContext());
             } catch (Exception e) {

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintRepository.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintRepository.java?rev=978873&r1=978872&r2=978873&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintRepository.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintRepository.java Sat Jul 24 13:42:04 2010
@@ -372,6 +372,10 @@ public class BlueprintRepository impleme
     public Object convert(Object value, ReifiedType type) throws Exception {
         return blueprintContainer.getConverter().convert(value, type);
     }
+    
+    public boolean canConvert(Object value, ReifiedType type) {
+        return blueprintContainer.getConverter().canConvert(value, type);
+    }
 
     public Class loadClass(String typeName) throws ClassNotFoundException {
         return blueprintContainer.loadClass(typeName);

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ExecutionContext.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ExecutionContext.java?rev=978873&r1=978872&r2=978873&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ExecutionContext.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ExecutionContext.java Sat Jul 24 13:42:04 2010
@@ -90,6 +90,8 @@ public interface ExecutionContext {
     public abstract Object getPartialObject(String name);
 
     public abstract Object convert(Object value, ReifiedType type) throws Exception;
+    
+    public abstract boolean canConvert(Object value, ReifiedType type);
 
     public abstract Class loadClass(String className) throws ClassNotFoundException;
 

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java?rev=978873&r1=978872&r2=978873&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/main/java/org/apache/aries/blueprint/utils/ReflectionUtils.java Sat Jul 24 13:42:04 2010
@@ -32,12 +32,17 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.WeakHashMap;
 
+import org.apache.aries.blueprint.container.GenericType;
+import org.apache.aries.blueprint.di.ExecutionContext;
+import org.osgi.service.blueprint.container.ComponentDefinitionException;
+
 /**
  * TODO: javadoc
  *
@@ -129,85 +134,71 @@ public class ReflectionUtils {
         }
         
         if (properties[index] == null) {
-            Map<String,PropertyDescriptor> props = new HashMap<String, PropertyDescriptor>();
+            Set<String> propertyNames = new HashSet<String>();
+            Map<String,Method> getters = new HashMap<String, Method>();
+            Map<String,List<Method>> setters = new HashMap<String, List<Method>>();
+            Set<String> illegalProperties = new HashSet<String>();
+            
             for (Method method : clazz.getMethods()) {
-                if (Modifier.isStatic(method.getModifiers()) || method.isBridge()) {
-                    continue;
-                }
+                if (Modifier.isStatic(method.getModifiers()) || method.isBridge()) continue;
+                
                 String name = method.getName();
                 Class<?> argTypes[] = method.getParameterTypes();
                 Class<?> resultType = method.getReturnType();
                 
-                Class<?> argType = resultType;
-                Method getter = null;
-                Method setter = null;
-                
                 if (name.length() > 3 && name.startsWith("set") && resultType == Void.TYPE && argTypes.length == 1) {
                     name = decapitalize(name.substring(3));
-                    setter = method;
-                    argType = argTypes[0];
-                } else if (name.length() > 3 && name.startsWith("get") && argTypes.length == 0) {
+                    if (!!!setters.containsKey(name)) setters.put(name, new ArrayList<Method>());
+                    setters.get(name).add(method);
+                    propertyNames.add(name);
+                } else if (name.length() > 3 && name.startsWith("get") && resultType != Void.TYPE && argTypes.length == 0) {
                     name = decapitalize(name.substring(3));
-                    getter = method;
+
+                    if (getters.containsKey(name)) illegalProperties.add(name);
+                    else propertyNames.add(name);
+                    
+                    getters.put(name, method);                    
                 } else if (name.length() > 2 && name.startsWith("is") && argTypes.length == 0 && resultType == boolean.class) {
                     name = decapitalize(name.substring(2));
-                    getter = method;
-                } else {
-                    continue;
+
+                    if (getters.containsKey(name)) illegalProperties.add(name);
+                    else propertyNames.add(name);
+                    
+                    getters.put(name, method);                    
                 }
                 
-                if (props.containsKey(name)) {
-                    PropertyDescriptor pd = props.get(name);
-                    if (pd != INVALID_PROPERTY) {
-                        if (!argType.equals(pd.type)) {
-                            props.put(name, INVALID_PROPERTY);
-                        } else if (getter != null) {
-                            if (pd.getter == null || pd.getter.equals(getter))
-                                pd.getter = getter;
-                            else
-                                props.put(name, INVALID_PROPERTY);
-                        } else if (setter != null) {
-                            if (pd.setter == null || pd.setter.equals(setter)) 
-                                pd.setter = setter;
-                            else
-                                props.put(name, INVALID_PROPERTY);
-                        }
-                    }
-                } else {
-                    props.put(name, new PropertyDescriptor(name, argType, getter, setter));
-                }
             }
+
+            Map<String, PropertyDescriptor> props = new HashMap<String, PropertyDescriptor>();
+            for (String propName : propertyNames) {
+                props.put(propName,
+                        new MethodPropertyDescriptor(propName, getters.get(propName), setters.get(propName)));
+            }            
             
             if (allowFieldInjection) {
                 for (Field field : clazz.getDeclaredFields()) {
-                    if (Modifier.isStatic(field.getModifiers())) {
-                        continue;
-                    }
-                    
-                    String name = decapitalize(field.getName());
-                    if (!props.containsKey(name)) {
-                        props.put(name, new PropertyDescriptor(name, field.getType(), field));
-                    } else {
-                        PropertyDescriptor pd = props.get(name);
-                        if (pd != INVALID_PROPERTY) {
-                            if (pd.type.equals(field.getType())) {
-                                pd.field = field;
-                            } 
-                            // no else, we don't require field implementations to have the same
-                            // type as the getter and setter
+                    if (!!!Modifier.isStatic(field.getModifiers())) {
+                        String name = decapitalize(field.getName());
+                        PropertyDescriptor desc = props.get(name);
+                        if (desc == null) {
+                            props.put(name, new FieldPropertyDescriptor(name, field));
+                        } else if (desc instanceof MethodPropertyDescriptor) {
+                            props.put(name, 
+                                    new JointPropertyDescriptor((MethodPropertyDescriptor) desc, 
+                                            new FieldPropertyDescriptor(name, field)));
+                        } else {
+                            illegalProperties.add(name);
                         }
                     }
                 }
             }
             
-            Iterator<PropertyDescriptor> it = props.values().iterator();
-            while (it.hasNext()) {
-                if (it.next() == INVALID_PROPERTY)
-                    it.remove();
+            List<PropertyDescriptor> result = new ArrayList<PropertyDescriptor>();
+            for (PropertyDescriptor prop : props.values()) {
+                if (!!!illegalProperties.contains(prop.getName())) result.add(prop);
             }
             
-            Collection<PropertyDescriptor> tmp = props.values();
-            properties[index] = tmp.toArray(new PropertyDescriptor[tmp.size()]); 
+            properties[index] = result.toArray(new PropertyDescriptor[result.size()]); 
         }
         return properties[index];
     }
@@ -273,41 +264,22 @@ public class ReflectionUtils {
         }
     }
     
-    private static final PropertyDescriptor INVALID_PROPERTY = new PropertyDescriptor(null, null, null, null);
-
-    public static class PropertyDescriptor {
-        private String name;
-        private Class<?> type;
-        private Method getter;
-        private Method setter;
-        private Field field;
-
-        public PropertyDescriptor(String name, Class<?> type, Method getter, Method setter) {
-            this.name = name;
-            this.type = type;
-            this.getter = getter;
-            this.setter = setter;
-        }
+    public static abstract class PropertyDescriptor {
+        private final String name;
         
-        public PropertyDescriptor(String name, Class<?> type, Field field) {
+        public PropertyDescriptor(String name) {
             this.name = name;
-            this.type = type;
-            this.field = field;
-            this.getter = null;
-            this.setter = null;
         }
-
+        
         public String getName() {
             return name;
         }
         
-        public boolean allowsGet() {
-            return getter != null || field != null;
-        }
+        public abstract boolean allowsGet();
+        public abstract boolean allowsSet();
         
-        public boolean allowsSet() {
-            return setter != null || field != null;
-        }
+        protected abstract Object internalGet(Object instance) throws Exception;
+        protected abstract void internalSet(Object instance, Object value) throws Exception;        
         
         public Object get(final Object instance, AccessControlContext acc) throws Exception {            
             if (acc == null) {
@@ -324,18 +296,6 @@ public class ReflectionUtils {
                 }
             }
         }
-            
-        private Object internalGet(Object instance) 
-                throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
-            if (getter != null) {
-                return getter.invoke(instance);
-            } else if (field != null) {
-                field.setAccessible(true);
-                return field.get(instance);
-            } else {
-                throw new UnsupportedOperationException();
-            }
-        }
 
         public void set(final Object instance, final Object value, AccessControlContext acc) throws Exception {
             if (acc == null) {
@@ -354,31 +314,170 @@ public class ReflectionUtils {
             }            
         }
         
-        private void internalSet(Object instance, Object value) 
+        protected Object convert(Object obj, Type type) throws Exception {
+            return ExecutionContext.Holder.getContext().convert(obj, new GenericType(type));
+        }
+    }
+    
+    private static class JointPropertyDescriptor extends PropertyDescriptor {
+        private final MethodPropertyDescriptor mpd;
+        private final FieldPropertyDescriptor fpd;
+        
+        public JointPropertyDescriptor(MethodPropertyDescriptor mpd, FieldPropertyDescriptor fpd) {
+            super(mpd.getName());
+            this.mpd = mpd;
+            this.fpd = fpd;
+        }
+
+        @Override
+        public boolean allowsGet() {
+            return mpd.allowsGet() || fpd.allowsGet();
+        }
+
+        @Override
+        public boolean allowsSet() {
+            return mpd.allowsSet() || fpd.allowsSet();
+        }
+
+        @Override
+        protected Object internalGet(Object instance) throws Exception {
+            if (mpd.allowsGet()) return mpd.internalGet(instance);
+            else if (fpd.allowsGet()) return fpd.internalGet(instance);
+            else throw new UnsupportedOperationException();
+        }
+
+        @Override
+        protected void internalSet(Object instance, Object value) throws Exception {
+            if (mpd.allowsSet()) mpd.internalSet(instance, value);
+            else if (fpd.allowsSet()) fpd.internalSet(instance, value);
+            else throw new UnsupportedOperationException();
+        }
+    }
+    
+    private static class FieldPropertyDescriptor extends PropertyDescriptor {
+        private final Field field;
+        
+        public FieldPropertyDescriptor(String name, Field field) {
+            super(name);
+            this.field = field;
+        }
+
+        public boolean allowsGet() {
+            return true;
+        }
+
+        public boolean allowsSet() {
+            return true;
+        }
+
+        protected Object internalGet(Object instance) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+            field.setAccessible(true);
+            return field.get(instance);
+        }
+
+        protected void internalSet(Object instance, Object value) throws Exception {
+            field.setAccessible(true);
+            field.set(instance, convert(value, field.getGenericType()));
+        }
+    }
+    
+    private static class MethodPropertyDescriptor extends PropertyDescriptor {
+        private final Method getter;
+        private final Collection<Method> setters;
+
+        private MethodPropertyDescriptor(String name, Method getter, Collection<Method> setters) {
+            super(name);
+            this.getter = getter;
+            this.setters = (setters != null) ? setters : Collections.<Method>emptyList();
+        }
+        
+        public boolean allowsGet() {
+            return getter != null;
+        }
+        
+        public boolean allowsSet() {
+            return !!!setters.isEmpty();
+        }
+        
+        protected Object internalGet(Object instance) 
                 throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
-            if (setter != null) {
-                setter.invoke(instance, value);
-            } else if (field != null) {
-                field.setAccessible(true);
-                field.set(instance, value);
+            if (getter != null) {
+                return getter.invoke(instance);
             } else {
                 throw new UnsupportedOperationException();
             }
         }
         
-        public Type getGenericType() {
-            if (setter != null)
-                return setter.getGenericParameterTypes()[0];
-            else if (getter != null)
-                return getter.getGenericReturnType();
-            else 
-                return field.getGenericType();
+        protected void internalSet(Object instance, Object value) throws Exception {
+            
+            Method setterMethod = findSetter(value);
+
+            if (setterMethod != null) {
+                setterMethod.invoke(instance, convert(value, setterMethod.getGenericParameterTypes()[0]));
+            } else {
+                throw new ComponentDefinitionException(
+                        "No converter available to convert value "+value+" into a form applicable for the " + 
+                        "setters of property "+getName());
+            }
+        }
+        
+        private Method findSetter(Object value) {
+            Class<?> valueType = (value == null) ? null : value.getClass();
+            
+            Method result = findMethodByClass(valueType);
+            
+            if (result == null) result = findMethodWithConversion(value);
+                        
+            return result;
+        }
+        
+        private Method findMethodByClass(Class<?> arg) throws ComponentDefinitionException {
+            Method result = null;
+            
+            for (Method m : setters) {
+                Class<?> paramType = m.getParameterTypes()[0];
                 
+                if ((arg == null && Object.class.isAssignableFrom(paramType)) 
+                        || (arg != null && paramType.isAssignableFrom(arg))) {
+                    
+                    // pick the method that has the more specific parameter if any
+                    if (result != null) {
+                        Class<?> oldParamType = result.getParameterTypes()[0];
+                        if (paramType.isAssignableFrom(oldParamType)) {
+                            // do nothing, result is correct
+                        } else if (oldParamType.isAssignableFrom(paramType)) {
+                            result = m;
+                        } else {
+                            throw new ComponentDefinitionException(
+                                    "Ambiguous setter method for property "+getName()+
+                                    ". More than one method matches the  parameter type "+arg);
+                        }
+                    } else {
+                        result = m;
+                    }
+                }
+            }            
+            
+            return result;
+        }
+        
+        private Method findMethodWithConversion(Object value) throws ComponentDefinitionException {
+            ExecutionContext ctx = ExecutionContext.Holder.getContext();
+            List<Method> matchingMethods = new ArrayList<Method>();
+            for (Method m : setters) {
+                Type paramType = m.getGenericParameterTypes()[0];
+                if (ctx.canConvert(value, new GenericType(paramType))) matchingMethods.add(m);
+            }
+            
+            if (matchingMethods.isEmpty()) return null;
+            else if (matchingMethods.size() == 1) return matchingMethods.get(0);
+            else throw new ComponentDefinitionException(
+                    "Ambiguous setter method for property "+ getName() + 
+                    ". More than one method matches the parameter "+value+" after applying conversion.");
         }
         
         public String toString() {
-            return "PropertyDescriptor <name: "+name+", getter: "+getter+", setter: "+setter+
-                ", field: "+field+">";
+            return "PropertyDescriptor <name: "+getName()+", getter: "+getter+", setter: "+setters;
         }
     }
 

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/WiringTest.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/WiringTest.java?rev=978873&r1=978872&r2=978873&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/WiringTest.java (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/WiringTest.java Sat Jul 24 13:42:04 2010
@@ -35,6 +35,7 @@ import org.apache.aries.blueprint.contai
 import org.apache.aries.blueprint.di.CircularDependencyException;
 import org.apache.aries.blueprint.di.Repository;
 import org.apache.aries.blueprint.namespace.ComponentDefinitionRegistryImpl;
+import org.apache.aries.blueprint.pojos.AmbiguousPojo;
 import org.apache.aries.blueprint.pojos.BeanD;
 import org.apache.aries.blueprint.pojos.BeanF;
 import org.apache.aries.blueprint.pojos.FITestBean;
@@ -138,6 +139,19 @@ public class WiringTest extends Abstract
         assertEquals(true, pojob.getDestroyCalled());
     }
     
+    public void testSetterDisambiguation() throws Exception {
+        ComponentDefinitionRegistryImpl registry = parse("/test-wiring.xml");
+        Repository repository = new TestBlueprintContainer(registry).getRepository();
+
+        AmbiguousPojo pojo = (AmbiguousPojo) repository.create("ambiguousViaInt");
+        assertEquals(5, pojo.getSum());
+        
+        pojo = (AmbiguousPojo) repository.create("ambiguousViaList");
+        assertEquals(7, pojo.getSum());
+        
+        
+    }
+    
     public void testFieldInjection() throws Exception {
       ComponentDefinitionRegistryImpl registry = parse("/test-wiring.xml");
       Repository repository = new TestBlueprintContainer(registry).getRepository();

Added: incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/AmbiguousPojo.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/AmbiguousPojo.java?rev=978873&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/AmbiguousPojo.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/pojos/AmbiguousPojo.java Sat Jul 24 13:42:04 2010
@@ -0,0 +1,41 @@
+/*
+ * 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.aries.blueprint.pojos;
+
+import java.util.List;
+
+public class AmbiguousPojo {
+    private int sum;
+    
+    public int getSum() {
+        return sum;
+    }
+    
+    public void setSum(int sum) {
+        this.sum = sum;
+    }
+    
+    public void setSum(List<Integer> numbers) {
+        this.sum = 0;
+        for (int i : numbers) {
+            this.sum += i;
+        }
+    }
+    
+}

Added: incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/utils/ReflectionUtilsTest.java
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/utils/ReflectionUtilsTest.java?rev=978873&view=auto
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/utils/ReflectionUtilsTest.java (added)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/test/java/org/apache/aries/blueprint/utils/ReflectionUtilsTest.java Sat Jul 24 13:42:04 2010
@@ -0,0 +1,288 @@
+/*
+ * 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.aries.blueprint.utils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+import org.apache.aries.blueprint.di.CircularDependencyException;
+import org.apache.aries.blueprint.di.ExecutionContext;
+import org.apache.aries.blueprint.di.Recipe;
+import org.apache.aries.blueprint.utils.ReflectionUtils.PropertyDescriptor;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.osgi.service.blueprint.container.ComponentDefinitionException;
+import org.osgi.service.blueprint.container.ReifiedType;
+
+import static org.junit.Assert.*;
+
+public class ReflectionUtilsTest {
+    private PropertyDescriptor[] sut;
+    
+    static class GetterOnly {
+        public String getValue() { return "test"; }
+    }
+    
+    private class Inconvertible {}
+    
+    @BeforeClass
+    public static void before()
+    {
+        ExecutionContext.Holder.setContext(new ExecutionContext() {
+            public void addFullObject(String name, Object object) {}
+            public void addPartialObject(String name, Object object) {}
+            public boolean containsObject(String name) { return false; }
+
+            public Object convert(Object value, ReifiedType type) throws Exception {
+                if (type.getRawClass().equals(Inconvertible.class)) throw new Exception();
+                else if (type.getRawClass().equals(String.class)) return String.valueOf(value);
+                else if (type.getRawClass().equals(List.class)) {
+                    if (value == null) return null;
+                    else if (value instanceof Collection) return new ArrayList((Collection) value);
+                    else throw new Exception();
+                } else if (value == null) return null;
+                else if (type.getRawClass().isInstance(value)) return value;
+                else throw new Exception();
+            }
+            
+            public boolean canConvert(Object value, ReifiedType type) {
+                if (value instanceof Inconvertible) return false;
+                else if (type.getRawClass().equals(String.class)) return true;
+                else if (type.getRawClass().equals(List.class) && (value == null || value instanceof Collection)) return true;
+                else return false;
+            }
+
+            public Object getInstanceLock() { return null; }
+            public Object getObject(String name) { return null; }
+            public Object getPartialObject(String name) { return null; }
+            public Recipe getRecipe(String name) { return null; }
+            public Class loadClass(String className) throws ClassNotFoundException { return null; }
+            public Recipe pop() { return null; }
+            public void push(Recipe recipe) throws CircularDependencyException {}
+            public Object removePartialObject(String name) { return null; }            
+        });
+    }
+    
+    @Test
+    public void testGetterOnly() throws Exception {
+        loadProps(GetterOnly.class, true);
+        
+        assertEquals(2, sut.length);
+        assertEquals("class", sut[0].getName());
+        assertEquals("value", sut[1].getName());
+        
+        assertTrue(sut[1].allowsGet());
+        assertFalse(sut[1].allowsSet());
+        
+        assertEquals("test", sut[1].get(new GetterOnly(), null));
+    }
+    
+    static class SetterOnly {
+        private String f;
+        
+        public void setField(String val) { f = val; }
+        public String retrieve() { return f; }
+    }
+    
+    @Test
+    public void testSetterOnly() throws Exception {
+        loadProps(SetterOnly.class, false);
+        
+        assertEquals(2, sut.length);
+        assertEquals("field", sut[1].getName());
+        
+        assertFalse(sut[1].allowsGet());
+        assertTrue(sut[1].allowsSet());
+        
+        SetterOnly so = new SetterOnly();
+        sut[1].set(so, "trial", null);
+        assertEquals("trial", so.retrieve());
+    }
+    
+    static class SetterAndGetter {
+        private String f;
+        
+        public void setField(String val) { f = val; }
+        public String getField() { return f; }
+    }
+    
+    @Test
+    public void testSetterAndGetter() throws Exception {
+        loadProps(SetterAndGetter.class, false);
+        
+        assertEquals(2, sut.length);
+        assertEquals("field", sut[1].getName());
+        
+        assertTrue(sut[1].allowsGet());
+        assertTrue(sut[1].allowsSet());
+        
+        SetterAndGetter sag = new SetterAndGetter();
+        sut[1].set(sag, "tribulation", null);
+        assertEquals("tribulation", sut[1].get(sag, null));
+    }
+    
+    static class DuplicateGetter {
+        public boolean isField() { return true; }
+        public boolean getField() { return false; }
+    }
+    
+    @Test
+    public void testDuplicateGetter() {
+        loadProps(DuplicateGetter.class, false);
+        
+        assertEquals(1, sut.length);
+        assertEquals("class", sut[0].getName());
+    }
+    
+    static class FieldsAndProps {
+        private String hidden = "ordeal";
+        private String nonHidden;
+        
+        public String getHidden() { return hidden; }
+    }
+    
+    @Test
+    public void testFieldsAndProps() throws Exception {
+        loadProps(FieldsAndProps.class, true);
+        
+        assertEquals(3, sut.length);
+        
+        FieldsAndProps fap = new FieldsAndProps();
+        
+        // no mixing of setter and field injection
+        assertEquals("hidden", sut[1].getName());
+        assertTrue(sut[1].allowsGet());
+        assertTrue(sut[1].allowsSet());
+        
+        assertEquals("ordeal", sut[1].get(fap, null));
+        sut[1].set(fap, "calvary", null);
+        assertEquals("calvary", sut[1].get(fap, null));
+        
+        assertEquals("nonHidden", sut[2].getName());
+        assertTrue(sut[2].allowsGet());
+        assertTrue(sut[2].allowsSet());
+        
+        sut[2].set(fap, "predicament", null);
+        assertEquals("predicament", sut[2].get(fap, null));
+    }
+    
+    static class OverloadedSetters {
+        public Object field;
+        
+        public void setField(String val) { field = val; }
+        public void setField(List<String> val) { field = val; }
+    }
+    
+    @Test
+    public void testOverloadedSetters() throws Exception {
+        loadProps(OverloadedSetters.class, false);
+        
+        OverloadedSetters os = new OverloadedSetters();
+
+        sut[1].set(os, "scrutiny", null);
+        assertEquals("scrutiny", os.field);
+        
+        sut[1].set(os, Arrays.asList("evaluation"), null);
+        assertEquals(Arrays.asList("evaluation"), os.field);
+        
+        // conversion case, Integer -> String
+        sut[1].set(os, new Integer(3), null);
+        assertEquals("3", os.field);
+    }
+    
+    @Test(expected=ComponentDefinitionException.class)
+    public void testApplicableSetter() throws Exception {
+        loadProps(OverloadedSetters.class, false);
+        
+        sut[1].set(new OverloadedSetters(), new Inconvertible(), null);
+    }
+    
+    static class MultipleMatchesByConversion {
+        public void setField(String s) {}
+        public void setField(List<String> list) {}
+    }
+    
+    @Test(expected=ComponentDefinitionException.class)
+    public void testMultipleMatchesByConversion() throws Exception {
+        loadProps(MultipleMatchesByConversion.class, false);
+        
+        sut[1].set(new MultipleMatchesByConversion(), new HashSet<String>(), null);
+    }
+    
+    static class MultipleMatchesByType {
+        public void setField(List<String> list) {}
+        public void setField(Queue<String> list) {}
+        
+        public static int field;
+        
+        public void setOther(Collection<String> list) { field=1; }
+        public void setOther(List<String> list) { field=2; }
+    }
+    
+    @Test(expected=ComponentDefinitionException.class)
+    public void testMultipleSettersMatchByType() throws Exception {
+        loadProps(MultipleMatchesByType.class, false);
+        
+        sut[1].set(new MultipleMatchesByType(), new LinkedList<String>(), null);
+    }
+    
+    @Test
+    public void testDisambiguationByHierarchy() throws Exception {
+        loadProps(MultipleMatchesByType.class, false);
+        
+        sut[2].set(new MultipleMatchesByType(), new ArrayList<String>(), null);
+        assertEquals(2, MultipleMatchesByType.field);
+    }
+    
+    static class NullSetterDisambiguation {
+        public static int field;
+        
+        public void setField(int i) { field = i; }
+        public void setField(Integer i) { field = -1; }
+    }
+    
+    @Test
+    public void testNullDisambiguation() throws Exception {
+        loadProps(NullSetterDisambiguation.class, false);
+        
+        sut[1].set(new NullSetterDisambiguation(), null, null);
+        assertEquals(-1, NullSetterDisambiguation.field);
+    }
+    
+    private void loadProps(Class<?> clazz, boolean allowsFieldInjection)
+    {
+        List<PropertyDescriptor> props = new ArrayList<PropertyDescriptor>(
+                Arrays.asList(ReflectionUtils.getPropertyDescriptors(clazz, allowsFieldInjection)));
+        
+        Collections.sort(props, new Comparator<PropertyDescriptor>() {
+            public int compare(PropertyDescriptor o1, PropertyDescriptor o2) {
+                return o1.getName().compareTo(o2.getName());
+            }
+        });
+        
+        sut = props.toArray(new PropertyDescriptor[0]);
+    }
+}

Modified: incubator/aries/trunk/blueprint/blueprint-core/src/test/resources/test-wiring.xml
URL: http://svn.apache.org/viewvc/incubator/aries/trunk/blueprint/blueprint-core/src/test/resources/test-wiring.xml?rev=978873&r1=978872&r2=978873&view=diff
==============================================================================
--- incubator/aries/trunk/blueprint/blueprint-core/src/test/resources/test-wiring.xml (original)
+++ incubator/aries/trunk/blueprint/blueprint-core/src/test/resources/test-wiring.xml Sat Jul 24 13:42:04 2010
@@ -131,5 +131,18 @@
     <bean id="FIFailureTest2Bean" class="org.apache.aries.blueprint.pojos.FITestBean" ext:field-injection="false">
         <property name="attr" value="value" />
     </bean>
+    
+    <bean id="ambiguousViaInt" class="org.apache.aries.blueprint.pojos.AmbiguousPojo">
+    	<property name="sum" value="5" />
+    </bean>
+    
+    <bean id="ambiguousViaList" class="org.apache.aries.blueprint.pojos.AmbiguousPojo">
+    	<property name="sum">
+    		<list>
+    			<value>3</value>
+    			<value>4</value>
+    		</list>
+    	</property>
+    </bean>
 
 </blueprint>
\ No newline at end of file