home | career | drupal | java | mac | mysql | perl | scala | uml | unix

Groovy example source code file (ObjectGraphBuilder.java)

This example Groovy source code file (ObjectGraphBuilder.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Groovy tags/keywords

class, classnameresolver, closure, illegalaccessexception, instantiationexception, map, map, object, object, objectgraphbuilder, objectgraphbuilder, reflection, regex, runtimeexception, string, string, util

The Groovy ObjectGraphBuilder.java source code

/*
 * Copyright 2003-2007 the original author or authors.
 *
 * Licensed 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 groovy.util;

import groovy.lang.Closure;
import groovy.lang.GString;
import groovy.lang.MetaProperty;
import groovy.lang.MissingPropertyException;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;

import org.codehaus.groovy.runtime.InvokerHelper;

/**
 * A builder for creating object graphs.<br>
 * Each node defines the class to be created and the property on its parent (if
 * any) at the same time.
 *
 * @author Scott Vlaminck (http://refactr.com)
 * @author Andres Almiray <aalmiray@users.sourceforge.com>
 */
public class ObjectGraphBuilder extends FactoryBuilderSupport {
    public static final String NODE_CLASS = "_NODE_CLASS_";
    public static final String NODE_NAME = "_NODE_NAME_";
    public static final String OBJECT_ID = "_OBJECT_ID_";
    public static final String LAZY_REF = "_LAZY_REF_";

    public static final String CLASSNAME_RESOLVER_KEY = "name";
    public static final String CLASSNAME_RESOLVER_REFLECTION = "reflection";
    public static final String CLASSNAME_RESOLVER_REFLECTION_ROOT = "root";

    // Regular expression pattern used to identify words ending in 'y' preceded by a consonant
    private static final Pattern PLURAL_IES_PATTERN = Pattern.compile(".*[^aeiouy]y", Pattern.CASE_INSENSITIVE);

    private ChildPropertySetter childPropertySetter;
    private ClassNameResolver classNameResolver;
    private IdentifierResolver identifierResolver;
    private NewInstanceResolver newInstanceResolver;
    private ObjectFactory objectFactory = new ObjectFactory();
    private ObjectBeanFactory objectBeanFactory = new ObjectBeanFactory();
    private ObjectRefFactory objectRefFactory = new ObjectRefFactory();
    private ReferenceResolver referenceResolver;
    private RelationNameResolver relationNameResolver;
    private Map<String, Class> resolvedClasses = new HashMap();
    private ClassLoader classLoader;
    private boolean lazyReferencesAllowed = true;
    private List<NodeReference> lazyReferences = new ArrayList();
    private String beanFactoryName = "bean";

    public ObjectGraphBuilder() {
        classNameResolver = new DefaultClassNameResolver();
        newInstanceResolver = new DefaultNewInstanceResolver();
        relationNameResolver = new DefaultRelationNameResolver();
        childPropertySetter = new DefaultChildPropertySetter();
        identifierResolver = new DefaultIdentifierResolver();
        referenceResolver = new DefaultReferenceResolver();

        addPostNodeCompletionDelegate(new Closure(this, this) {
            public void doCall(ObjectGraphBuilder builder, Object parent, Object node) {
                if (parent == null) {
                    builder.resolveLazyReferences();
                    builder.dispose();
                }
            }
        });
    }

    /**
     * Returns the current name of the 'bean' node.
     */
    public String getBeanFactoryName() {
        return beanFactoryName; 
    }

    /**
     * Returns the current ChildPropertySetter.
     */
    public ChildPropertySetter getChildPropertySetter() {
        return childPropertySetter;
    }

    /**
     * Returns the classLoader used to load a node's class.
     */
    public ClassLoader getClassLoader() {
        return classLoader;
    }

    /**
     * Returns the current ClassNameResolver.
     */
    public ClassNameResolver getClassNameResolver() {
        return classNameResolver;
    }

    /**
     * Returns the current NewInstanceResolver.
     */
    public NewInstanceResolver getNewInstanceResolver() {
        return newInstanceResolver;
    }

    /**
     * Returns the current RelationNameResolver.
     */
    public RelationNameResolver getRelationNameResolver() {
        return relationNameResolver;
    }

    /**
     * Returns true if references can be resolved lazily
     */
    public boolean isLazyReferencesAllowed() {
        return lazyReferencesAllowed;
    }

    /**
     * Sets the name for the 'bean' node.
     */
    public void setBeanFactoryName(String beanFactoryName) {
        this.beanFactoryName = beanFactoryName;
    }

    /**
     * Sets the current ChildPropertySetter.<br>
     * It will assign DefaultChildPropertySetter if null.<br>
     * It accepts a ChildPropertySetter instance or a Closure.
     */
    public void setChildPropertySetter(final Object childPropertySetter) {
        if (childPropertySetter instanceof ChildPropertySetter) {
            this.childPropertySetter = (ChildPropertySetter) childPropertySetter;
        } else if (childPropertySetter instanceof Closure) {
            final ObjectGraphBuilder self = this;
            this.childPropertySetter = new ChildPropertySetter() {
                public void setChild(Object parent, Object child, String parentName,
                                     String propertyName) {
                    Closure cls = (Closure) childPropertySetter;
                    cls.setDelegate(self);
                    cls.call(new Object[]{parent, child, parentName, propertyName});
                }
            };
        } else {
            this.childPropertySetter = new DefaultChildPropertySetter();
        }
    }

    /**
     * Sets the classLoader used to load a node's class.
     */
    public void setClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    /**
     * Sets the current ClassNameResolver.<br>
     * It will assign DefaultClassNameResolver if null.<br>
     * It accepts a ClassNameResolver instance, a String, a Closure or a Map.
     */
    public void setClassNameResolver(final Object classNameResolver) {
        if (classNameResolver instanceof ClassNameResolver) {
            this.classNameResolver = (ClassNameResolver) classNameResolver;
        } else if (classNameResolver instanceof String) {
            this.classNameResolver = new ClassNameResolver() {
                public String resolveClassname(String classname) {
                    return makeClassName((String) classNameResolver, classname);
                }
            };
        } else if (classNameResolver instanceof Closure) {
            final ObjectGraphBuilder self = this;
            this.classNameResolver = new ClassNameResolver() {
                public String resolveClassname(String classname) {
                    Closure cls = (Closure) classNameResolver;
                    cls.setDelegate(self);
                    return (String) cls.call(new Object[]{classname});
                }
            };
        } else if (classNameResolver instanceof Map) {
            Map classNameResolverOptions = (Map) classNameResolver;

            String resolverName = (String) classNameResolverOptions.get(CLASSNAME_RESOLVER_KEY);

            if (resolverName == null) {
                throw new RuntimeException("key '" + CLASSNAME_RESOLVER_KEY + "' not defined");
            }

            if (CLASSNAME_RESOLVER_REFLECTION.equals(resolverName)) {
                String root = (String) classNameResolverOptions.get(CLASSNAME_RESOLVER_REFLECTION_ROOT);

                if (root == null) {
                    throw new RuntimeException("key '" + CLASSNAME_RESOLVER_REFLECTION_ROOT + "' not defined");
                }

                this.classNameResolver = new ReflectionClassNameResolver(root);
            } else {
                throw new RuntimeException("unknown class name resolver " + resolverName);
            }
        } else {
            this.classNameResolver = new DefaultClassNameResolver();
        }
    }

    /**
     * Sets the current IdentifierResolver.<br>
     * It will assign DefaultIdentifierResolver if null.<br>
     * It accepts a IdentifierResolver instance, a String or a Closure.
     */
    public void setIdentifierResolver(final Object identifierResolver) {
        if (identifierResolver instanceof IdentifierResolver) {
            this.identifierResolver = (IdentifierResolver) identifierResolver;
        } else if (identifierResolver instanceof String) {
            this.identifierResolver = new IdentifierResolver() {
                public String getIdentifierFor(String nodeName) {
                    return (String) identifierResolver;
                }
            };
        } else if (identifierResolver instanceof Closure) {
            final ObjectGraphBuilder self = this;
            this.identifierResolver = new IdentifierResolver() {
                public String getIdentifierFor(String nodeName) {
                    Closure cls = (Closure) identifierResolver;
                    cls.setDelegate(self);
                    return (String) cls.call(new Object[]{nodeName});
                }
            };
        } else {
            this.identifierResolver = new DefaultIdentifierResolver();
        }
    }

    /**
     * Sets whether references can be resolved lazily or not.
     */
    public void setLazyReferencesAllowed(boolean lazyReferencesAllowed) {
        this.lazyReferencesAllowed = lazyReferencesAllowed;
    }

    /**
     * Sets the current NewInstanceResolver.<br>
     * It will assign DefaultNewInstanceResolver if null.<br>
     * It accepts a NewInstanceResolver instance or a Closure.
     */
    public void setNewInstanceResolver(final Object newInstanceResolver) {
        if (newInstanceResolver instanceof NewInstanceResolver) {
            this.newInstanceResolver = (NewInstanceResolver) newInstanceResolver;
        } else if (newInstanceResolver instanceof Closure) {
            final ObjectGraphBuilder self = this;
            this.newInstanceResolver = new NewInstanceResolver() {
                public Object newInstance(Class klass, Map attributes)
                        throws InstantiationException, IllegalAccessException {
                    Closure cls = (Closure) newInstanceResolver;
                    cls.setDelegate(self);
                    return cls.call(new Object[]{klass, attributes});
                }
            };
        } else {
            this.newInstanceResolver = new DefaultNewInstanceResolver();
        }
    }

    /**
     * Sets the current ReferenceResolver.<br>
     * It will assign DefaultReferenceResolver if null.<br>
     * It accepts a ReferenceResolver instance, a String or a Closure.
     */
    public void setReferenceResolver(final Object referenceResolver) {
        if (referenceResolver instanceof ReferenceResolver) {
            this.referenceResolver = (ReferenceResolver) referenceResolver;
        } else if (referenceResolver instanceof String) {
            this.referenceResolver = new ReferenceResolver() {
                public String getReferenceFor(String nodeName) {
                    return (String) referenceResolver;
                }
            };
        } else if (referenceResolver instanceof Closure) {
            final ObjectGraphBuilder self = this;
            this.referenceResolver = new ReferenceResolver() {
                public String getReferenceFor(String nodeName) {
                    Closure cls = (Closure) referenceResolver;
                    cls.setDelegate(self);
                    return (String) cls.call(new Object[]{nodeName});
                }
            };
        } else {
            this.referenceResolver = new DefaultReferenceResolver();
        }
    }

    /**
     * Sets the current RelationNameResolver.<br>
     * It will assign DefaultRelationNameResolver if null.
     */
    public void setRelationNameResolver(RelationNameResolver relationNameResolver) {
        this.relationNameResolver = relationNameResolver != null ? relationNameResolver
                : new DefaultRelationNameResolver();
    }

    protected void postInstantiate(Object name, Map attributes, Object node) {
        super.postInstantiate(name, attributes, node);
        Map context = getContext();
        String objectId = (String) context.get(OBJECT_ID);
        if (objectId != null && node != null) {
            setVariable(objectId, node);
        }
    }

    protected void preInstantiate(Object name, Map attributes, Object value) {
        super.preInstantiate(name, attributes, value);
        Map context = getContext();
        context.put(OBJECT_ID,
                attributes.remove(identifierResolver.getIdentifierFor((String) name)));
    }

    protected Factory resolveFactory(Object name, Map attributes, Object value) {
        // let custom factories be resolved first
        Factory factory = super.resolveFactory(name, attributes, value);
        if (factory != null) {
            return factory;
        }
        if (attributes.get(referenceResolver.getReferenceFor((String) name)) != null) {
            return objectRefFactory;
        }
        if (beanFactoryName != null && beanFactoryName.equals((String) name)) {
            return objectBeanFactory;
        }
        return objectFactory;
    }

    /**
     * Strategy for setting a child node on its parent.<br>
     * Useful for handling Lists/Arrays vs normal properties.
     */
    public interface ChildPropertySetter {
        /**
         * @param parent       the parent's node value
         * @param child        the child's node value
         * @param parentName   the name of the parent node
         * @param propertyName the resolved relation name of the child
         */
        void setChild(Object parent, Object child, String parentName, String propertyName);
    }

    /**
     * Strategy for resolving a classname.
     */
    public interface ClassNameResolver {
        /**
         * @param classname the node name as written on the building code
         */
        String resolveClassname(String classname);
    }

    /**
     * Default impl that calls parent.propertyName = child<br>
     * If parent.propertyName is a Collection it will try to add child to the
     * collection.
     */
    public static class DefaultChildPropertySetter implements ChildPropertySetter {
        public void setChild(Object parent, Object child, String parentName, String propertyName) {
            try {
                Object property = InvokerHelper.getProperty(parent, propertyName);
                if (property != null && Collection.class.isAssignableFrom(property.getClass())) {
                    ((Collection) property).add(child);
                } else {
                    InvokerHelper.setProperty(parent, propertyName, child);
                }
            } catch (MissingPropertyException mpe) {
                // ignore
            }
        }
    }

    /**
     * Default impl that capitalizes the classname.
     */
    public static class DefaultClassNameResolver implements ClassNameResolver {
        public String resolveClassname(String classname) {
            if (classname.length() == 1) {
                return classname.toUpperCase();
            }
            return classname.substring(0, 1)
                    .toUpperCase() + classname.substring(1);
        }
    }

    /**
     * Build objects using reflection to resolve class names.
     */
    public class ReflectionClassNameResolver implements ClassNameResolver {
        private final String root;

        /**
         * @param root package where the graph root class is located
         */
        public ReflectionClassNameResolver(String root) {
            this.root = root;
        }

        public String resolveClassname(String classname) {
            Object currentNode = getContext().get(CURRENT_NODE);

            if (currentNode == null) {
                return makeClassName(root, classname);
            } else {
                try {
                    Class klass = currentNode.getClass().getDeclaredField(classname).getType();

                    if (Collection.class.isAssignableFrom(klass)) {
                        Type type = currentNode.getClass().getDeclaredField(classname).getGenericType();
                        if (type instanceof ParameterizedType) {
                            ParameterizedType ptype = (ParameterizedType) type;
                            Type[] actualTypeArguments = ptype.getActualTypeArguments();
                            if (actualTypeArguments.length != 1) {
                                throw new RuntimeException("can't determine class name for collection field " + classname + " with multiple generics");
                            }

                            Type typeArgument = actualTypeArguments[0];
                            if (typeArgument instanceof Class) {
                                klass = (Class) actualTypeArguments[0];
                            } else {
                                throw new RuntimeException("can't instantiate collection field " + classname + " elements as they aren't a class");
                            }
                        } else {
                            throw new RuntimeException("collection field " + classname + " must be genericised");
                        }
                    }

                    return klass.getName();
                } catch (NoSuchFieldException e) {
                    throw new RuntimeException("can't find field " + classname + " for node class " + currentNode.getClass().getName(), e);
                }
            }
        }
    }

    /**
     * Default impl, always returns 'id'
     */
    public static class DefaultIdentifierResolver implements IdentifierResolver {
        public String getIdentifierFor(String nodeName) {
            return "id";
        }
    }

    /**
     * Default impl that calls Class.newInstance()
     */
    public static class DefaultNewInstanceResolver implements NewInstanceResolver {
        public Object newInstance(Class klass, Map attributes) throws InstantiationException,
                IllegalAccessException {
            return klass.newInstance();
        }
    }

    /**
     * Default impl, always returns 'refId'
     */
    public static class DefaultReferenceResolver implements ReferenceResolver {
        public String getReferenceFor(String nodeName) {
            return "refId";
        }
    }

    /**
     * Default impl that returns parentName & childName accordingly.
     */
    public static class DefaultRelationNameResolver implements RelationNameResolver {
        /**
         * Handles the common English regular plurals with the following rules.<br />
         * <ul>
         * <li>If childName ends in {consonant}y, replace 'y' with "ies". For example, allergy to allergies.
         * <li>Otherwise, append 's'. For example, monkey to monkeys; employee to employees.
         * </ul>
         * If the property does not exist then it will return childName unchanged.
         *
         * @see <a href="http://en.wikipedia.org/wiki/English_plural">English_plural
         */
        public String resolveChildRelationName(String parentName, Object parent, String childName,
                                               Object child) {
            boolean matchesIESRule = PLURAL_IES_PATTERN.matcher(childName).matches();
            String childNamePlural = matchesIESRule ? childName.substring(0, childName.length() - 1) + "ies" : childName + "s";

            MetaProperty metaProperty = InvokerHelper.getMetaClass(parent)
                    .hasProperty(parent, childNamePlural);

            return metaProperty != null ? childNamePlural : childName;
        }

        /**
         * Follow the most conventional pattern, returns the parentName
         * unchanged.
         */
        public String resolveParentRelationName(String parentName, Object parent,
                                                String childName, Object child) {
            return parentName;
        }
    }

    /**
     * Strategy for picking the correct synthetic identifier.
     */
    public interface IdentifierResolver {
        /**
         * Returns the name of the property that will identify the node.<br>
         *
         * @param nodeName the name of the node
         */
        String getIdentifierFor(String nodeName);
    }

    /**
     * Strategy for creating new instances of a class.<br>
     * Useful for plug-in calls to non-default constructors.
     */
    public interface NewInstanceResolver {
        /**
         * Create a new instance of Class klass.
         *
         * @param klass      the resolved class name
         * @param attributes the attribute Map available for the node
         */
        Object newInstance(Class klass, Map attributes) throws InstantiationException,
                IllegalAccessException;
    }

    /**
     * Strategy for picking the correct synthetic reference identifier.
     */
    public interface ReferenceResolver {
        /**
         * Returns the name of the property that references another node.<br>
         *
         * @param nodeName the name of the node
         */
        String getReferenceFor(String nodeName);
    }

    /**
     * Strategy for resolving a relationship property name.
     */
    public interface RelationNameResolver {
        /**
         * Returns the mapping name of child -> parent
         *
         * @param parentName the name of the parent node
         * @param parent     the parent node
         * @param childName  the name of the child node
         * @param child      the child node
         */
        String resolveChildRelationName(String parentName, Object parent, String childName,
                                        Object child);

        /**
         * Returns the mapping name of parent -> child
         *
         * @param parentName the name of the parent node
         * @param parent     the parent node
         * @param childName  the name of the child node
         * @param child      the child node
         */
        String resolveParentRelationName(String parentName, Object parent, String childName,
                                         Object child);
    }

    private void resolveLazyReferences() {
        if (!lazyReferencesAllowed) return;
        for (NodeReference ref : lazyReferences) {
            if (ref.parent == null) continue;

            Object child = null;
            try {
                child = getProperty(ref.refId);
            } catch (MissingPropertyException mpe) {
                // ignore
            }
            if (child == null) {
                throw new IllegalArgumentException("There is no valid node for reference "
                        + ref.parentName + "." + ref.childName + "=" + ref.refId);
            }

            // set child first
            childPropertySetter.setChild(ref.parent, child, ref.parentName,
                    relationNameResolver.resolveChildRelationName(ref.parentName,
                            ref.parent, ref.childName, child));

            // set parent afterwards
            String propertyName = relationNameResolver.resolveParentRelationName(ref.parentName,
                    ref.parent, ref.childName, child);
            MetaProperty metaProperty = InvokerHelper.getMetaClass(child)
                    .hasProperty(child, propertyName);
            if (metaProperty != null) {
                metaProperty.setProperty(child, ref.parent);
            }
        }
    }

    private static String makeClassName(String root, String name) {
        return root + "." + name.substring(0, 1).toUpperCase() + name.substring(1);
    }

    private static class ObjectFactory extends AbstractFactory {
        public Object newInstance(FactoryBuilderSupport builder, Object name, Object value,
                                  Map properties) throws InstantiationException, IllegalAccessException {
            ObjectGraphBuilder ogbuilder = (ObjectGraphBuilder) builder;
            String classname = ogbuilder.classNameResolver.resolveClassname((String) name);
            Class klass = resolveClass(builder, classname, name, value, properties);
            Map context = builder.getContext();
            context.put(ObjectGraphBuilder.NODE_NAME, name);
            context.put(ObjectGraphBuilder.NODE_CLASS, klass);
            return resolveInstance(builder, name, value, klass, properties);
        }

        protected Class resolveClass(FactoryBuilderSupport builder, String classname, Object name, Object value,
                                  Map properties) throws InstantiationException, IllegalAccessException {
            ObjectGraphBuilder ogbuilder = (ObjectGraphBuilder) builder;
            Class klass = ogbuilder.resolvedClasses.get(classname);
            if (klass == null) {
                klass = loadClass(ogbuilder.classLoader, classname);
                if (klass == null) {
                    klass = loadClass(ogbuilder.getClass().getClassLoader(), classname);
                }
                if (klass == null) {
                    try {
                        klass = Class.forName(classname);
                    } catch (ClassNotFoundException e) {
                        // ignore
                    }
                }
                if (klass == null) {
                    klass = loadClass(Thread.currentThread().getContextClassLoader(), classname);
                }
                if (klass == null) {
                    throw new RuntimeException(new ClassNotFoundException(classname));
                }
                ogbuilder.resolvedClasses.put(classname, klass);
            }

            return klass;
        }

        protected Object resolveInstance(FactoryBuilderSupport builder, Object name, Object value, Class klass,
                                  Map properties) throws InstantiationException, IllegalAccessException {
            ObjectGraphBuilder ogbuilder = (ObjectGraphBuilder) builder;
            if (value != null && klass.isAssignableFrom(value.getClass())) {
                return value;
            }

            return ogbuilder.newInstanceResolver.newInstance(klass, properties);
        }

        public void setChild(FactoryBuilderSupport builder, Object parent, Object child) {
            if (child == null) return;

            ObjectGraphBuilder ogbuilder = (ObjectGraphBuilder) builder;
            if (parent != null) {
                Map context = ogbuilder.getContext();
                Map parentContext = ogbuilder.getParentContext();

                String parentName = null;
                String childName = (String) context.get(NODE_NAME);
                if (parentContext != null) {
                    parentName = (String) parentContext.get(NODE_NAME);
                }

                String propertyName = ogbuilder.relationNameResolver.resolveParentRelationName(
                        parentName, parent, childName, child);
                MetaProperty metaProperty = InvokerHelper.getMetaClass(child)
                        .hasProperty(child, propertyName);
                if (metaProperty != null) {
                    metaProperty.setProperty(child, parent);
                }
            }
        }

        public void setParent(FactoryBuilderSupport builder, Object parent, Object child) {
            if (child == null) return;

            ObjectGraphBuilder ogbuilder = (ObjectGraphBuilder) builder;
            if (parent != null) {
                Map context = ogbuilder.getContext();
                Map parentContext = ogbuilder.getParentContext();

                String parentName = null;
                String childName = (String) context.get(NODE_NAME);
                if (parentContext != null) {
                    parentName = (String) parentContext.get(NODE_NAME);
                }

                ogbuilder.childPropertySetter.setChild(parent, child, parentName,
                        ogbuilder.relationNameResolver.resolveChildRelationName(parentName,
                                parent, childName, child));
            }
        }

        protected Class loadClass(ClassLoader classLoader, String classname) {
            if (classLoader == null || classname == null) {
                return null;
            }
            try {
                return classLoader.loadClass(classname);
            } catch (ClassNotFoundException e) {
                return null;
            }
        }
    }

    private static class ObjectBeanFactory extends ObjectFactory {
        public Object newInstance(FactoryBuilderSupport builder, Object name, Object value,
                                  Map properties) throws InstantiationException, IllegalAccessException {
            if(value == null) return super.newInstance(builder, name, value, properties);

            Object bean = null;
            Class klass = null;
            Map context = builder.getContext();
            if(value instanceof String || value instanceof GString) {
                /*
                String classname = value.toString();
                klass = resolveClass(builder, classname, name, value, properties);
                bean = resolveInstance(builder, name, value, klass, properties);
                */
                throw new IllegalArgumentException("ObjectGraphBuilder."+((ObjectGraphBuilder)builder).getBeanFactoryName()+"() does not accept String nor GString as value.");
            } else if(value instanceof Class) {
                klass = (Class) value;
                bean = resolveInstance(builder, name, value, klass, properties);
            } else {
                klass = value.getClass();
                bean = value;
            }

            String nodename = klass.getSimpleName();
            if(nodename.length() > 1) {
                nodename = nodename.substring(0, 1).toLowerCase() + nodename.substring(1);
            } else {
                nodename = nodename.toLowerCase();
            }
            context.put(ObjectGraphBuilder.NODE_NAME, nodename);
            context.put(ObjectGraphBuilder.NODE_CLASS, klass);
            return bean;
        }
    }

    private static class ObjectRefFactory extends ObjectFactory {
        public boolean isLeaf() {
            return true;
        }

        public Object newInstance(FactoryBuilderSupport builder, Object name, Object value,
                                  Map properties) throws InstantiationException, IllegalAccessException {
            ObjectGraphBuilder ogbuilder = (ObjectGraphBuilder) builder;
            String refProperty = ogbuilder.referenceResolver.getReferenceFor((String) name);
            Object refId = properties.remove(refProperty);

            Object object = null;
            Boolean lazy = Boolean.FALSE;
            if (refId instanceof String) {
                try {
                    object = ogbuilder.getProperty((String) refId);
                } catch (MissingPropertyException mpe) {
                    // ignore, will try lazy reference
                }
                if (object == null) {
                    if (ogbuilder.isLazyReferencesAllowed()) {
                        lazy = Boolean.TRUE;
                    } else {
                        throw new IllegalArgumentException("There is no previous node with "
                                + ogbuilder.identifierResolver.getIdentifierFor((String) name) + "="
                                + refId);
                    }
                }
            } else {
                // assume we got a true reference to the object
                object = refId;
            }

            if (!properties.isEmpty()) {
                throw new IllegalArgumentException(
                        "You can not modify the properties of a referenced object.");
            }

            Map context = ogbuilder.getContext();
            context.put(ObjectGraphBuilder.NODE_NAME, name);
            context.put(ObjectGraphBuilder.LAZY_REF, lazy);

            if (lazy.booleanValue()) {
                Map parentContext = ogbuilder.getParentContext();

                Object parent = null;
                String parentName = null;
                String childName = (String) name;
                if (parentContext != null) {
                    parent = context.get(CURRENT_NODE);
                    parentName = (String) parentContext.get(NODE_NAME);
                }
                ogbuilder.lazyReferences.add(new NodeReference(parent,
                        parentName,
                        childName,
                        (String) refId));
            } else {
                context.put(ObjectGraphBuilder.NODE_CLASS, object.getClass());
            }

            return object;
        }

        public void setChild(FactoryBuilderSupport builder, Object parent, Object child) {
            Boolean lazy = (Boolean) builder.getContext().get(ObjectGraphBuilder.LAZY_REF);
            if (!lazy.booleanValue()) super.setChild(builder, parent, child);
        }

        public void setParent(FactoryBuilderSupport builder, Object parent, Object child) {
            Boolean lazy = (Boolean) builder.getContext().get(ObjectGraphBuilder.LAZY_REF);
            if (!lazy.booleanValue()) super.setParent(builder, parent, child);
        }
    }

    private static class NodeReference {
        private final Object parent;
        private final String parentName;
        private final String childName;
        private final String refId;

        private NodeReference(Object parent, String parentName, String childName, String refId) {
            this.parent = parent;
            this.parentName = parentName;
            this.childName = childName;
            this.refId = refId;
        }

        public String toString() {
            return new StringBuilder().append("[parentName=").append(parentName)
                    .append(", childName=").append(childName)
                    .append(", refId=").append(refId)
                    .append("]").toString();
        }
    }
}

Other Groovy examples (source code examples)

Here is a short list of links related to this Groovy ObjectGraphBuilder.java source code file:

new blog posts

 

Copyright 1998-2014 Alvin Alexander, alvinalexander.com
All Rights Reserved.