geronimo-xbean-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From d...@apache.org
Subject svn commit: r632095 - in /geronimo/xbean/trunk/xbean-reflect/src: main/java/org/apache/xbean/parameter/ main/java/org/apache/xbean/recipe/ test/java/org/apache/xbean/parameter/ test/java/org/apache/xbean/recipe/
Date Thu, 28 Feb 2008 19:26:31 GMT
Author: dain
Date: Thu Feb 28 11:26:28 2008
New Revision: 632095

URL: http://svn.apache.org/viewvc?rev=632095&view=rev
Log:
Added support for named constructor args and static factory parameters

Added:
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/AsmParameterNameLoader.java
      - copied, changed from r630653, geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/AsmParameterNames.java
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ParameterNameLoader.java
      - copied, changed from r630653, geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/ParameterNames.java
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ParameterNames.java
    geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ParameterNameLoaderTest.java
      - copied, changed from r630653, geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/parameter/ParameterNamesTest.java
Removed:
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/Construction.java
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ConstructionStrategy.java
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ExplicitConstruction.java
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ExplicitConstructionStrategy.java
    geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/parameter/
Modified:
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ObjectRecipe.java
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/Option.java
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java
    geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java
    geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ObjectRecipeTest.java
    geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/Person.java
    geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/PersonFactory.java

Copied: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/AsmParameterNameLoader.java (from r630653, geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/AsmParameterNames.java)
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/AsmParameterNameLoader.java?p2=geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/AsmParameterNameLoader.java&p1=geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/AsmParameterNames.java&r1=630653&r2=632095&rev=632095&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/AsmParameterNames.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/AsmParameterNameLoader.java Thu Feb 28 11:26:28 2008
@@ -15,7 +15,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package org.apache.xbean.parameter;
+package org.apache.xbean.recipe;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -28,6 +28,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
+import java.util.Arrays;
 
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.Label;
@@ -36,12 +37,12 @@
 import org.objectweb.asm.commons.EmptyVisitor;
 
 /**
- * Implementation of ParameterNames that uses ASM to read the parameter names from the local variable table in the
+ * Implementation of ParameterNameLoader that uses ASM to read the parameter names from the local variable table in the
  * class byte code.
  *
  * This wonderful piece of code was taken from org.springframework.core.LocalVariableTableParameterNameDiscover
  */
-public class AsmParameterNames implements ParameterNames {
+public class AsmParameterNameLoader implements ParameterNameLoader {
     /**
      * Weak map from Constructor to List<String>.
      */
@@ -89,13 +90,14 @@
      */
     public Map<Constructor,List<String>> getAllConstructorParameters(Class clazz) {
         // Determine the constructors?
-        Constructor[] constructors = clazz.getConstructors();
-        if (constructors.length == 0) {
+        List<Constructor> constructors = new ArrayList<Constructor>(Arrays.asList(clazz.getConstructors()));
+        constructors.addAll(Arrays.asList(clazz.getDeclaredConstructors()));
+        if (constructors.isEmpty()) {
             return Collections.emptyMap();
         }
 
         // Check the cache
-        if (constructorCache.containsKey(constructors[0])) {
+        if (constructorCache.containsKey(constructors.get(0))) {
             Map<Constructor,List<String>> constructorParameters = new HashMap<Constructor,List<String>>();
             for (Constructor constructor : constructors) {
                 constructorParameters.put(constructor, constructorCache.get(constructor));
@@ -106,9 +108,9 @@
         // Load the parameter names using ASM
         Map<Constructor,List<String>> constructorParameters = new HashMap<Constructor,List<String>> ();
         try {
-            ClassReader reader = AsmParameterNames.createClassReader(clazz);
+            ClassReader reader = AsmParameterNameLoader.createClassReader(clazz);
 
-            AsmParameterNames.AllParameterNamesDiscoveringVisitor visitor = new AsmParameterNames.AllParameterNamesDiscoveringVisitor(clazz);
+            AsmParameterNameLoader.AllParameterNamesDiscoveringVisitor visitor = new AsmParameterNameLoader.AllParameterNamesDiscoveringVisitor(clazz);
             reader.accept(visitor, false);
 
             Map exceptions = visitor.getExceptions();
@@ -155,9 +157,9 @@
         // Load the parameter names using ASM
         Map<Method,List<String>>  methodParameters = new HashMap<Method,List<String>>();
         try {
-            ClassReader reader = AsmParameterNames.createClassReader(clazz);
+            ClassReader reader = AsmParameterNameLoader.createClassReader(clazz);
 
-            AsmParameterNames.AllParameterNamesDiscoveringVisitor visitor = new AsmParameterNames.AllParameterNamesDiscoveringVisitor(clazz, methodName);
+            AsmParameterNameLoader.AllParameterNamesDiscoveringVisitor visitor = new AsmParameterNameLoader.AllParameterNamesDiscoveringVisitor(clazz, methodName);
             reader.accept(visitor, false);
 
             Map exceptions = visitor.getExceptions();
@@ -180,8 +182,9 @@
     }
 
     private Method[] getMethods(Class clazz, String methodName) {
-        Method[] methods = clazz.getMethods();
-        List<Method> matchingMethod = new ArrayList<Method>(methods.length);
+        List<Method> methods = new ArrayList<Method>(Arrays.asList(clazz.getMethods()));
+        methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
+        List<Method> matchingMethod = new ArrayList<Method>(methods.size());
         for (Method method : methods) {
             if (method.getName().equals(methodName)) {
                 matchingMethod.add(method);
@@ -218,7 +221,8 @@
         public AllParameterNamesDiscoveringVisitor(Class type, String methodName) {
             this.methodName = methodName;
 
-            Method[] methods = type.getMethods();
+            List<Method> methods = new ArrayList<Method>(Arrays.asList(type.getMethods()));
+            methods.addAll(Arrays.asList(type.getDeclaredMethods()));
             for (Method method : methods) {
                 if (method.getName().equals(methodName)) {
                     methodMap.put(Type.getMethodDescriptor(method), method);
@@ -229,7 +233,8 @@
         public AllParameterNamesDiscoveringVisitor(Class type) {
             this.methodName = "<init>";
 
-            Constructor[] constructors = type.getConstructors();
+            List<Constructor> constructors = new ArrayList<Constructor>(Arrays.asList(type.getConstructors()));
+            constructors.addAll(Arrays.asList(type.getDeclaredConstructors()));
             for (Constructor constructor : constructors) {
                 Type[] types = new Type[constructor.getParameterTypes().length];
                 for (int j = 0; j < types.length; j++) {

Modified: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ObjectRecipe.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ObjectRecipe.java?rev=632095&r1=632094&r2=632095&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ObjectRecipe.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ObjectRecipe.java Thu Feb 28 11:26:28 2008
@@ -22,10 +22,14 @@
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import org.apache.xbean.recipe.ReflectionUtil.*;
 
 /**
  * @version $Rev: 6688 $ $Date: 2005-12-29T02:08:29.200064Z $
@@ -33,10 +37,9 @@
 public class ObjectRecipe extends AbstractRecipe {
     private String typeName;
     private Class typeClass;
-    private ConstructionStrategy constructionStrategy;
     private String factoryMethod;
-    private String[] constructorArgNames;
-    private Class[] constructorArgTypes;
+    private List<String> constructorArgNames;
+    private List<Class<?>> constructorArgTypes;
     private final LinkedHashMap<Property,Object> properties = new LinkedHashMap<Property,Object>();
     private final EnumSet<Option> options = EnumSet.of(Option.FIELD_INJECTION);
     private final Map<String,Object> unsetProperties = new LinkedHashMap<String,Object>();
@@ -72,9 +75,8 @@
     public ObjectRecipe(Class typeClass, String factoryMethod, String[] constructorArgNames, Class[] constructorArgTypes, Map<String,Object> properties) {
         this.typeClass = typeClass;
         this.factoryMethod = factoryMethod;
-        this.constructorArgNames = constructorArgNames;
-        this.constructorArgTypes = constructorArgTypes;
-        constructionStrategy = new ExplicitConstructionStrategy();
+        this.constructorArgNames = constructorArgNames != null ? Arrays.asList(constructorArgNames) : null;
+        this.constructorArgTypes = constructorArgTypes != null ? Arrays.<Class<?>>asList(constructorArgTypes) : null;
         if (properties != null) {
             setAllProperties(properties);
         }
@@ -111,9 +113,8 @@
     public ObjectRecipe(String typeName, String factoryMethod, String[] constructorArgNames, Class[] constructorArgTypes, Map<String,Object> properties) {
         this.typeName = typeName;
         this.factoryMethod = factoryMethod;
-        this.constructorArgNames = constructorArgNames;
-        this.constructorArgTypes = constructorArgTypes;
-        constructionStrategy = new ExplicitConstructionStrategy();
+        this.constructorArgNames = constructorArgNames != null ? Arrays.asList(constructorArgNames) : null;
+        this.constructorArgTypes = constructorArgTypes != null ? Arrays.<Class<?>>asList(constructorArgTypes) : null;
         if (properties != null) {
             setAllProperties(properties);
         }
@@ -127,28 +128,32 @@
         options.remove(option);
     }
 
-    public ConstructionStrategy getConstructionStrategy() {
-        return constructionStrategy;
+    public Set<Option> getOptions() {
+        return Collections.unmodifiableSet(options);
     }
 
-    public void setConstructionStrategy(ConstructionStrategy constructionStrategy) {
-        this.constructionStrategy = constructionStrategy;
-    }
-
-    public String[] getConstructorArgNames() {
+    public List<String> getConstructorArgNames() {
         return constructorArgNames;
     }
 
     public void setConstructorArgNames(String[] constructorArgNames) {
+        this.constructorArgNames = constructorArgNames != null ? Arrays.asList(constructorArgNames) : null;
+    }
+
+    public void setConstructorArgNames(List<String> constructorArgNames) {
         this.constructorArgNames = constructorArgNames;
     }
 
-    public Class[] getConstructorArgTypes() {
+    public List<Class<?>> getConstructorArgTypes() {
         return constructorArgTypes;
     }
 
     public void setConstructorArgTypes(Class[] constructorArgTypes) {
-        this.constructorArgTypes = constructorArgTypes;
+        this.constructorArgTypes = constructorArgTypes != null ? Arrays.<Class<?>>asList(constructorArgTypes) : null;
+    }
+
+    public void setConstructorArgTypes(List<? extends Class<?>> constructorArgTypes) {
+        this.constructorArgTypes = new ArrayList<Class<?>>(constructorArgTypes);
     }
 
     /**
@@ -232,10 +237,14 @@
     }
 
     public List<Recipe> getConstructorRecipes() {
-        Construction construction = constructionStrategy.getConstruction(this, Object.class);
-        if (!construction.hasInstanceFactory()) {
+        // find the factory that will be used to create the class instance
+        Factory factory = findFactory(Object.class);
+
+        // if we are NOT using an instance factory to create the object
+        // (we have a factory method and it is not a static factory method)
+        if (factoryMethod != null && !(factory instanceof StaticFactory)) {
             // only include recipes used in the construcor args
-            List<String> parameterNames = construction.getParameterNames();
+            List<String> parameterNames = factory.getParameterNames();
             List<Recipe> nestedRecipes = new ArrayList<Recipe>(parameterNames.size());
             for (Map.Entry<Property, Object> entry : properties.entrySet()) {
                 if (parameterNames.contains(entry.getKey().name) && entry.getValue() instanceof Recipe) {
@@ -257,40 +266,53 @@
 
     protected Object internalCreate(Type expectedType, boolean lazyRefAllowed) throws ConstructionException {
         unsetProperties.clear();
+
+        //
         // load the type class
         Class typeClass = getType();
 
-        // verify that it is a class we can construct
-        if (!Modifier.isPublic(typeClass.getModifiers())) {
-            throw new ConstructionException("Class is not public: " + typeClass.getName());
-        }
-        if (Modifier.isInterface(typeClass.getModifiers())) {
-            throw new ConstructionException("Class is an interface: " + typeClass.getName());
-        }
-        if (Modifier.isAbstract(typeClass.getModifiers())) {
-            throw new ConstructionException("Class is abstract: " + typeClass.getName());
-        }
-
+        //
         // clone the properties so they can be used again
         Map<Property,Object> propertyValues = new LinkedHashMap<Property,Object>(properties);
 
+        //
         // create the instance
-        Construction construction = constructionStrategy.getConstruction(this, expectedType);
-        Object[] parameters = extractConstructorArgs(propertyValues,
-                construction.getParameterNames(),
-                construction.getParameterTypes());
-        Object instance = construction.create(parameters);
+        Factory factory = findFactory(expectedType);
+        Object[] parameters = extractConstructorArgs(propertyValues, factory);
+        Object instance = factory.create(parameters);
 
+        //
         // add to execution context if name is specified
         if (getName() != null) {
             ExecutionContext.getContext().addObject(getName(), instance);
         }
-       
+
+        //
         // set the properties
         setProperties(propertyValues, instance, instance.getClass());
 
+        //
         // call instance factory method
-        instance = construction.callInstanceFactory(instance);
+
+        // if we have a factory method name and did not find a static factory,
+        // then we have an instance factory
+        if (factoryMethod != null && !(factory instanceof StaticFactory)) {
+            // find the instance factory method
+            Method instanceFactory = ReflectionUtil.findInstanceFactory(instance.getClass(), factoryMethod, null);
+
+            try {
+                instance = instanceFactory.invoke(instance);
+            } catch (Exception e) {
+                Throwable t = e;
+                if (e instanceof InvocationTargetException) {
+                    InvocationTargetException invocationTargetException = (InvocationTargetException) e;
+                    if (invocationTargetException.getCause() != null) {
+                        t = invocationTargetException.getCause();
+                    }
+                }
+                throw new ConstructionException("Error calling instance factory method: " + instanceFactory, t);
+            }
+        }
 
         return instance;
     }
@@ -403,11 +425,56 @@
         }
     }
 
-    private Object[] extractConstructorArgs(Map propertyValues, List<String> argNames, List<Type> argTypes) {
-        Object[] parameters = new Object[argNames.size()];
-        for (int i = 0; i < argNames.size(); i++) {
-            Property name = new Property(argNames.get(i));
-            Type type = argTypes.get(i);
+    private Factory findFactory(Type expectedType) {
+        Class type = getType();
+
+        //
+        // attempt to find a static factory
+        if (factoryMethod != null) {
+            try {
+                StaticFactory staticFactory = ReflectionUtil.findStaticFactory(
+                        type,
+                        factoryMethod,
+                        constructorArgNames,
+                        constructorArgTypes,
+                        getProperties().keySet(),
+                        options);
+                return staticFactory;
+            } catch (MissingFactoryMethodException ignored) {
+            }
+
+        }
+
+        //
+        // factory was not found, look for a constuctor
+
+        // if expectedType is a subclass of the assigned type, we create
+        // the sub class instead
+        Class consturctorClass;
+        if (RecipeHelper.isAssignable(type, expectedType)) {
+            consturctorClass = RecipeHelper.toClass(expectedType);
+        } else {
+            consturctorClass = type;
+        }
+
+        ConstructorFactory constructor = ReflectionUtil.findConstructor(
+                consturctorClass,
+                constructorArgNames,
+                constructorArgTypes,
+                getProperties().keySet(),
+                options);
+
+        return constructor;
+    }
+
+    private Object[] extractConstructorArgs(Map propertyValues, Factory factory) {
+        List<String> parameterNames = factory.getParameterNames();
+        List<Type> parameterTypes = factory.getParameterTypes();
+
+        Object[] parameters = new Object[parameterNames.size()];
+        for (int i = 0; i < parameterNames.size(); i++) {
+            Property name = new Property(parameterNames.get(i));
+            Type type = parameterTypes.get(i);
 
             Object value;
             if (propertyValues.containsKey(name)) {

Modified: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/Option.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/Option.java?rev=632095&r1=632094&r2=632095&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/Option.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/Option.java Thu Feb 28 11:26:28 2008
@@ -28,5 +28,6 @@
     LAZY_ASSIGNMENT,
     PRIVATE_CONSTRUCTOR,
     PRIVATE_FACTORY,
-    CASE_INSENSITIVE_FACTORY,    
+    CASE_INSENSITIVE_FACTORY,
+    NAMED_PARAMETERS
 }

Copied: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ParameterNameLoader.java (from r630653, geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/ParameterNames.java)
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ParameterNameLoader.java?p2=geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ParameterNameLoader.java&p1=geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/ParameterNames.java&r1=630653&r2=632095&rev=632095&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/parameter/ParameterNames.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ParameterNameLoader.java Thu Feb 28 11:26:28 2008
@@ -15,7 +15,7 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package org.apache.xbean.parameter;
+package org.apache.xbean.recipe;
 
 import java.util.List;
 import java.util.Map;
@@ -25,7 +25,7 @@
 /**
  * Determines the parameter names of Constructors or Methods.
  */
-public interface ParameterNames {
+public interface ParameterNameLoader {
     /**
      * Gets the parameter names of the specified method or null if the class was compiled without debug symbols on.
      * @param method the method for which the parameter names should be retrieved
@@ -39,19 +39,4 @@
      * @return the parameter names or null if the class was compiled without debug symbols on
      */
     List<String> get(Constructor constructor);
-
-    /**
-     * Gets the parameter names of all constructoror null if the class was compiled without debug symbols on.
-     * @param clazz the class for which the constructor parameter names should be retrieved
-     * @return a map from Constructor object to the parameter names or null if the class was compiled without debug symbols on
-     */
-    Map<Constructor,List<String>> getAllConstructorParameters(Class clazz);
-
-    /**
-     * Gets the parameter names of all methods with the specified name or null if the class was compiled without debug symbols on.
-     * @param clazz the class for which the method parameter names should be retrieved
-     * @param methodName the of the method for which the parameters should be retrieved
-     * @return a map from Method object to the parameter names or null if the class was compiled without debug symbols on
-     */
-    Map<Method,List<String>> getAllMethodParameters(Class clazz, String methodName);
 }

Added: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ParameterNames.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ParameterNames.java?rev=632095&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ParameterNames.java (added)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ParameterNames.java Thu Feb 28 11:26:28 2008
@@ -0,0 +1,19 @@
+package org.apache.xbean.recipe;
+
+import java.lang.annotation.Documented;
+import static java.lang.annotation.ElementType.*;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ * ParameterNames explicitly declared the names of a constructor parameters.
+ * This annotation was introduced in Java6 and is present here to provide backwards
+ * compatability when running on Java5
+ */
+@Documented
+@Retention(value = RUNTIME)
+@Target(value = {CONSTRUCTOR, METHOD})
+public @interface ParameterNames {
+    String[] value();
+}
\ No newline at end of file

Modified: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java?rev=632095&r1=632094&r2=632095&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java Thu Feb 28 11:26:28 2008
@@ -16,21 +16,21 @@
  */
 package org.apache.xbean.recipe;
 
-import org.apache.xbean.propertyeditor.PropertyEditors;
-
-import java.lang.reflect.Modifier;
+import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
-import java.lang.reflect.Type;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Modifier;
 import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
-import java.lang.reflect.GenericArrayType;
-import java.lang.reflect.Array;
-import java.util.Comparator;
-import java.util.Map;
-import java.util.List;
-import java.util.Collections;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.xbean.propertyeditor.PropertyEditors;
 
 /**
  * @version $Rev: 6687 $ $Date: 2005-12-28T21:08:56.733437Z $
@@ -171,13 +171,13 @@
         return value;
     }
 
-    public static boolean isAssignableFrom(Class[] expectedTypes, Class[] actualTypes) {
-        if (expectedTypes.length != actualTypes.length) {
+    public static boolean isAssignableFrom(List<? extends Class<?>> expectedTypes, List<? extends Class<?>> actualTypes) {
+        if (expectedTypes.size() != actualTypes.size()) {
             return false;
         }
-        for (int i = 0; i < expectedTypes.length; i++) {
-            Class expectedType = expectedTypes[i];
-            Class actualType = actualTypes[i];
+        for (int i = 0; i < expectedTypes.size(); i++) {
+            Class expectedType = expectedTypes.get(i);
+            Class actualType = actualTypes.get(i);
             if (expectedType != actualType && !isAssignableFrom(expectedType, actualType)) {
                 return false;
             }

Modified: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java?rev=632095&r1=632094&r2=632095&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java Thu Feb 28 11:26:28 2008
@@ -20,22 +20,37 @@
 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.lang.annotation.Annotation;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Set;
 
 import static org.apache.xbean.recipe.RecipeHelper.isAssignableFrom;
 
 public final class ReflectionUtil {
+    private static ParameterNameLoader parameterNamesLoader;
+    static {
+        try {
+            Class<? extends ParameterNameLoader> loaderClass = ReflectionUtil.class.getClassLoader().loadClass("org.apache.xbean.recipe.AsmParameterNameLoader").asSubclass(ParameterNameLoader.class);
+            parameterNamesLoader = loaderClass.newInstance();
+        } catch (Throwable ignored) {
+        }
+    }
+
     private ReflectionUtil() {
     }
 
-    public static Field findField(Class typeClass, String propertyName, Object propertyValue, EnumSet<Option> options) {
+    public static Field findField(Class typeClass, String propertyName, Object propertyValue, Set<Option> options) {
         if (typeClass == null) throw new NullPointerException("typeClass is null");
         if (propertyName == null) throw new NullPointerException("name is null");
         if (propertyName.length() == 0) throw new IllegalArgumentException("name is an empty string");
@@ -133,7 +148,7 @@
         }
     }
 
-    public static Method findSetter(Class typeClass, String propertyName, Object propertyValue, EnumSet<Option> options) {
+    public static Method findSetter(Class typeClass, String propertyName, Object propertyValue, Set<Option> options) {
         if (typeClass == null) throw new NullPointerException("typeClass is null");
         if (propertyName == null) throw new NullPointerException("name is null");
         if (propertyName.length() == 0) throw new IllegalArgumentException("name is an empty string");
@@ -263,37 +278,86 @@
         }
     }
 
-    public static Constructor findConstructor(Class typeClass, Class[] argTypes, EnumSet<Option> options) {
+    public static ConstructorFactory findConstructor(Class typeClass, List<? extends Class<?>> parameterTypes, Set<Option> options) {
+        return findConstructor(typeClass, null, parameterTypes, null, options);
+
+    }
+    public static ConstructorFactory findConstructor(Class typeClass, List<String> parameterNames, List<? extends Class<?>> parameterTypes, Set<String> availableProperties, Set<Option> options) {
         if (typeClass == null) throw new NullPointerException("typeClass is null");
-        if (argTypes == null) throw new NullPointerException("argTypes is null");
+        if (availableProperties == null) availableProperties = Collections.emptySet();
         if (options == null) options = EnumSet.noneOf(Option.class);
 
-        int matchLevel = 0;
-        MissingFactoryMethodException missException = null;
+        //
+        // verify that it is a class we can construct
+        if (!Modifier.isPublic(typeClass.getModifiers())) {
+            throw new ConstructionException("Class is not public: " + typeClass.getName());
+        }
+        if (Modifier.isInterface(typeClass.getModifiers())) {
+            throw new ConstructionException("Class is an interface: " + typeClass.getName());
+        }
+        if (Modifier.isAbstract(typeClass.getModifiers())) {
+            throw new ConstructionException("Class is abstract: " + typeClass.getName());
+        }
 
-        boolean allowPrivate = options.contains(Option.PRIVATE_CONSTRUCTOR);
+        // verify parameter names and types are the same length
+        if (parameterNames != null) {
+            if (parameterTypes == null) parameterTypes = Collections.nCopies(parameterNames.size(), null);
+            if (parameterNames.size() != parameterTypes.size()) {
+                throw new ConstructionException("Invalid ObjectRecipe: recipe has " + parameterNames.size() +
+                        " parameter names and " + parameterTypes.size() + " parameter types");
+            }
+        } else if (!options.contains(Option.NAMED_PARAMETERS)) {
+            // Named parameters are not supported and no explicit parameters were given,
+            // so we will only use the no-arg constructor
+            parameterNames = Collections.emptyList();
+            parameterTypes = Collections.emptyList();
+        }
 
+
+        // get all methods sorted so that the methods with the most constructor args are first
         List<Constructor> constructors = new ArrayList<Constructor>(Arrays.asList(typeClass.getConstructors()));
         constructors.addAll(Arrays.asList(typeClass.getDeclaredConstructors()));
+        Collections.sort(constructors, new Comparator<Constructor>() {
+            public int compare(Constructor constructor1, Constructor constructor2) {
+                return constructor2.getParameterTypes().length - constructor1.getParameterTypes().length;
+            }
+        });
+
+        // as we check each constructor, we remember the closest invalid match so we can throw a nice exception to the user
+        int matchLevel = 0;
+        MissingFactoryMethodException missException = null;
+
+        boolean allowPrivate = options.contains(Option.PRIVATE_CONSTRUCTOR);
         for (Constructor constructor : constructors) {
-            if (constructor.getParameterTypes().length != argTypes.length) {
-                if (matchLevel < 1) {
-                    matchLevel = 1;
-                    missException = new MissingFactoryMethodException("Constructor has " + constructor.getParameterTypes().length + " arugments " +
-                            "but expected " + argTypes.length + " arguments: " + constructor);
+            // if an explicit constructor is specified (via parameter types), look a constructor that matches
+            if (parameterTypes != null) {
+                if (constructor.getParameterTypes().length != parameterTypes.size()) {
+                    if (matchLevel < 1) {
+                        matchLevel = 1;
+                        missException = new MissingFactoryMethodException("Constructor has " + constructor.getParameterTypes().length + " arugments " +
+                                "but expected " + parameterTypes.size() + " arguments: " + constructor);
+                    }
+                    continue;
                 }
-                continue;
-            }
 
-            if (!isAssignableFrom(argTypes, constructor.getParameterTypes())) {
-                if (matchLevel < 2) {
-                    matchLevel = 2;
-                    missException = new MissingFactoryMethodException("Constructor has signature " +
-                            "public static " + typeClass.getName() + toParameterList(constructor.getParameterTypes()) +
-                            " but expected signature " +
-                            "public static " + typeClass.getName() + toParameterList(argTypes));
+                if (!isAssignableFrom(parameterTypes, Arrays.<Class<?>>asList(constructor.getParameterTypes()))) {
+                    if (matchLevel < 2) {
+                        matchLevel = 2;
+                        missException = new MissingFactoryMethodException("Constructor has signature " +
+                                "public static " + typeClass.getName() + toParameterList(constructor.getParameterTypes()) +
+                                " but expected signature " +
+                                "public static " + typeClass.getName() + toParameterList(parameterTypes));
+                    }
+                    continue;
+                }
+            } else {
+                // Implicit constructor selection based on named constructor args
+                //
+                // Only consider methods where we can supply a value for all of the parameters
+                parameterNames = getParameterNames(constructor);
+                if (parameterNames == null || !availableProperties.containsAll(parameterNames)) {
+                    continue;
                 }
-                continue;
             }
 
             if (Modifier.isAbstract(constructor.getModifiers())) {
@@ -316,93 +380,142 @@
                 setAccessible(constructor);
             }
 
-            return constructor;
+            return new ConstructorFactory(constructor, parameterNames);
         }
 
         if (missException != null) {
             throw missException;
         } else {
             StringBuffer buffer = new StringBuffer("Unable to find a valid constructor: ");
-            buffer.append("public void ").append(typeClass.getName()).append(toParameterList(argTypes));
+            buffer.append("public void ").append(typeClass.getName()).append(toParameterList(parameterTypes));
             throw new ConstructionException(buffer.toString());
         }
     }
 
-    public static Method findStaticFactory(Class typeClass, String factoryMethod, Class[] argTypes, EnumSet<Option> options) {
+    public static StaticFactory findStaticFactory(Class typeClass, String factoryMethod, List<? extends Class<?>>  parameterTypes, Set<Option> options) {
+        return findStaticFactory(typeClass, factoryMethod, null, parameterTypes, null, options);
+    }
+
+    public static StaticFactory findStaticFactory(Class typeClass, String factoryMethod, List<String> parameterNames, List<? extends Class<?>> parameterTypes, Set<String> allProperties, Set<Option> options) {
         if (typeClass == null) throw new NullPointerException("typeClass is null");
         if (factoryMethod == null) throw new NullPointerException("name is null");
         if (factoryMethod.length() == 0) throw new IllegalArgumentException("name is an empty string");
-        if (argTypes == null) throw new NullPointerException("argTypes is null");
+        if (allProperties == null) allProperties = Collections.emptySet();
         if (options == null) options = EnumSet.noneOf(Option.class);
 
+        //
+        // verify that it is a class we can construct
+        if (!Modifier.isPublic(typeClass.getModifiers())) {
+            throw new ConstructionException("Class is not public: " + typeClass.getName());
+        }
+        if (Modifier.isInterface(typeClass.getModifiers())) {
+            throw new ConstructionException("Class is an interface: " + typeClass.getName());
+        }
+
+        // verify parameter names and types are the same length
+        if (parameterNames != null) {
+            if (parameterTypes == null) parameterTypes = Collections.nCopies(parameterNames.size(), null);
+            if (parameterNames.size() != parameterTypes.size()) {
+                throw new ConstructionException("Invalid ObjectRecipe: recipe has " + parameterNames.size() +
+                        " parameter names and " + parameterTypes.size() + " parameter types");
+            }
+        } else if (!options.contains(Option.NAMED_PARAMETERS)) {
+            // Named parameters are not supported and no explicit parameters were given,
+            // so we will only use the no-arg constructor
+            parameterNames = Collections.emptyList();
+            parameterTypes = Collections.emptyList();
+        }
+
+        // get all methods sorted so that the methods with the most constructor args are first
+        List<Method> methods = new ArrayList<Method>(Arrays.asList(typeClass.getMethods()));
+        methods.addAll(Arrays.asList(typeClass.getDeclaredMethods()));
+        Collections.sort(methods, new Comparator<Method>() {
+            public int compare(Method method2, Method method1) {
+                return method1.getParameterTypes().length - method2.getParameterTypes().length;
+            }
+        });
+
+
+        // as we check each constructor, we remember the closest invalid match so we can throw a nice exception to the user
         int matchLevel = 0;
         MissingFactoryMethodException missException = null;
 
         boolean allowPrivate = options.contains(Option.PRIVATE_FACTORY);
         boolean caseInsesnitive = options.contains(Option.CASE_INSENSITIVE_FACTORY);
-
-        List<Method> methods = new ArrayList<Method>(Arrays.asList(typeClass.getMethods()));
-        methods.addAll(Arrays.asList(typeClass.getDeclaredMethods()));
         for (Method method : methods) {
-            if (method.getName().equals(factoryMethod) || (caseInsesnitive && method.getName().equalsIgnoreCase(method.getName()))) {
-                if (method.getParameterTypes().length != argTypes.length) {
+            // Only consider methods where the name matches
+            if (!method.getName().equals(factoryMethod) && (!caseInsesnitive || !method.getName().equalsIgnoreCase(method.getName()))) {
+                continue;
+            }
+
+            // if an explicit constructor is specified (via parameter types), look a constructor that matches
+            if (parameterTypes != null) {
+                if (method.getParameterTypes().length != parameterTypes.size()) {
                     if (matchLevel < 1) {
                         matchLevel = 1;
                         missException = new MissingFactoryMethodException("Static factory method has " + method.getParameterTypes().length + " arugments " +
-                                "but expected " + argTypes.length + " arguments: " + method);
+                                "but expected " + parameterTypes.size() + " arguments: " + method);
                     }
                     continue;
                 }
 
-                if (!isAssignableFrom(argTypes, method.getParameterTypes())) {
+                if (!isAssignableFrom(parameterTypes, Arrays.asList(method.getParameterTypes()))) {
                     if (matchLevel < 2) {
                         matchLevel = 2;
                         missException = new MissingFactoryMethodException("Static factory method has signature " +
                                 "public static " + typeClass.getName() + "." + factoryMethod + toParameterList(method.getParameterTypes()) +
                                 " but expected signature " +
-                                "public static " + typeClass.getName() + "." + factoryMethod + toParameterList(argTypes));
+                                "public static " + typeClass.getName() + "." + factoryMethod + toParameterList(parameterTypes));
                     }
                     continue;
                 }
-
-                if (method.getReturnType() == Void.TYPE) {
-                    if (matchLevel < 3) {
-                        matchLevel = 3;
-                        missException = new MissingFactoryMethodException("Static factory method does not return a value: " + method);
-                    }
+            } else {
+                // Implicit constructor selection based on named constructor args
+                //
+                // Only consider methods where we can supply a value for all of the parameters
+                parameterNames = getParameterNames(method);
+                if (parameterNames == null || !allProperties.containsAll(parameterNames)) {
                     continue;
                 }
+            }
 
-                if (Modifier.isAbstract(method.getModifiers())) {
-                    if (matchLevel < 4) {
-                        matchLevel = 4;
-                        missException = new MissingFactoryMethodException("Static factory method is abstract: " + method);
-                    }
-                    continue;
+            if (method.getReturnType() == Void.TYPE) {
+                if (matchLevel < 3) {
+                    matchLevel = 3;
+                    missException = new MissingFactoryMethodException("Static factory method does not return a value: " + method);
                 }
+                continue;
+            }
 
-                if (!allowPrivate && !Modifier.isPublic(method.getModifiers())) {
-                    if (matchLevel < 5) {
-                        matchLevel = 5;
-                        missException = new MissingFactoryMethodException("Static factory method is not public: " + method);
-                    }
-                    continue;
+            if (Modifier.isAbstract(method.getModifiers())) {
+                if (matchLevel < 4) {
+                    matchLevel = 4;
+                    missException = new MissingFactoryMethodException("Static factory method is abstract: " + method);
                 }
+                continue;
+            }
 
-                if (!Modifier.isStatic(method.getModifiers())) {
-                    if (matchLevel < 6) {
-                        matchLevel = 6;
-                        missException = new MissingFactoryMethodException("Static factory method is not static: " + method);
-                    }
-                    continue;
+            if (!allowPrivate && !Modifier.isPublic(method.getModifiers())) {
+                if (matchLevel < 5) {
+                    matchLevel = 5;
+                    missException = new MissingFactoryMethodException("Static factory method is not public: " + method);
                 }
+                continue;
+            }
 
-                if (allowPrivate && !Modifier.isPublic(method.getModifiers())) {
-                    setAccessible(method);
+            if (!Modifier.isStatic(method.getModifiers())) {
+                if (matchLevel < 6) {
+                    matchLevel = 6;
+                    missException = new MissingFactoryMethodException("Static factory method is not static: " + method);
                 }
+                continue;
+            }
 
-                return method;
+            if (allowPrivate && !Modifier.isPublic(method.getModifiers())) {
+                setAccessible(method);
             }
+
+            return new StaticFactory(method, parameterNames);
         }
 
         if (missException != null) {
@@ -410,12 +523,12 @@
         } else {
             StringBuffer buffer = new StringBuffer("Unable to find a valid factory method: ");
             buffer.append("public void ").append(typeClass.getName()).append(".");
-            buffer.append(factoryMethod).append(toParameterList(argTypes));
+            buffer.append(factoryMethod).append(toParameterList(parameterTypes));
             throw new MissingFactoryMethodException(buffer.toString());
         }
     }
 
-    public static Method findInstanceFactory(Class typeClass, String factoryMethod, EnumSet<Option> options) {
+    public static Method findInstanceFactory(Class typeClass, String factoryMethod, Set<Option> options) {
         if (typeClass == null) throw new NullPointerException("typeClass is null");
         if (factoryMethod == null) throw new NullPointerException("name is null");
         if (factoryMethod.length() == 0) throw new IllegalArgumentException("name is an empty string");
@@ -492,6 +605,124 @@
         }
     }
 
+    public static List<String> getParameterNames(Constructor<?> constructor) {
+        // use reflection to get Java6 ConstructorParameter annotation value
+        try {
+            Class<? extends Annotation> constructorPropertiesClass = ClassLoader.getSystemClassLoader().loadClass("java.beans.ConstructorProperties").asSubclass(Annotation.class);
+            Annotation constructorProperties = constructor.getAnnotation(constructorPropertiesClass);
+            if (constructorProperties != null) {
+                String[] parameterNames = (String[]) constructorPropertiesClass.getMethod("value").invoke(constructorProperties);
+                if (parameterNames != null) {
+                    return Arrays.asList(parameterNames);
+                }
+            }
+        } catch (Throwable e) {
+        }
+
+        ParameterNames parameterNames = constructor.getAnnotation(ParameterNames.class);
+        if (parameterNames != null && parameterNames.value() != null) {
+            return Arrays.asList(parameterNames.value());
+        }
+        if (parameterNamesLoader != null) {
+            return parameterNamesLoader.get(constructor);
+        }
+        return null;
+    }
+
+    public static List<String> getParameterNames(Method method) {
+        ParameterNames parameterNames = method.getAnnotation(ParameterNames.class);
+        if (parameterNames != null && parameterNames.value() != null) {
+            return Arrays.asList(parameterNames.value());
+        }
+        if (parameterNamesLoader != null) {
+            return parameterNamesLoader.get(method);
+        }
+        return null;
+    }
+
+    public static interface Factory {
+        List<String> getParameterNames();
+
+        List<Type> getParameterTypes();
+
+        Object create(Object... parameters) throws ConstructionException;
+    }
+
+    public static class ConstructorFactory implements Factory {
+        private Constructor constructor;
+        private List<String> parameterNames;
+
+        public ConstructorFactory(Constructor constructor, List<String> parameterNames) {
+            if (constructor == null) throw new NullPointerException("constructor is null");
+            if (parameterNames == null) throw new NullPointerException("parameterNames is null");
+            this.constructor = constructor;
+            this.parameterNames = parameterNames;
+        }
+
+        public List<String> getParameterNames() {
+            return parameterNames;
+        }
+
+        public List<Type> getParameterTypes() {
+            return new ArrayList<Type>(Arrays.asList(constructor.getGenericParameterTypes()));
+        }
+
+        public Object create(Object... parameters) throws ConstructionException {
+            // create the instance
+            try {
+                Object instance = constructor.newInstance(parameters);
+                return instance;
+            } catch (Exception e) {
+                Throwable t = e;
+                if (e instanceof InvocationTargetException) {
+                    InvocationTargetException invocationTargetException = (InvocationTargetException) e;
+                    if (invocationTargetException.getCause() != null) {
+                        t = invocationTargetException.getCause();
+                    }
+                }
+                throw new ConstructionException("Error invoking constructor: " + constructor, t);
+            }
+        }
+    }
+
+    public static class StaticFactory implements Factory {
+        private Method staticFactory;
+        private List<String> parameterNames;
+
+        public StaticFactory(Method staticFactory, List<String> parameterNames) {
+            this.staticFactory = staticFactory;
+            this.parameterNames = parameterNames;
+        }
+
+        public List<String> getParameterNames() {
+            if (parameterNames == null) {
+                throw new ConstructionException("InstanceFactory has not been initialized");
+            }
+
+            return parameterNames;
+        }
+
+        public List<Type> getParameterTypes() {
+            return new ArrayList<Type>(Arrays.asList(staticFactory.getGenericParameterTypes()));
+        }
+
+        public Object create(Object... parameters) throws ConstructionException {
+            try {
+                Object instance = staticFactory.invoke(null, parameters);
+                return instance;
+            } catch (Exception e) {
+                Throwable t = e;
+                if (e instanceof InvocationTargetException) {
+                    InvocationTargetException invocationTargetException = (InvocationTargetException) e;
+                    if (invocationTargetException.getCause() != null) {
+                        t = invocationTargetException.getCause();
+                    }
+                }
+                throw new ConstructionException("Error invoking factory method: " + staticFactory, t);
+            }
+        }
+    }
+
     private static void setAccessible(final AccessibleObject accessibleObject) {
         AccessController.doPrivileged(new PrivilegedAction<Object>() {
             public Object run() {
@@ -501,13 +732,21 @@
         });
     }
 
-    private static String toParameterList(Class[] parameterTypes) {
+    private static String toParameterList(Class<?>[] parameterTypes) {
+        return toParameterList(parameterTypes != null ? Arrays.asList(parameterTypes) : null);
+    }
+
+    private static String toParameterList(List<? extends Class<?>> parameterTypes) {
         StringBuffer buffer = new StringBuffer();
         buffer.append("(");
-        for (int i = 0; i < parameterTypes.length; i++) {
-            Class type = parameterTypes[i];
-            if (i > 0) buffer.append(", ");
-            buffer.append(type.getName());
+        if (parameterTypes != null) {
+            for (int i = 0; i < parameterTypes.size(); i++) {
+                Class type = parameterTypes.get(i);
+                if (i > 0) buffer.append(", ");
+                buffer.append(type.getName());
+            }
+        } else {
+            buffer.append("...");
         }
         buffer.append(")");
         return buffer.toString();

Modified: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ObjectRecipeTest.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ObjectRecipeTest.java?rev=632095&r1=632094&r2=632095&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ObjectRecipeTest.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ObjectRecipeTest.java Thu Feb 28 11:26:28 2008
@@ -15,15 +15,17 @@
  * limitations under the License.
  */
 package org.apache.xbean.recipe;
-/**
- * @version $Rev$ $Date$
- */
 
 import junit.framework.TestCase;
 import org.apache.xbean.propertyeditor.PropertyEditors;
+import static org.apache.xbean.recipe.Person.ConstructionCalled.CONSTRUCTOR;
+import static org.apache.xbean.recipe.Person.ConstructionCalled.CONSTRUCTOR_4_ARG;
+import static org.apache.xbean.recipe.Person.ConstructionCalled.NEW_INSTANCE;
+import static org.apache.xbean.recipe.Person.ConstructionCalled.NEW_INSTANCE_4_ARG;
+import static org.apache.xbean.recipe.Person.ConstructionCalled.PERSON_FACTORY;
+import static org.apache.xbean.recipe.Person.ConstructionCalled.PERSON_FACTORY_4_ARG;
 
 import java.net.URL;
-import java.net.MalformedURLException;
 
 public class ObjectRecipeTest extends TestCase {
 
@@ -34,64 +36,85 @@
     public void testSetters() throws Exception {
 
         ObjectRecipe objectRecipe = new ObjectRecipe(Person.class);
-        doTest(objectRecipe);
+        doTest(objectRecipe, CONSTRUCTOR);
     }
 
     public void testConstructor() throws Exception {
 
         ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, new String[]{"name", "age", "homePage", "car"}, new Class[]{String.class, Integer.TYPE, URL.class, Car.class});
-        doTest(objectRecipe);
+        doTest(objectRecipe, CONSTRUCTOR_4_ARG);
     }
 
     public void testConstructorWithImpliedTypes() throws Exception {
 
         ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, new String[]{"name", "age", "homePage", "car"}, null);
-        doTest(objectRecipe);
+        doTest(objectRecipe, CONSTRUCTOR_4_ARG);
+    }
+
+    public void testConstructorWithNamedParameters() throws Exception {
+
+        ObjectRecipe objectRecipe = new ObjectRecipe(Person.class);
+        objectRecipe.allow(Option.NAMED_PARAMETERS);
+        doTest(objectRecipe, CONSTRUCTOR_4_ARG);
     }
 
     public void testStaticFactoryMethodAndSetters() throws Exception {
 
         ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, "newInstance");
-        doTest(objectRecipe);
+        doTest(objectRecipe, NEW_INSTANCE);
     }
 
     public void testStaticFactoryMethodWithParams() throws Exception {
 
         ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, "newInstance", new String[]{"name", "age", "homePage", "car"}, new Class[]{String.class, Integer.TYPE, URL.class, Car.class});
-        doTest(objectRecipe);
+        doTest(objectRecipe, NEW_INSTANCE_4_ARG);
     }
 
     public void testStaticFactoryMethodWithImpliedTypes() throws Exception {
 
         ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, "newInstance", new String[]{"name", "age", "homePage", "car"});
-        doTest(objectRecipe);
+        doTest(objectRecipe, NEW_INSTANCE_4_ARG);
+    }
+
+    public void testStaticFactoryMethodWithNamedParameters() throws Exception {
+
+        ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, "newInstance");
+        objectRecipe.allow(Option.NAMED_PARAMETERS);
+        doTest(objectRecipe, NEW_INSTANCE_4_ARG);
     }
 
     public void testInstanceFactorySetters() throws Exception {
 
         ObjectRecipe objectRecipe = new ObjectRecipe(PersonFactory.class, "create");
-        doTest(objectRecipe);
+        doTest(objectRecipe, PERSON_FACTORY);
     }
 
     public void testInstanceFactoryConstructor() throws Exception {
 
         ObjectRecipe objectRecipe = new ObjectRecipe(PersonFactory.class, "create", new String[]{"name", "age", "homePage", "car"}, new Class[]{String.class, Integer.TYPE, URL.class, Car.class});
-        doTest(objectRecipe);
+        doTest(objectRecipe, PERSON_FACTORY_4_ARG);
     }
 
     public void testInstanceFactoryConstructorWithImpliedTypes() throws Exception {
 
         ObjectRecipe objectRecipe = new ObjectRecipe(PersonFactory.class, "create", new String[]{"name", "age", "homePage", "car"});
-        doTest(objectRecipe);
+        doTest(objectRecipe, PERSON_FACTORY_4_ARG);
     }
 
-    private void doTest(ObjectRecipe objectRecipe) throws Exception {
+    public void testInstanceFactoryConstructorWithNamedParameters() throws Exception {
+
+        ObjectRecipe objectRecipe = new ObjectRecipe(PersonFactory.class, "create");
+        objectRecipe.allow(Option.NAMED_PARAMETERS);
+        doTest(objectRecipe, PERSON_FACTORY_4_ARG);
+    }
+
+    private void doTest(ObjectRecipe objectRecipe, Person.ConstructionCalled expectedConstruction) throws Exception {
         Person expected = new Person("Joe", 21, new URL("http://www.acme.org"), new Car("Mini", "Cooper", 2008));
 
         objectRecipe.setProperty("name", "Joe");
         objectRecipe.setProperty("age", "21");
         objectRecipe.setProperty("homePage", "http://www.acme.org");
-        
+
         ObjectRecipe carRecipe = new ObjectRecipe(Car.class, new String[]{"make", "model", "year"});
         carRecipe.setProperty("make", "Mini");
         carRecipe.setProperty("model", "Cooper");
@@ -100,6 +123,8 @@
 
         Person actual = (Person) objectRecipe.create(Person.class.getClassLoader());
         assertEquals("person", expected, actual);
+
+        assertEquals(expectedConstruction, actual.getConstructionCalled());
     }
 
 }

Copied: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ParameterNameLoaderTest.java (from r630653, geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/parameter/ParameterNamesTest.java)
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ParameterNameLoaderTest.java?p2=geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ParameterNameLoaderTest.java&p1=geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/parameter/ParameterNamesTest.java&r1=630653&r2=632095&rev=632095&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/parameter/ParameterNamesTest.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ParameterNameLoaderTest.java Thu Feb 28 11:26:28 2008
@@ -15,92 +15,79 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package org.apache.xbean.parameter;
+package org.apache.xbean.recipe;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
 import junit.framework.TestCase;
-import org.apache.xbean.recipe.RecipeHelper;
 
 /**
  * @version $Rev$ $Date$
  */
-public class ParameterNamesTest extends TestCase {
-    private final ParameterNames parameterNames = new AsmParameterNames();
+public class ParameterNameLoaderTest extends TestCase {
     public void testConstructor() throws Exception {
         Constructor constructor = TestClass.class.getConstructor(int.class, Object.class, Long.class);
         assertParameterNames(Arrays.asList("one", "two", "three"), constructor);
     }
 
+    public void tesConstructorAnnotated() throws Exception {
+        Constructor constructor = AnnotatedClass.class.getConstructor(int.class, Object.class, Long.class);
+        assertParameterNames(Arrays.asList("one", "two", "three"), constructor);
+    }
+
     public void testMethod() throws Exception {
         Method method = TestClass.class.getMethod("instanceMethod", int.class, Object.class, Long.class);
         assertParameterNames(Arrays.asList("x", "y", "z"), method);
     }
 
+    public void testMethodAnnotated() throws Exception {
+        Method method = AnnotatedClass.class.getMethod("instanceMethod", int.class, Object.class, Long.class);
+        assertParameterNames(Arrays.asList("x", "y", "z"), method);
+    }
+
     public void testStaticMethod() throws Exception {
         Method method = TestClass.class.getMethod("factoryMethod", int.class, Object.class, Long.class);
         assertParameterNames(Arrays.asList("a", "b", "c"), method);
     }
 
+    public void testStaticMethodAnnotated() throws Exception {
+        Method method = AnnotatedClass.class.getMethod("factoryMethod", int.class, Object.class, Long.class);
+        assertParameterNames(Arrays.asList("a", "b", "c"), method);
+    }
+
     public void testInheritedMethod() throws Exception {
         Method method = TestClass.class.getMethod("inheritedMethod", Map.class);
         assertParameterNames(Arrays.asList("nothing"), method);
     }
 
-    public void testPrivateConstructor() throws Exception {
-        Constructor constructor = findPrivateConstructor(TestClass.class, new Class[]{Double.class});
-        assertNull(parameterNames.get(constructor));
-    }
-
-    public void testPrivateMethod() throws Exception {
-        Method method = findPrivateMethod(TestClass.class, "factoryMethod", new Class[] {Double.class});
-        assertNull(parameterNames.get(method));
+    public void testInheritedMethodAnnotated() throws Exception {
+        Method method = AnnotatedClass.class.getMethod("inheritedMethod", Map.class);
+        assertParameterNames(Arrays.asList("nothing"), method);
     }
 
-    public void testAllConstructors() throws Exception {
-        Map<Constructor,List<String>> expectedMap = new HashMap<Constructor,List<String>>();
-        expectedMap.put(TestClass.class.getConstructor(int.class, Object.class, Long.class),Arrays.asList("one", "two", "three"));
-        expectedMap.put(TestClass.class.getConstructor(int.class),Arrays.asList("foo"));
-        expectedMap.put(TestClass.class.getConstructor(Object.class),Arrays.asList("bar"));
-        expectedMap.put(TestClass.class.getConstructor(Object[].class),Arrays.asList("objectArray"));
-
-        Map<Constructor, List<String>> actualMap = parameterNames.getAllConstructorParameters(TestClass.class);
-        assertEquals(expectedMap, actualMap);
+    public void testPrivateConstructor() throws Exception {
+        Constructor constructor = findPrivateConstructor(TestClass.class, Double.class);
+        assertParameterNames(Arrays.asList("scotch"), constructor);
     }
 
-    public void testAllInstanceMethods() throws Exception {
-        Map<Method,List<String>> expectedMap = new HashMap<Method,List<String>>();
-        expectedMap.put(TestClass.class.getMethod("instanceMethod", int.class, Object.class, Long.class), Arrays.asList("x", "y", "z"));
-        expectedMap.put(TestClass.class.getMethod("instanceMethod", int.class), Arrays.asList("apple"));
-        expectedMap.put(TestClass.class.getMethod("instanceMethod", Object.class), Arrays.asList("ipod"));
-
-        Map<Method, List<String>> actualMap = parameterNames.getAllMethodParameters(TestClass.class, "instanceMethod");
-        assertEquals(expectedMap, actualMap);
+    public void testPrivateConstructorAnnotated() throws Exception {
+        Constructor constructor = findPrivateConstructor(AnnotatedClass.class, Double.class);
+        assertParameterNames(Arrays.asList("scotch"), constructor);
     }
 
-    public void testAllStataicMethods() throws Exception {
-        Map<Method,List<String>> expectedMap = new HashMap<Method,List<String>>();
-        expectedMap.put(TestClass.class.getMethod("factoryMethod", int.class, Object.class, Long.class), Arrays.asList("a", "b", "c"));
-        expectedMap.put(TestClass.class.getMethod("factoryMethod", int.class), Arrays.asList("beer"));
-        expectedMap.put(TestClass.class.getMethod("factoryMethod", Object.class), Arrays.asList("pizza"));
-
-        Map<Method, List<String>> actualMap = parameterNames.getAllMethodParameters(TestClass.class, "factoryMethod");
-        assertEquals(expectedMap, actualMap);
+    public void testPrivateMethod() throws Exception {
+        Method method = findPrivateMethod(TestClass.class, "factoryMethod", Arrays.asList(Double.class));
+        assertParameterNames(Arrays.asList("shot"), method);
     }
 
-    public void testAllMixedMethods() throws Exception {
-        Map<Method,List<String>> expectedMap = new HashMap<Method,List<String>>();
-        expectedMap.put(TestClass.class.getMethod("mixedMethods", Double.class), Arrays.asList("gin"));
-        expectedMap.put(TestClass.class.getMethod("mixedMethods", Short.class), Arrays.asList("tonic"));
-
-        Map<Method, List<String>> actualMap = parameterNames.getAllMethodParameters(TestClass.class, "mixedMethods");
-        assertEquals(expectedMap, actualMap);
+    public void testPrivateMethodAnnotated() throws Exception {
+        Method method = findPrivateMethod(AnnotatedClass.class, "factoryMethod", Arrays.asList(Double.class));
+        assertParameterNames(Arrays.asList("shot"), method);
     }
 
     @SuppressWarnings({"UnusedDeclaration"})
@@ -132,13 +119,59 @@
         public abstract void abstractMethod(Byte ear);
     }
 
+    @SuppressWarnings({"UnusedDeclaration"})
+    private static class ParentAnnotatedClass {
+        @ParameterNames({"nothing"})
+        public void inheritedMethod(Map arg1) {}
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    private static abstract class AnnotatedClass extends ParentAnnotatedClass {
+        @ParameterNames({"one", "two", "three"})
+        public AnnotatedClass(int arg1, Object arg2, Long arg3) {}
+        @ParameterNames({"foo"})
+        public AnnotatedClass(int arg1) {}
+        @ParameterNames({"bar"})
+        public AnnotatedClass(Object arg1) {}
+        @ParameterNames({"objectArray"})
+        public AnnotatedClass(Object[] arg1) {}
+        @ParameterNames({"scotch"})
+        private AnnotatedClass(Double arg1) {}
+
+        @ParameterNames({"a", "b", "c"})
+        public static void factoryMethod(int arg1, Object arg2, Long arg3) {}
+        @ParameterNames({"beer"})
+        public static void factoryMethod(int arg1) {}
+        @ParameterNames({"pizza"})
+        public static void factoryMethod(Object arg1) {}
+        @ParameterNames({"shot"})
+        private static void factoryMethod(Double arg1) {}
+
+        @ParameterNames({"x", "y", "z"})
+        public void instanceMethod(int arg1, Object arg2, Long arg3) {}
+        @ParameterNames({"apple"})
+        public void instanceMethod(int arg1) {}
+        @ParameterNames({"ipod"})
+        public void instanceMethod(Object arg1) {}
+        @ParameterNames({"psp"})
+        private void instanceMethod(Double arg1) {}
+
+        @ParameterNames({"gin"})
+        public static void mixedMethods(Double arg1) {}
+        @ParameterNames({"tonic"})
+        public void mixedMethods(Short arg1) {}
+
+        @ParameterNames({"ear"})
+        public abstract void abstractMethod(Byte arg1);
+    }
+
     private void assertParameterNames(List<String> expectedNames, Constructor constructor) {
-        List<String> actualNames = parameterNames.get(constructor);
+        List<String> actualNames = ReflectionUtil.getParameterNames(constructor);
         assertEquals(expectedNames, actualNames);
     }
 
     private void assertParameterNames(List<String> expectedNames, Method method) {
-        List<String> actualNames = parameterNames.get(method);
+        List<String> actualNames = ReflectionUtil.getParameterNames(method);
         assertEquals(expectedNames, actualNames);
     }
 
@@ -148,20 +181,10 @@
         assertEquals(Arrays.asList(expectedNames), Arrays.asList(actualNames));
     }
 
-    @SuppressWarnings({"SuspiciousMethodCalls"})
-    private void assertEquals(Map<?,List<String>> expectedMap, Map<?,List<String>> actualMap) {
-        for (Map.Entry<?, List<String>> entry : actualMap.entrySet()) {
-            Object key = entry.getKey();
-            List<String> expectedNames = expectedMap.get(key);
-            List<String> actualNames = entry.getValue();
-            assertEquals(expectedNames, actualNames);
-        }
-    }
-
-    private Constructor findPrivateConstructor(Class clazz, Class[] argTypes) {
+    private Constructor findPrivateConstructor(Class clazz, Class<?>... parameterTypes) {
         Constructor[] constructors = clazz.getDeclaredConstructors();
         for (Constructor constructor : constructors) {
-            if (RecipeHelper.isAssignableFrom(argTypes, constructor.getParameterTypes())) {
+            if (RecipeHelper.isAssignableFrom(Arrays.asList(parameterTypes), Arrays.<Class<?>>asList(constructor.getParameterTypes()))) {
                 if (!Modifier.isPublic(constructor.getModifiers())) {
                     return constructor;
                 }
@@ -171,10 +194,10 @@
         return null;
     }
 
-    private Method findPrivateMethod(Class clazz, String methodName, Class[] argTypes) {
+    private Method findPrivateMethod(Class clazz, String methodName, List<? extends Class<?>> parameterTypes) {
         Method[] methods = clazz.getDeclaredMethods();
         for (Method method : methods) {
-            if (method.getName().equals(methodName) && RecipeHelper.isAssignableFrom(argTypes, method.getParameterTypes())) {
+            if (method.getName().equals(methodName) && RecipeHelper.isAssignableFrom(parameterTypes, Arrays.asList(method.getParameterTypes()))) {
                 if (!Modifier.isPublic(method.getModifiers())) {
                     return method;
                 }

Modified: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/Person.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/Person.java?rev=632095&r1=632094&r2=632095&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/Person.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/Person.java Thu Feb 28 11:26:28 2008
@@ -21,10 +21,22 @@
 import java.util.Map;
 import java.util.Properties;
 
+import junit.framework.Assert;
+import static org.apache.xbean.recipe.Person.ConstructionCalled.*;
+
 /**
  * @version $Rev$ $Date$
  */
 public class Person {
+    public static enum ConstructionCalled {
+        CONSTRUCTOR,
+        CONSTRUCTOR_4_ARG,
+        NEW_INSTANCE,
+        NEW_INSTANCE_4_ARG,
+        PERSON_FACTORY,
+        PERSON_FACTORY_4_ARG,
+    }
+
     private String name;
     private int age;
     private URL homePage;
@@ -34,22 +46,52 @@
     private Map<String, Object> allMap;
     private Properties allProperties;
 
+    private ConstructionCalled constructionCalled;
+
     public Person() {
+        this(null, 0, null, null, CONSTRUCTOR);
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    public Person(String name, int age, URL homePage) {
+        Assert.fail("3 arg constructor should never be called");
     }
 
     public Person(String name, int age, URL homePage, Car car) {
+        this(name, age, homePage, car, CONSTRUCTOR_4_ARG);
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    public Person(String name, int age, URL homePage, Car car, String unused) {
+        Assert.fail("5 arg constructor should never be called");
+    }
+
+    Person(String name, int age, URL homePage, Car car, ConstructionCalled constructionCalled) {
         this.name = name;
         this.age = age;
         this.homePage = homePage;
         this.car = car;
+        this.constructionCalled = constructionCalled;
     }
 
     public static Person newInstance() {
-        return new Person();
+        return new Person(null, 0, null, null, NEW_INSTANCE);
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    public static Person newInstance(String name, int age, URL homePage) {
+        Assert.fail("3 arg static factory should never be called");
+        return null;
     }
 
     public static Person newInstance(String name, int age, URL homePage, Car car) {
-        return new Person(name, age, homePage, car);
+        return new Person(name, age, homePage, car, NEW_INSTANCE_4_ARG);
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    public static Person newInstance(String name, int age, URL homePage, Car car, String unused) {
+        Assert.fail("5 arg static factory should never be called");
+        return null;
     }
 
     public String getName() {
@@ -114,6 +156,10 @@
 
     public void setAllProperties(Properties allProperties) {
         this.allProperties = allProperties;
+    }
+
+    public ConstructionCalled getConstructionCalled() {
+        return constructionCalled;
     }
 
     public String toString() {

Modified: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/PersonFactory.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/PersonFactory.java?rev=632095&r1=632094&r2=632095&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/PersonFactory.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/PersonFactory.java Thu Feb 28 11:26:28 2008
@@ -19,13 +19,24 @@
 
 import java.net.URL;
 
+import static org.apache.xbean.recipe.Person.ConstructionCalled;
+import static org.apache.xbean.recipe.Person.ConstructionCalled.*;
+import junit.framework.Assert;
+
 public class PersonFactory {
     private String name;
     private int age;
     private URL homePage;
     private Car car;
+    private ConstructionCalled constructionCalled;
 
     public PersonFactory() {
+        constructionCalled = PERSON_FACTORY;
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    public PersonFactory(String name, int age, URL homePage) {
+        Assert.fail("3 arg instance factory constructor should never be called");
     }
 
     public PersonFactory(String name, int age, URL homePage, Car car) {
@@ -33,6 +44,12 @@
         this.age = age;
         this.homePage = homePage;
         this.car = car;
+        constructionCalled = PERSON_FACTORY_4_ARG;
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    public PersonFactory(String name, int age, URL homePage, Car car, String unused) {
+        Assert.fail("3 arg instance factory constructor should never be called");
     }
 
     public String getName() {
@@ -68,6 +85,6 @@
     }
 
     public Person create() {
-        return new Person(name, age, homePage, car);
+        return new Person(name, age, homePage, car, constructionCalled);
     }
 }



Mime
View raw message