geronimo-xbean-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From d...@apache.org
Subject svn commit: r617547 [3/3] - in /geronimo/xbean/trunk/xbean-reflect/src: main/java/org/apache/xbean/recipe/ test/java/org/apache/xbean/recipe/
Date Fri, 01 Feb 2008 16:23:29 GMT
Added: 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=617547&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java (added)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java Fri Feb  1 08:23:21 2008
@@ -0,0 +1,515 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.xbean.recipe;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.List;
+
+import static org.apache.xbean.recipe.RecipeHelper.isAssignableFrom;
+
+public final class ReflectionUtil {
+    private ReflectionUtil() {
+    }
+
+    public static Field findField(Class typeClass, String propertyName, Object propertyValue, EnumSet<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");
+        if (options == null) options = EnumSet.noneOf(Option.class);
+
+        int matchLevel = 0;
+        MissingAccessorException missException = null;
+
+        if (propertyName.contains("/")){
+            String[] strings = propertyName.split("/");
+            if (strings == null || strings.length != 2) throw new IllegalArgumentException("badly formed <class>/<attribute> property name: " + propertyName);
+
+            String className = strings[0];
+            propertyName = strings[1];
+
+            boolean found = false;
+            while(!typeClass.equals(Object.class) && !found){
+                if (typeClass.getName().equals(className)){
+                    found = true;
+                    break;
+                } else {
+                    typeClass = typeClass.getSuperclass();
+                }
+            }
+
+            if (!found) throw new MissingAccessorException("Type not assignable to class: " + className, -1);
+        }
+
+        List<Field> fields = new ArrayList<Field>(Arrays.asList(typeClass.getDeclaredFields()));
+        Class parent = typeClass.getSuperclass();
+        while (parent != null){
+            fields.addAll(Arrays.asList(parent.getDeclaredFields()));
+            parent = parent.getSuperclass();
+        }
+
+        boolean allowPrivate = options.contains(Option.PRIVATE_PROPERTIES);
+        boolean allowStatic = options.contains(Option.STATIC_PROPERTIES);
+        boolean caseInsesnitive = options.contains(Option.CASE_INSENSITIVE_PROPERTIES);
+
+        for (Field field : fields) {
+            if (field.getName().equals(propertyName) || (caseInsesnitive && field.getName().equalsIgnoreCase(propertyName))) {
+
+                if (!allowPrivate && !Modifier.isPublic(field.getModifiers())) {
+                    if (matchLevel < 4) {
+                        matchLevel = 4;
+                        missException = new MissingAccessorException("Field is not public: " + field, matchLevel);
+                    }
+                    continue;
+                }
+
+                if (!allowStatic && Modifier.isStatic(field.getModifiers())) {
+                    if (matchLevel < 4) {
+                        matchLevel = 4;
+                        missException = new MissingAccessorException("Field is static: " + field, matchLevel);
+                    }
+                    continue;
+                }
+
+                Class fieldType = field.getType();
+                if (fieldType.isPrimitive() && propertyValue == null) {
+                    if (matchLevel < 6) {
+                        matchLevel = 6;
+                        missException = new MissingAccessorException("Null can not be assigned to " +
+                                fieldType.getName() + ": " + field, matchLevel);
+                    }
+                    continue;
+                }
+
+
+                if (!RecipeHelper.isInstance(fieldType, propertyValue) && !RecipeHelper.isConvertable(fieldType, propertyValue)) {
+                    if (matchLevel < 5) {
+                        matchLevel = 5;
+                        missException = new MissingAccessorException((propertyValue == null ? "null" : propertyValue.getClass().getName()) + " can not be assigned or converted to " +
+                                fieldType.getName() + ": " + field, matchLevel);
+                    }
+                    continue;
+                }
+
+                if (allowPrivate && !Modifier.isPublic(field.getModifiers())) {
+                    setAccessible(field);
+                }
+
+                return field;
+            }
+
+        }
+
+        if (missException != null) {
+            throw missException;
+        } else {
+            StringBuffer buffer = new StringBuffer("Unable to find a valid field: ");
+            buffer.append("public ").append(" ").append(propertyValue == null ? "null" : propertyValue.getClass().getName());
+            buffer.append(" ").append(propertyName).append(";");
+            throw new MissingAccessorException(buffer.toString(), -1);
+        }
+    }
+
+    public static Method findSetter(Class typeClass, String propertyName, Object propertyValue, EnumSet<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");
+        if (options == null) options = EnumSet.noneOf(Option.class);
+
+        if (propertyName.contains("/")){
+            String[] strings = propertyName.split("/");
+            if (strings == null || strings.length != 2) throw new IllegalArgumentException("badly formed <class>/<attribute> property name: " + propertyName);
+
+            String className = strings[0];
+            propertyName = strings[1];
+
+            boolean found = false;
+            while(!typeClass.equals(Object.class) && !found){
+                if (typeClass.getName().equals(className)){
+                    found = true;
+                    break;
+                } else {
+                    typeClass = typeClass.getSuperclass();
+                }
+            }
+
+            if (!found) throw new MissingAccessorException("Type not assignable to class: " + className, -1);
+        }
+
+        String setterName = "set" + Character.toUpperCase(propertyName.charAt(0));
+        if (propertyName.length() > 0) {
+            setterName += propertyName.substring(1);
+        }
+
+
+        int matchLevel = 0;
+        MissingAccessorException missException = null;
+
+        boolean allowPrivate = options.contains(Option.PRIVATE_PROPERTIES);
+        boolean allowStatic = options.contains(Option.STATIC_PROPERTIES);
+        boolean caseInsesnitive = options.contains(Option.CASE_INSENSITIVE_PROPERTIES);
+
+        List<Method> methods = new ArrayList<Method>(Arrays.asList(typeClass.getMethods()));
+        methods.addAll(Arrays.asList(typeClass.getDeclaredMethods()));
+        for (Method method : methods) {
+            if (method.getName().equals(setterName) || (caseInsesnitive && method.getName().equalsIgnoreCase(setterName))) {
+                if (method.getParameterTypes().length == 0) {
+                    if (matchLevel < 1) {
+                        matchLevel = 1;
+                        missException = new MissingAccessorException("Setter takes no parameters: " + method, matchLevel);
+                    }
+                    continue;
+                }
+
+                if (method.getParameterTypes().length > 1) {
+                    if (matchLevel < 1) {
+                        matchLevel = 1;
+                        missException = new MissingAccessorException("Setter takes more then one parameter: " + method, matchLevel);
+                    }
+                    continue;
+                }
+
+                if (method.getReturnType() != Void.TYPE) {
+                    if (matchLevel < 2) {
+                        matchLevel = 2;
+                        missException = new MissingAccessorException("Setter returns a value: " + method, matchLevel);
+                    }
+                    continue;
+                }
+
+                if (Modifier.isAbstract(method.getModifiers())) {
+                    if (matchLevel < 3) {
+                        matchLevel = 3;
+                        missException = new MissingAccessorException("Setter is abstract: " + method, matchLevel);
+                    }
+                    continue;
+                }
+
+                if (!allowPrivate && !Modifier.isPublic(method.getModifiers())) {
+                    if (matchLevel < 4) {
+                        matchLevel = 4;
+                        missException = new MissingAccessorException("Setter is not public: " + method, matchLevel);
+                    }
+                    continue;
+                }
+
+                if (!allowStatic && Modifier.isStatic(method.getModifiers())) {
+                    if (matchLevel < 4) {
+                        matchLevel = 4;
+                        missException = new MissingAccessorException("Setter is static: " + method, matchLevel);
+                    }
+                    continue;
+                }
+
+                Class methodParameterType = method.getParameterTypes()[0];
+                if (methodParameterType.isPrimitive() && propertyValue == null) {
+                    if (matchLevel < 6) {
+                        matchLevel = 6;
+                        missException = new MissingAccessorException("Null can not be assigned to " +
+                                methodParameterType.getName() + ": " + method, matchLevel);
+                    }
+                    continue;
+                }
+
+
+                if (!RecipeHelper.isInstance(methodParameterType, propertyValue) && !RecipeHelper.isConvertable(methodParameterType, propertyValue)) {
+                    if (matchLevel < 5) {
+                        matchLevel = 5;
+                        missException = new MissingAccessorException((propertyValue == null ? "null" : propertyValue.getClass().getName()) + " can not be assigned or converted to " +
+                                methodParameterType.getName() + ": " + method, matchLevel);
+                    }
+                    continue;
+                }
+
+                if (allowPrivate && !Modifier.isPublic(method.getModifiers())) {
+                    setAccessible(method);
+                }
+
+                return method;
+            }
+
+        }
+
+        if (missException != null) {
+            throw missException;
+        } else {
+            StringBuffer buffer = new StringBuffer("Unable to find a valid setter method: ");
+            buffer.append("public void ").append(typeClass.getName()).append(".");
+            buffer.append(setterName).append("(").append(propertyValue == null ? "null" : propertyValue.getClass().getName()).append(")");
+            throw new MissingAccessorException(buffer.toString(), -1);
+        }
+    }
+
+    public static Constructor findConstructor(Class typeClass, Class[] argTypes, EnumSet<Option> options) {
+        if (typeClass == null) throw new NullPointerException("typeClass is null");
+        if (argTypes == null) throw new NullPointerException("argTypes is null");
+        if (options == null) options = EnumSet.noneOf(Option.class);
+
+        int matchLevel = 0;
+        MissingFactoryMethodException missException = null;
+
+        boolean allowPrivate = options.contains(Option.PRIVATE_CONSTRUCTOR);
+
+        List<Constructor> constructors = new ArrayList<Constructor>(Arrays.asList(typeClass.getConstructors()));
+        constructors.addAll(Arrays.asList(typeClass.getDeclaredConstructors()));
+        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);
+                }
+                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));
+                }
+                continue;
+            }
+
+            if (Modifier.isAbstract(constructor.getModifiers())) {
+                if (matchLevel < 4) {
+                    matchLevel = 4;
+                    missException = new MissingFactoryMethodException("Constructor is abstract: " + constructor);
+                }
+                continue;
+            }
+
+            if (!allowPrivate && !Modifier.isPublic(constructor.getModifiers())) {
+                if (matchLevel < 5) {
+                    matchLevel = 5;
+                    missException = new MissingFactoryMethodException("Constructor is not public: " + constructor);
+                }
+                continue;
+            }
+
+            if (allowPrivate && !Modifier.isPublic(constructor.getModifiers())) {
+                setAccessible(constructor);
+            }
+
+            return constructor;
+        }
+
+        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));
+            throw new ConstructionException(buffer.toString());
+        }
+    }
+
+    public static Method findStaticFactory(Class typeClass, String factoryMethod, Class[] argTypes, EnumSet<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 (options == null) options = EnumSet.noneOf(Option.class);
+
+        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) {
+                    if (matchLevel < 1) {
+                        matchLevel = 1;
+                        missException = new MissingFactoryMethodException("Static factory method has " + method.getParameterTypes().length + " arugments " +
+                                "but expected " + argTypes.length + " arguments: " + method);
+                    }
+                    continue;
+                }
+
+                if (!isAssignableFrom(argTypes, 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));
+                    }
+                    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 (Modifier.isAbstract(method.getModifiers())) {
+                    if (matchLevel < 4) {
+                        matchLevel = 4;
+                        missException = new MissingFactoryMethodException("Static factory method is abstract: " + 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.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())) {
+                    setAccessible(method);
+                }
+
+                return method;
+            }
+        }
+
+        if (missException != null) {
+            throw missException;
+        } 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));
+            throw new MissingFactoryMethodException(buffer.toString());
+        }
+    }
+
+    public static Method findInstanceFactory(Class typeClass, String factoryMethod, EnumSet<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 (options == null) options = EnumSet.noneOf(Option.class);
+        
+        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 (Modifier.isStatic(method.getModifiers())) {
+                    if (matchLevel < 1) {
+                        matchLevel = 1;
+                        missException = new MissingFactoryMethodException("Instance factory method is static: " + method);
+                    }
+                    continue;
+                }
+
+                if (method.getParameterTypes().length != 0) {
+                    if (matchLevel < 2) {
+                        matchLevel = 2;
+                        missException = new MissingFactoryMethodException("Instance factory method has signature " +
+                                "public " + typeClass.getName() + "." + factoryMethod + toParameterList(method.getParameterTypes()) +
+                                " but expected signature " +
+                                "public " + typeClass.getName() + "." + factoryMethod + "()");
+                    }
+                    continue;
+                }
+
+                if (method.getReturnType() == Void.TYPE) {
+                    if (matchLevel < 3) {
+                        matchLevel = 3;
+                        missException = new MissingFactoryMethodException("Instance factory method does not return a value: " + method);
+                    }
+                    continue;
+                }
+
+                if (Modifier.isAbstract(method.getModifiers())) {
+                    if (matchLevel < 4) {
+                        matchLevel = 4;
+                        missException = new MissingFactoryMethodException("Instance factory method is abstract: " + method);
+                    }
+                    continue;
+                }
+
+                if (!allowPrivate && !Modifier.isPublic(method.getModifiers())) {
+                    if (matchLevel < 5) {
+                        matchLevel = 5;
+                        missException = new MissingFactoryMethodException("Instance factory method is not public: " + method);
+                    }
+                    continue;
+                }
+
+                if (allowPrivate && !Modifier.isPublic(method.getModifiers())) {
+                    setAccessible(method);
+                }
+
+                return method;
+            }
+        }
+
+        if (missException != null) {
+            throw missException;
+        } else {
+            StringBuffer buffer = new StringBuffer("Unable to find a valid factory method: ");
+            buffer.append("public void ").append(typeClass.getName()).append(".");
+            buffer.append(factoryMethod).append("()");
+            throw new MissingFactoryMethodException(buffer.toString());
+        }
+    }
+
+    private static void setAccessible(final AccessibleObject accessibleObject) {
+        AccessController.doPrivileged(new PrivilegedAction<Object>() {
+            public Object run() {
+                accessibleObject.setAccessible(true);
+                return null;
+            }
+        });
+    }
+
+    private static String toParameterList(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());
+        }
+        buffer.append(")");
+        return buffer.toString();
+    }
+}

Added: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/Repository.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/Repository.java?rev=617547&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/Repository.java (added)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/Repository.java Fri Feb  1 08:23:21 2008
@@ -0,0 +1,45 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.xbean.recipe;
+
+public interface Repository {
+    /**
+     * Does this repository contain a object with the specified name.
+     *
+     * @param name the unique name of the object instance
+     * @return true if this repository contain a object with the specified name
+     */
+    boolean contains(String name);
+
+    /**
+     * Gets the object or recipe with the specified name from the repository.
+     *
+     * @param name the unique name of the object instance
+     * @return the object instance, a recipe to build the object or null
+     */
+    Object get(String name);
+
+    /**
+     * Add an object to the repository.
+     *
+     * @param name the unique name of the object instance
+     * @param object the object instance
+     * @throws ConstructionException if another object instance is already registered with the name
+     */
+    void add(String name, Object object);
+}

Added: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/UnresolvedReferencesException.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/UnresolvedReferencesException.java?rev=617547&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/UnresolvedReferencesException.java (added)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/UnresolvedReferencesException.java Fri Feb  1 08:23:21 2008
@@ -0,0 +1,49 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.xbean.recipe;
+
+import java.util.List;
+import java.util.Map;
+
+public class UnresolvedReferencesException extends ConstructionException {
+    private final Map<String,List<Reference>> unresolvedRefs;
+
+    public UnresolvedReferencesException(Map<String, List<Reference>> unresolvedRefs) {
+        super("Unresolved references to " + unresolvedRefs.keySet());
+        this.unresolvedRefs = unresolvedRefs;
+    }
+
+    public UnresolvedReferencesException(String message, Map<String, List<Reference>> unresolvedRefs) {
+        super(message);
+        this.unresolvedRefs = unresolvedRefs;
+    }
+
+    public UnresolvedReferencesException(String message, Throwable cause, Map<String, List<Reference>> unresolvedRefs) {
+        super(message, cause);
+        this.unresolvedRefs = unresolvedRefs;
+    }
+
+    public UnresolvedReferencesException(Throwable cause, Map<String, List<Reference>> unresolvedRefs) {
+        super("Unresolved references to " + unresolvedRefs.keySet(), cause);
+        this.unresolvedRefs = unresolvedRefs;
+    }
+
+    public Map<String, List<Reference>> getUnresolvedRefs() {
+        return unresolvedRefs;
+    }
+}

Modified: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/UnsetPropertiesRecipe.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/UnsetPropertiesRecipe.java?rev=617547&r1=617546&r2=617547&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/UnsetPropertiesRecipe.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/UnsetPropertiesRecipe.java Fri Feb  1 08:23:21 2008
@@ -20,16 +20,17 @@
 import java.util.Map;
 import java.util.Properties;
 
-public class UnsetPropertiesRecipe extends AbstractSecretRecipe {
+public class UnsetPropertiesRecipe extends AbstractRecipe {
     public float getPriority() {
         return 100;
     }
 
-    public boolean canCreate(Class type, ClassLoader classLoader) {
+    public boolean canCreate(Class type) {
         return type.isAssignableFrom(Properties.class);
     }
 
-    public Object create(Recipe outerRecipe, ClassLoader classLoader) throws ConstructionException {
+    protected Object internalCreate(Class expectedType, boolean lazyRefAllowed) throws ConstructionException {
+        Recipe outerRecipe = RecipeHelper.getCaller();
         if (!(outerRecipe instanceof ObjectRecipe)) {
             throw new ConstructionException("UnsetPropertiesRecipe can only be nested in an ObjectRecipe: outerRecipe=" + outerRecipe);
         }
@@ -41,6 +42,12 @@
         for (Map.Entry<String, Object> entry : unsetProperties.entrySet()) {
             properties.put(entry.getKey(), entry.getValue());
         }
+
+        // add to execution context if name is specified
+        if (getName() != null) {
+            ExecutionContext.getContext().addObject(getName(), properties);
+        }
+
         return properties;
     }
 }

Copied: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/AllPropertiesTest.java (from r614104, geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/UnsetPropertiesTest.java)
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/AllPropertiesTest.java?p2=geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/AllPropertiesTest.java&p1=geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/UnsetPropertiesTest.java&r1=614104&r2=617547&rev=617547&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/UnsetPropertiesTest.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/AllPropertiesTest.java Fri Feb  1 08:23:21 2008
@@ -17,33 +17,44 @@
  */
 package org.apache.xbean.recipe;
 
-import junit.framework.TestCase;
-
 import java.net.URL;
-import java.util.Collections;
+import java.util.Map;
 import java.util.Properties;
 
-public class UnsetPropertiesTest extends TestCase {
+import junit.framework.TestCase;
+
+public class AllPropertiesTest extends TestCase {
     public void testSetters() throws Exception {
         ObjectRecipe objectRecipe = new ObjectRecipe(Person.class);
         doTest(objectRecipe);
     }
 
     private void doTest(ObjectRecipe objectRecipe) throws Exception {
-        Person expected = new Person("Joe", 21, new URL("http://www.acme.org"));
-        expected.setUnsetMap(Collections.<String,Object>singletonMap("Fake Property", "Fake Value"));
+        Person expected = new Person("Joe", 21, new URL("http://www.acme.org"), null);
+
+        AllPropertiesRecipe allPropertiesRecipe = new AllPropertiesRecipe();
+
         Properties properties = new Properties();
+        properties.setProperty("name", "Joe");
+        properties.setProperty("age", "21");
+        properties.setProperty("homePage", "http://www.acme.org");
         properties.setProperty("Fake Property", "Fake Value");
-        expected.setUnsetProperties(properties);
+        properties.put("allMap", allPropertiesRecipe);
+        properties.put("allProperties", allPropertiesRecipe);
+        expected.setAllProperties(properties);
+        expected.setAllMap((Map)properties);
 
         objectRecipe.setProperty("name", "Joe");
         objectRecipe.setProperty("age", "21");
         objectRecipe.setProperty("homePage", "http://www.acme.org");
         objectRecipe.setProperty("Fake Property", "Fake Value");
-        objectRecipe.setProperty("unsetMap", new UnsetPropertiesRecipe());
-        objectRecipe.setProperty("unsetProperties", new UnsetPropertiesRecipe());
+        objectRecipe.setProperty("allMap", allPropertiesRecipe);
+        objectRecipe.setProperty("allProperties", allPropertiesRecipe);
+        objectRecipe.allow(Option.IGNORE_MISSING_PROPERTIES);
 
         Person actual = (Person) objectRecipe.create(Person.class.getClassLoader());
+        assertEquals(properties, actual.getAllProperties());
+        assertEquals(properties, actual.getAllMap());
         assertEquals("person", expected, actual);
     }
-}
+}
\ No newline at end of file

Modified: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/Car.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/Car.java?rev=617547&r1=617546&r2=617547&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/Car.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/Car.java Fri Feb  1 08:23:21 2008
@@ -20,23 +20,51 @@
  * @version $Rev$ $Date$
  */
 public class Car {
+    public String make;
+    public String model;
+    public int year;
 
-    private final Person driver;
-    private Person passenger;
+    public Car(String make, String model, int year) {
+        if (make == null) throw new NullPointerException("make is null");
+        if (model == null) throw new NullPointerException("model is null");
 
-    public Car(Person driver) {
-        this.driver = driver;
+        this.make = make;
+        this.model = model;
+        this.year = year;
     }
 
-    public Person getDriver() {
-        return driver;
+    public String getMake() {
+        return make;
     }
 
-    public Person getPassenger() {
-        return passenger;
+    public String getModel() {
+        return model;
     }
 
-    public void setPassenger(Person passenger) {
-        this.passenger = passenger;
+    public int getYear() {
+        return year;
+    }
+
+    public String toString() {
+        return "[Car: make=\"" + make + "\", model=\"" + model + "\", year=\"" + year + "\"]";
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Car car = (Car) o;
+
+        return year == car.year &&
+                make.equals(car.make) &&
+                model.equals(car.model);
+    }
+
+    public int hashCode() {
+        int result;
+        result = make.hashCode();
+        result = 31 * result + model.hashCode();
+        result = 31 * result + year;
+        return result;
     }
 }

Modified: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/CaseInsensitivePropertiesTest.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/CaseInsensitivePropertiesTest.java?rev=617547&r1=617546&r2=617547&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/CaseInsensitivePropertiesTest.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/CaseInsensitivePropertiesTest.java Fri Feb  1 08:23:21 2008
@@ -25,7 +25,7 @@
     public void testSetters() throws Exception {
         ObjectRecipe objectRecipe = new ObjectRecipe(Person.class);
         objectRecipe.allow(Option.CASE_INSENSITIVE_PROPERTIES);
-        Person expected = new Person("Joe", 21, new URL("http://www.acme.org"));
+        Person expected = new Person("Joe", 21, new URL("http://www.acme.org"), null);
 
         objectRecipe.setProperty("naMe", "Joe");
         objectRecipe.setProperty("aGe", "21");

Added: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/CollectionTest.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/CollectionTest.java?rev=617547&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/CollectionTest.java (added)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/CollectionTest.java Fri Feb  1 08:23:21 2008
@@ -0,0 +1,78 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.xbean.recipe;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+public class CollectionTest extends TestCase {
+    public Map notGeneric;
+    public Map<Key, Value> simple;
+    public Map<?,?> wildcard;
+    public Map<? extends Key, ? extends Value> extendsWildcard;
+    public Map<? super Key, ? extends Value> superWildcard;
+
+
+    public MyMap notGenericMyMap;
+    public MyMap<Fake, Value, Key> simpleMyMap;
+    public MyMap<?,?,?> wildcardMyMap;
+    public MyMap<? extends Fake, ? extends Value, ? extends Key> extendsWildcardMyMap;
+    public MyMap<? super Fake, ? super Value, ? extends Key> superWildcardMyMap;
+
+    public FixedMap fixedMap;
+
+    public void testReflection() {
+        for (Field field : getClass().getFields()) {
+            Type[] types = RecipeHelper.getTypeParameters(Map.class, field.getGenericType());
+            if (types == null) {
+                System.out.println("Map");
+            } else {
+                System.out.println("Map" + Arrays.toString(types));                
+            }
+        }
+    }
+
+
+    public static class MyMap<Unused, MapValue, MapKey> extends AbstractMap<MapKey, MapValue> {
+        public Iterator<MapKey> iterator() {
+            return null;
+        }
+
+        public int size() {
+            return 0;
+        }
+
+        public Set<Entry<MapKey, MapValue>> entrySet() {
+            return null;
+        }
+    }
+
+    public static class FixedMap extends MyMap<Fake,Value,Key> {
+    }
+
+    public static class Fake {}
+    public static class Key {}
+    public static class Value {}
+}

Added: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ObjectGraphTest.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ObjectGraphTest.java?rev=617547&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ObjectGraphTest.java (added)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ObjectGraphTest.java Fri Feb  1 08:23:21 2008
@@ -0,0 +1,426 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.xbean.recipe;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+public class ObjectGraphTest extends TestCase {
+    public void testCreateSingle() {
+        Repository repository = createNewRepository();
+        ObjectGraph graph = new ObjectGraph(repository);
+
+        Object actual = graph.create("Radiohead");
+        assertNotNull("actual is null", actual);
+        assertTrue("actual should be an instance of Artist", actual instanceof Artist);
+        Artist radiohead = (Artist)actual;
+        assertEquals(new Artist("Radiohead"), radiohead);
+
+        // multiple calls should result in same object
+        assertSame(radiohead, graph.create("Radiohead"));
+    }
+
+    public void testCreateNested() {
+        Album expectedBends = createBends();
+
+        Repository repository = createNewRepository();
+        ObjectGraph graph = new ObjectGraph(repository);
+
+        Object actual = graph.create("Bends");
+        assertNotNull("actual is null", actual);
+        assertTrue("actual should be an instance of Album", actual instanceof Album);
+        Album actualBends = (Album)actual;
+        assertEquals(expectedBends, actualBends);
+
+        // we should get the same values out of the graph
+        assertSame(actualBends, graph.create("Bends"));
+        assertSame(actualBends.getArtist(), graph.create("Radiohead"));
+        assertSame(actualBends.getSongs().get(0), graph.create("High and Dry"));
+        assertSame(actualBends.getSongs().get(1), graph.create("Fake Plastic Trees"));
+    }
+
+    public void testCreateAll() {
+        Album expectedBends = createBends();
+
+        Repository repository = createNewRepository();
+        ObjectGraph graph = new ObjectGraph(repository);
+
+        Map<String,Object> created = graph.createAll("Bends");
+        assertNotNull("created is null", created);
+        assertEquals(4, created.size());
+
+        Object actual = created.get("Bends");
+        assertNotNull("actual is null", actual);
+        assertTrue("actual should be an instance of Album", actual instanceof Album);
+        Album actualBends = (Album)actual;
+        assertEquals(expectedBends, actualBends);
+
+        // we should get the same values out of the graph
+        assertSame(actualBends, graph.create("Bends"));
+        assertSame(created.get("Radiohead"), graph.create("Radiohead"));
+        assertSame(created.get("High and Dry"), graph.create("High and Dry"));
+        assertSame(created.get("Fake Plastic Trees"), graph.create("Fake Plastic Trees"));
+
+        // verify nested objects in actualBends are the same objects in the created map
+        assertSame(actualBends.getArtist(), created.get("Radiohead"));
+        assertSame(actualBends.getSongs().get(0), created.get("High and Dry"));
+        assertSame(actualBends.getSongs().get(1), created.get("Fake Plastic Trees"));
+    }
+
+    public void testCreateAllGroupings() {
+        ObjectGraph graph = new ObjectGraph(createNewRepository());
+        Map<String,Object> created = graph.createAll("Radiohead");
+        assertEquals(Arrays.asList("Radiohead"), new ArrayList<String>(created.keySet()));
+
+        graph = new ObjectGraph(createNewRepository());
+        created = graph.createAll("Fake Plastic Trees");
+        assertEquals(Arrays.asList("Radiohead", "Fake Plastic Trees"), new ArrayList<String>(created.keySet()));
+
+        graph = new ObjectGraph(createNewRepository());
+        created = graph.createAll("Fake Plastic Trees", "Fake Plastic Trees", "Fake Plastic Trees");
+        assertEquals(Arrays.asList("Radiohead", "Fake Plastic Trees"), new ArrayList<String>(created.keySet()));
+
+        graph = new ObjectGraph(createNewRepository());
+        created = graph.createAll("Fake Plastic Trees", "Radiohead");
+        assertEquals(Arrays.asList("Radiohead", "Fake Plastic Trees"), new ArrayList<String>(created.keySet()));
+
+        graph = new ObjectGraph(createNewRepository());
+        created = graph.createAll("Bends");
+        assertEquals(Arrays.asList("Radiohead", "Bends", "High and Dry", "Fake Plastic Trees"), new ArrayList<String>(created.keySet()));
+
+        graph = new ObjectGraph(createNewRepository());
+        created = graph.createAll("Radiohead");
+        assertEquals(Arrays.asList("Radiohead"), new ArrayList<String>(created.keySet()));
+        created = graph.createAll("Bends");
+        assertEquals(Arrays.asList("Bends", "High and Dry", "Fake Plastic Trees"), new ArrayList<String>(created.keySet()));
+    }
+
+    public void testCreateUnknown() {
+        ObjectGraph graph = new ObjectGraph(createNewRepository());
+        try {
+            graph.create("Unknown");
+            fail("Expected NoSuchObjectException");
+        } catch (NoSuchObjectException expected) {
+            assertEquals(expected.getName(), "Unknown");
+        }
+
+        try {
+            graph.createAll("Unknown");
+            fail("Expected NoSuchObjectException");
+        } catch (NoSuchObjectException expected) {
+            assertEquals(expected.getName(), "Unknown");
+        }
+
+        try {
+            graph.createAll("Radiohead", "Unknown");
+            fail("Expected NoSuchObjectException");
+        } catch (NoSuchObjectException expected) {
+            assertEquals(expected.getName(), "Unknown");
+        }
+
+        try {
+            graph.createAll("Unknown", "Radiohead");
+            fail("Expected NoSuchObjectException");
+        } catch (NoSuchObjectException expected) {
+            assertEquals(expected.getName(), "Unknown");
+        }
+    }
+
+    public void testCircularDependency() {
+        Repository repository = createNewRepository();
+        ObjectRecipe recipe = (ObjectRecipe) repository.get("Radiohead");
+        recipe.setConstructorArgNames(new String[] {"name", "albums"});
+        recipe.setProperty("albums", new CollectionRecipe(Arrays.asList(new ReferenceRecipe("Bends"))));
+
+        ObjectGraph graph = new ObjectGraph(repository);
+        try {
+            graph.create("Bends");
+            fail("Expected CircularDependencyException");
+        } catch (CircularDependencyException expected) {
+            assertCircularity(Arrays.asList(repository.get("Bends"), repository.get("Radiohead"), repository.get("Bends")),
+                    expected.getCircularDependency());
+        }
+
+
+        repository = createNewRepository();
+        recipe = (ObjectRecipe) repository.get("Radiohead");
+        recipe.setConstructorArgNames(new String[] {"name", "albums"});
+        recipe.setProperty("albums", new CollectionRecipe(Arrays.asList(repository.get("Bends"))));
+
+        graph = new ObjectGraph(repository);
+        try {
+            graph.create("Bends");
+            fail("Expected CircularDependencyException");
+        } catch (CircularDependencyException expected) {
+            assertCircularity(Arrays.asList(repository.get("Bends"), repository.get("Radiohead"), repository.get("Bends")),
+                    expected.getCircularDependency());
+        }
+
+
+    }
+
+    private Repository createNewRepository() {
+        Repository repository = new DefaultRepository();
+
+        ObjectRecipe bends = new ObjectRecipe(Album.class, new String[]{"name", "artist"});
+        bends.setName("Bends");
+        bends.setProperty("name", "Bends");
+        bends.setProperty("artist", new ReferenceRecipe("radiohead"));
+        bends.setProperty("songs", new CollectionRecipe(Arrays.asList(
+                new ReferenceRecipe("High and Dry"),
+                new ReferenceRecipe("Fake Plastic Trees"))));
+        bends.setProperty("artist", new ReferenceRecipe("Radiohead"));
+        repository.add("Bends", bends);
+
+        ObjectRecipe radiohead = new ObjectRecipe(Artist.class, new String[]{"name"});
+        radiohead.setName("Radiohead");
+        radiohead.setProperty("name", "Radiohead");
+        repository.add("Radiohead", radiohead);
+
+        ObjectRecipe highAndDry = new ObjectRecipe(Song.class, new String[]{"name", "composer"});
+        highAndDry.setName("High and Dry");
+        highAndDry.setProperty("name", "High and Dry");
+        highAndDry.setProperty("composer", new ReferenceRecipe("Radiohead"));
+        repository.add("High and Dry", highAndDry);
+
+        ObjectRecipe fakePlasticTrees = new ObjectRecipe(Song.class, new String[]{"name", "composer"});
+        fakePlasticTrees.setName("Fake Plastic Trees");
+        fakePlasticTrees.setProperty("name", "Fake Plastic Trees");
+        fakePlasticTrees.setProperty("composer", new ReferenceRecipe("Radiohead"));
+        repository.add("Fake Plastic Trees", fakePlasticTrees);
+        return repository;
+    }
+
+    private Repository XcreateNewRepository() {
+        Repository repository = new DefaultRepository();
+
+        ObjectRecipe radiohead = new ObjectRecipe(Artist.class, new String[]{"name"});
+        radiohead.setName("Radiohead");
+        radiohead.setProperty("name", "Radiohead");
+        repository.add("Radiohead", radiohead);
+
+        ObjectRecipe highAndDry = new ObjectRecipe(Song.class, new String[]{"name", "composer"});
+        highAndDry.setName("High and Dry");
+        highAndDry.setProperty("name", "High and Dry");
+        highAndDry.setProperty("composer", radiohead);
+        repository.add("High and Dry", highAndDry);
+
+        ObjectRecipe fakePlasticTrees = new ObjectRecipe(Song.class, new String[]{"name", "composer"});
+        fakePlasticTrees.setName("Fake Plastic Trees");
+        fakePlasticTrees.setProperty("name", "Fake Plastic Trees");
+        fakePlasticTrees.setProperty("composer", radiohead);
+        repository.add("Fake Plastic Trees", fakePlasticTrees);
+
+        ObjectRecipe bends = new ObjectRecipe(Album.class, new String[]{"name", "artist"});
+        bends.setName("Bends");
+        bends.setProperty("name", "Bends");
+        bends.setProperty("artist", radiohead);
+        bends.setProperty("songs", new CollectionRecipe(Arrays.asList(highAndDry, fakePlasticTrees)));
+        bends.setProperty("artist", radiohead);
+        repository.add("Bends", bends);
+
+        return repository;
+    }
+
+    private Album createBends() {
+        Artist radiohead = new Artist("Radiohead");
+        Album bends = new Album("Bends", radiohead);
+        Song highAndDry = new Song("High and Dry", radiohead);
+        Song fakePlasticTrees = new Song("Fake Plastic Trees", radiohead);
+
+        bends.setSongs(Arrays.asList(highAndDry, fakePlasticTrees));
+
+        return bends;
+    }
+
+    public static class Album {
+        private final String name;
+        private final Artist artist;
+        private List<Song> songs;
+
+        public Album(String name, Artist artist) {
+            if (name == null) throw new NullPointerException("name is null");
+            if (artist == null) throw new NullPointerException("artist is null");
+            this.name = name;
+            this.artist = artist;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Artist getArtist() {
+            return artist;
+        }
+
+        public List<Song> getSongs() {
+            return songs;
+        }
+
+        public void setSongs(List<Song> songs) {
+            this.songs = songs;
+        }
+
+        public String toString() {
+            return "[Album: " + name + " - " + artist.getName() + "]";
+        }
+
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            Album album = (Album) o;
+
+            return name.equals(album.name) &&
+                    artist.equals(album.artist) &&
+                    !(songs != null ? !songs.equals(album.songs) : album.songs != null);
+
+        }
+
+        public int hashCode() {
+            int result;
+            result = name.hashCode();
+            result = 31 * result + artist.hashCode();
+            result = 31 * result + (songs != null ? songs.hashCode() : 0);
+            return result;
+        }
+    }
+
+    public static class Song {
+        private final String name;
+        private final Artist composer;
+
+        public Song(String name, Artist composer) {
+            if (name == null) throw new NullPointerException("name is null");
+            if (composer == null) throw new NullPointerException("composer is null");
+            this.name = name;
+            this.composer = composer;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Artist getComposer() {
+            return composer;
+        }
+
+        public String toString() {
+            return "[Song: " + name + " - " + composer.name + "]";
+        }
+
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            Song song = (Song) o;
+
+            return name.equals(song.name) && composer.equals(song.composer);
+        }
+
+        public int hashCode() {
+            int result;
+            result = name.hashCode();
+            result = 31 * result + composer.hashCode();
+            return result;
+        }
+    }
+
+    public static class Artist {
+        private final String name;
+        private final Set<Album> albums = new HashSet<Album>();
+
+        public Artist(String name) {
+            if (name == null) throw new NullPointerException("name is null");
+            this.name = name;
+        }
+
+        public Artist(String name, Set<Album> albums) {
+            if (name == null) throw new NullPointerException("name is null");
+            this.name = name;
+            this.albums.addAll(albums);
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Set<Album> getAlbums() {
+            return albums;
+        }
+
+        public void setAlbums(Set<Album> albums) {
+            this.albums.clear();
+            this.albums.addAll(albums);
+        }
+
+        public String toString() {
+            return "[Artist: " + name + "]";
+        }
+
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            Artist artist = (Artist) o;
+
+            return name.equals(artist.name);
+        }
+
+        public int hashCode() {
+            return name.hashCode();
+        }
+    }
+
+    public static void assertCircularity(List<?> expected, List<?> actual) {
+//        if (expected.size() != actual.size()) {
+//            // this will fail, with nice message
+//            assertEquals(expected, actual);
+//        }
+
+        List<Object> newActual = new ArrayList<Object>(actual.subList(0, actual.size() -1));
+
+        Object start = expected.get(0);
+        int index = actual.indexOf(start);
+        if (index > 0) Collections.rotate(newActual, index);
+        newActual.add(start);
+
+
+        assertEquals(expected, newActual);
+//
+//
+//        if (index < 1) {
+//            // acutal list already starts at the same object as the expected list
+//            assertEquals(expected, actual);
+//        } else {
+//            // create a new actual list rotated at the
+////            List<Object> newActual = new ArrayList<Object>(actual.size());
+////            newActual.addAll(actual.subList(index, actual.size()));
+////            newActual.addAll(actual.subList(1, index));
+////            newActual.add(start);
+////            assertEquals(expected, newActual);
+//        }
+    }
+}

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=617547&r1=617546&r2=617547&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 Fri Feb  1 08:23:21 2008
@@ -39,13 +39,13 @@
 
     public void testConstructor() throws Exception {
 
-        ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, new String[]{"name", "age", "homePage"}, new Class[]{String.class, Integer.TYPE, URL.class});
+        ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, new String[]{"name", "age", "homePage", "car"}, new Class[]{String.class, Integer.TYPE, URL.class, Car.class});
         doTest(objectRecipe);
     }
 
     public void testConstructorWithImpliedTypes() throws Exception {
 
-        ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, new String[]{"name", "age", "homePage"}, null);
+        ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, new String[]{"name", "age", "homePage", "car"}, null);
         doTest(objectRecipe);
     }
 
@@ -57,13 +57,13 @@
 
     public void testStaticFactoryMethodWithParams() throws Exception {
 
-        ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, "newInstance", new String[]{"name", "age", "homePage"}, new Class[]{String.class, Integer.TYPE, URL.class});
+        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);
     }
 
     public void testStaticFactoryMethodWithImpliedTypes() throws Exception {
 
-        ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, "newInstance", new String[]{"name", "age", "homePage"}, null);
+        ObjectRecipe objectRecipe = new ObjectRecipe(Person.class, "newInstance", new String[]{"name", "age", "homePage", "car"});
         doTest(objectRecipe);
     }
 
@@ -75,22 +75,28 @@
 
     public void testInstanceFactoryConstructor() throws Exception {
 
-        ObjectRecipe objectRecipe = new ObjectRecipe(PersonFactory.class, "create", new String[]{"name", "age", "homePage"}, new Class[]{String.class, Integer.TYPE, URL.class});
+        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);
     }
 
     public void testInstanceFactoryConstructorWithImpliedTypes() throws Exception {
 
-        ObjectRecipe objectRecipe = new ObjectRecipe(PersonFactory.class, "create", new String[]{"name", "age", "homePage"}, null);
+        ObjectRecipe objectRecipe = new ObjectRecipe(PersonFactory.class, "create", new String[]{"name", "age", "homePage", "car"});
         doTest(objectRecipe);
     }
 
     private void doTest(ObjectRecipe objectRecipe) throws Exception {
-        Person expected = new Person("Joe", 21, new URL("http://www.acme.org"));
+        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");
+        carRecipe.setProperty("year", "2008");
+        objectRecipe.setProperty("car", carRecipe);
 
         Person actual = (Person) objectRecipe.create(Person.class.getClassLoader());
         assertEquals("person", expected, actual);

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=617547&r1=617546&r2=617547&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 Fri Feb  1 08:23:21 2008
@@ -17,8 +17,8 @@
 package org.apache.xbean.recipe;
 
 import java.net.URL;
-import java.util.Map;
 import java.util.HashMap;
+import java.util.Map;
 import java.util.Properties;
 
 /**
@@ -28,24 +28,28 @@
     private String name;
     private int age;
     private URL homePage;
-    private Map<String,Object> unsetMap;
+    private Car car;
+    private Map<String, Object> unsetMap;
     private Properties unsetProperties;
+    private Map<String, Object> allMap;
+    private Properties allProperties;
 
     public Person() {
     }
 
-    public Person(String name, int age, URL homePage) {
+    public Person(String name, int age, URL homePage, Car car) {
         this.name = name;
         this.age = age;
         this.homePage = homePage;
+        this.car = car;
     }
 
-    public static Person newInstance(){
+    public static Person newInstance() {
         return new Person();
     }
 
-    public static Person newInstance(String name, int age, URL homePage){
-        return new Person(name, age, homePage);        
+    public static Person newInstance(String name, int age, URL homePage, Car car) {
+        return new Person(name, age, homePage, car);
     }
 
     public String getName() {
@@ -72,6 +76,14 @@
         this.homePage = homePage;
     }
 
+    public Car getCar() {
+        return car;
+    }
+
+    public void setCar(Car car) {
+        this.car = car;
+    }
+
     public Map<String, Object> getUnsetMap() {
         return unsetMap;
     }
@@ -88,8 +100,24 @@
         this.unsetProperties = unsetProperties;
     }
 
+    public Map<String, Object> getAllMap() {
+        return allMap;
+    }
+
+    public void setAllMap(Map<String, Object> allMap) {
+        this.allMap = allMap;
+    }
+
+    public Properties getAllProperties() {
+        return allProperties;
+    }
+
+    public void setAllProperties(Properties allProperties) {
+        this.allProperties = allProperties;
+    }
+
     public String toString() {
-        return super.toString() + " (name=\"" + name + "\", age=\"" + age + "\", homePage=\"" + homePage + "\")";
+        return "[Person: name=\"" + name + "\", age=\"" + age + "\", homePage=\"" + homePage +"\", car=\"" + car + "\"]";
     }
 
     public boolean equals(Object o) {
@@ -101,8 +129,11 @@
         if (age != person.age) return false;
         if (homePage != null ? !homePage.equals(person.homePage) : person.homePage != null) return false;
         if (name != null ? !name.equals(person.name) : person.name != null) return false;
+        if (car != null ? !car.equals(person.car) : person.car != null) return false;
         if (unsetMap != null ? !unsetMap.equals(person.unsetMap) : person.unsetMap != null) return false;
         if (unsetProperties != null ? !unsetProperties.equals(person.unsetProperties) : person.unsetProperties != null) return false;
+        if (allMap != null ? !allMap.equals(person.allMap) : person.allMap != null) return false;
+        if (allProperties != null ? !allProperties.equals(person.allProperties) : person.allProperties != null) return false;
 
         return true;
     }
@@ -112,8 +143,11 @@
         result = (name != null ? name.hashCode() : 0);
         result = 31 * result + age;
         result = 31 * result + (homePage != null ? homePage.hashCode() : 0);
+        result = 31 * result + (car != null ? car.hashCode() : 0);
         result = 31 * result + (unsetMap != null ? unsetMap.hashCode() : 0);
         result = 31 * result + (unsetProperties != null ? unsetProperties.hashCode() : 0);
+        result = 31 * result + (allMap != null ? allMap.hashCode() : 0);
+        result = 31 * result + (allProperties != null ? allProperties.hashCode() : 0);
         return result;
     }
 }

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=617547&r1=617546&r2=617547&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 Fri Feb  1 08:23:21 2008
@@ -23,14 +23,16 @@
     private String name;
     private int age;
     private URL homePage;
+    private Car car;
 
     public PersonFactory() {
     }
 
-    public PersonFactory(String name, int age, URL homePage) {
+    public PersonFactory(String name, int age, URL homePage, Car car) {
         this.name = name;
         this.age = age;
         this.homePage = homePage;
+        this.car = car;
     }
 
     public String getName() {
@@ -57,7 +59,15 @@
         this.homePage = homePage;
     }
 
+    public Car getCar() {
+        return car;
+    }
+
+    public void setCar(Car car) {
+        this.car = car;
+    }
+
     public Person create() {
-        return new Person(name, age, homePage);
+        return new Person(name, age, homePage, car);
     }
 }

Added: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ReferenceTest.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ReferenceTest.java?rev=617547&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ReferenceTest.java (added)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/ReferenceTest.java Fri Feb  1 08:23:21 2008
@@ -0,0 +1,232 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.xbean.recipe;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import junit.framework.TestCase;
+
+public class ReferenceTest extends TestCase {
+    public void testReference() {
+        Reference reference = new Reference();
+        TestAction action = new TestAction();
+        reference.setAction(action);
+
+        assertFalse(reference.isResolved());
+        assertNull(reference.get());
+        assertFalse(action.isSet);
+        assertNull(action.value);
+        assertNull(action.ref);
+
+        reference.set("apple");
+
+        assertTrue(reference.isResolved());
+        assertEquals("apple", reference.get());
+        assertTrue(action.isSet);
+        assertEquals("apple", action.value);
+        assertSame(reference, action.ref);
+
+        try {
+            reference.set("orange");
+            fail("Expected ConstructionException because reference has already been set");
+        } catch (ConstructionException expected) {
+        }
+    }
+
+    public void testNullReference() {
+        Reference reference = new Reference();
+        TestAction action = new TestAction();
+        reference.setAction(action);
+
+        assertFalse(reference.isResolved());
+        assertNull(reference.get());
+        assertFalse(action.isSet);
+        assertNull(action.value);
+        assertNull(action.ref);
+
+        reference.set(null);
+
+        assertTrue(reference.isResolved());
+        assertNull(reference.get());
+        assertTrue(action.isSet);
+        assertNull(action.value);
+        assertSame(reference, action.ref);
+
+        try {
+            reference.set(null);
+            fail("Expected ConstructionException because reference has already been set");
+        } catch (ConstructionException expected) {
+        }
+    }
+
+    public void testContext() {
+        DefaultExecutionContext context = new DefaultExecutionContext();
+        Reference reference = new Reference();
+        TestAction action = new TestAction();
+        reference.setAction(action);
+
+        context.addReference("fruit", reference);
+
+        assertFalse(reference.isResolved());
+        assertNull(reference.get());
+        assertFalse(action.isSet);
+        assertNull(action.value);
+        assertNull(action.ref);
+
+        context.addObject("fruit", "apple");
+
+        assertTrue(reference.isResolved());
+        assertEquals("apple", reference.get());
+        assertTrue(action.isSet);
+        assertEquals("apple", action.value);
+        assertSame(reference, action.ref);
+    }
+
+    public void testNullContext() {
+        DefaultExecutionContext context = new DefaultExecutionContext();
+        Reference reference = new Reference();
+        TestAction action = new TestAction();
+        reference.setAction(action);
+
+        context.addReference("fruit", reference);
+
+        assertFalse(reference.isResolved());
+        assertNull(reference.get());
+        assertFalse(action.isSet);
+        assertNull(action.value);
+        assertNull(action.ref);
+
+        context.addObject("fruit", null);
+
+        assertTrue(reference.isResolved());
+        assertNull(reference.get());
+        assertTrue(action.isSet);
+        assertNull(action.value);
+        assertSame(reference, action.ref);
+    }
+
+    public void testRecipe() {
+        ReferenceRecipe recipe = new ReferenceRecipe("fruit");
+        assertEquals("fruit", recipe.getReferenceName());
+
+        //
+        // Create methods will fail bcause no object has been registered with the
+        // name "fruit" and lazy references are not allowed by default
+        try {
+            recipe.create();
+            fail("Expected ConstructionException because referenced object does not exist and lazy references are not allowed");
+        } catch (ConstructionException expected) {
+        }
+
+        try {
+            recipe.create(getClass().getClassLoader());
+            fail("Expected ConstructionException because referenced object does not exist and lazy references are not allowed");
+        } catch (ConstructionException expected) {
+        }
+
+        try {
+            recipe.create(Object.class, true);
+            fail("Expected UnresolvedReferencesException");
+        } catch (UnresolvedReferencesException expected) {
+            assertEquals(Collections.singleton("fruit"), expected.getUnresolvedRefs().keySet());
+        }
+
+        //
+        // Test lazy references
+        DefaultExecutionContext context = new DefaultExecutionContext();
+        ExecutionContext.setContext(context);
+
+        try {
+            Object value = recipe.create(Object.class, true);
+            assertTrue("value should be an instance of Reference", value instanceof Reference);
+            Reference reference = (Reference) value;
+            TestAction action = new TestAction();
+            reference.setAction(action);
+
+            assertFalse(reference.isResolved());
+            assertNull(reference.get());
+            assertFalse(action.isSet);
+            assertNull(action.value);
+            assertNull(action.ref);
+
+            context.addObject("fruit", "apple");
+
+            assertTrue(reference.isResolved());
+            assertEquals("apple", reference.get());
+            assertTrue(action.isSet);
+            assertEquals("apple", action.value);
+            assertSame(reference, action.ref);
+
+            //
+            // Test normal create calls with preexisting object
+            assertEquals("apple", recipe.create());
+            assertEquals("apple", recipe.create(getClass().getClassLoader()));
+            assertEquals("apple", recipe.create(Object.class, false));
+            assertEquals("apple", recipe.create(Object.class, true));
+        } finally {
+            ExecutionContext.setContext(null);
+        }
+    }
+
+    public void testCircularReference() {
+        ReferenceRecipe apple = new ReferenceRecipe("tree");
+        apple.setName("apple");
+        ReferenceRecipe tree = new ReferenceRecipe("seed");
+        tree.setName("tree");
+        ReferenceRecipe seed = new ReferenceRecipe("apple");
+        seed.setName("seed");
+
+        DefaultExecutionContext context = new DefaultExecutionContext();
+        ExecutionContext.setContext(context);
+        try {
+            context.addObject("apple", apple);
+            context.addObject("tree", tree);
+            context.addObject("seed", seed);
+
+            try {
+                apple.create(Object.class, false);
+                fail("Expected CircularDependencyException");
+            } catch (CircularDependencyException expected) {
+                assertEquals(Arrays.<Recipe>asList(apple, tree, seed, apple),
+                        expected.getCircularDependency());
+            }
+            try {
+                tree.create(Object.class, false);
+                fail("Expected CircularDependencyException");
+            } catch (CircularDependencyException expected) {
+                assertEquals(Arrays.<Recipe>asList(tree, seed, apple, tree),
+                        expected.getCircularDependency());
+            }
+        } finally {
+            ExecutionContext.setContext(null);
+        }
+    }
+
+    public class TestAction implements Reference.Action {
+        public boolean isSet;
+        public Object value;
+        public Reference ref;
+
+        public void onSet(Reference ref) {
+            isSet = true;
+            value = ref.get();
+            this.ref = ref;
+        }
+    }
+}

Modified: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/UnsetPropertiesTest.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/UnsetPropertiesTest.java?rev=617547&r1=617546&r2=617547&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/UnsetPropertiesTest.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/UnsetPropertiesTest.java Fri Feb  1 08:23:21 2008
@@ -30,7 +30,7 @@
     }
 
     private void doTest(ObjectRecipe objectRecipe) throws Exception {
-        Person expected = new Person("Joe", 21, new URL("http://www.acme.org"));
+        Person expected = new Person("Joe", 21, new URL("http://www.acme.org"), null);
         expected.setUnsetMap(Collections.<String,Object>singletonMap("Fake Property", "Fake Value"));
         Properties properties = new Properties();
         properties.setProperty("Fake Property", "Fake Value");
@@ -44,6 +44,8 @@
         objectRecipe.setProperty("unsetProperties", new UnsetPropertiesRecipe());
 
         Person actual = (Person) objectRecipe.create(Person.class.getClassLoader());
+        assertEquals("person.getUnsetProperties()", properties, actual.getUnsetProperties());
+        assertEquals("person.getUnsetMap()", properties, actual.getUnsetMap());
         assertEquals("person", expected, actual);
     }
 }



Mime
View raw message