You are viewing a plain text version of this content. The canonical link for it is here.
Posted to xbean-scm@geronimo.apache.org by da...@apache.org on 2008/02/28 20:26:31 UTC
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/
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);
}
}