All Downloads are FREE. Search and download functionalities are using the official Maven repository.

groovy.util.BuilderSupport Maven / Gradle / Ivy

There is a newer version: 1.5.8
Show newest version
/*
 * 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.GroovyObjectSupport;
import groovy.lang.MissingMethodException;

import java.util.List;
import java.util.Map;

import org.codehaus.groovy.runtime.InvokerHelper;

/**
 * An abstract base class for creating arbitrary nested trees of objects
 * or events
 *
 * @author James Strachan
 * @version $Revision: 7922 $
 */
public abstract class BuilderSupport extends GroovyObjectSupport {

    private Object current;
    private Closure nameMappingClosure;
    private final BuilderSupport proxyBuilder;

    public BuilderSupport() {
        this.proxyBuilder = this;
    }

    public BuilderSupport(BuilderSupport proxyBuilder) {
        this(null, proxyBuilder);
    }

    public BuilderSupport(Closure nameMappingClosure, BuilderSupport proxyBuilder) {
        this.nameMappingClosure = nameMappingClosure;
        this.proxyBuilder = proxyBuilder;
    }

    /**
     * Convenience method when no arguments are required
     * @return the result of the call
     * @param methodName the name of the method to invoke
     */
    public Object invokeMethod(String methodName) {
        return invokeMethod(methodName, null);
    }

    public Object invokeMethod(String methodName, Object args) {
        Object name = getName(methodName);
        return doInvokeMethod(methodName, name, args);
    }

    protected Object doInvokeMethod(String methodName, Object name, Object args) {
        Object node = null;
        Closure closure = null;
        List list = InvokerHelper.asList(args);

        //System.out.println("Called invokeMethod with name: " + name + " arguments: " + list);

        switch (list.size()) {
        		case 0:
	    	            node = proxyBuilder.createNode(name);
        		    break;
        	    	case 1:
        	    	{
        	    	    	Object object = list.get(0);
        	    	    	if (object instanceof Map) {
        	    	    	    node = proxyBuilder.createNode(name, (Map) object);
        	    	    	} else if (object instanceof Closure) {
        	    	    	    closure = (Closure) object;
        	    	    	    node = proxyBuilder.createNode(name);
        	    	    	} else {
        	    	    	    node = proxyBuilder.createNode(name, object);
        	    	    	}
        	    	}
        	    	break;
        	    	case 2:
        	    	{
        	    	    Object object1 = list.get(0);
    	    	        Object object2 = list.get(1);
        	    	    if (object1 instanceof Map) {
        	    	        if (object2 instanceof Closure) {
        	    	            closure = (Closure) object2;
        	    	            node = proxyBuilder.createNode(name, (Map) object1);
        	    	        } else {
        	    	            node = proxyBuilder.createNode(name, (Map) object1, object2);
        	    	        }
        	    	    } else {
        	    	        if (object2 instanceof Closure) {
        	    	            closure = (Closure) object2;
        	    	            node = proxyBuilder.createNode(name, object1);
				} else if (object2 instanceof Map) {
				    node = proxyBuilder.createNode(name, (Map) object2, object1);
        	    	        } else {
				    throw new MissingMethodException(name.toString(), getClass(), list.toArray(), false);
				}
        	    	    }
        	    	}
        	    	break;
        	    	case 3:
        	    	{
        	    	    Object arg0 = list.get(0);
        	    	    Object arg1 = list.get(1);
        	    	    Object arg2 = list.get(2);
        	    	    if (arg0 instanceof Map && arg2 instanceof Closure) {
        	    	        closure = (Closure) arg2;
        	    	        node = proxyBuilder.createNode(name, (Map) arg0, arg1);
			    } else if (arg1 instanceof Map && arg2 instanceof Closure) {
        	    	        closure = (Closure) arg2;
        	    	        node = proxyBuilder.createNode(name, (Map) arg1, arg0);
			    } else {
				throw new MissingMethodException(name.toString(), getClass(), list.toArray(), false);
			   }
        	    	}
        	    	break;
        	    	default:
        	    	{
			    throw new MissingMethodException(name.toString(), getClass(), list.toArray(), false);
			}

        }

        if (current != null) {
            proxyBuilder.setParent(current, node);
        }

        if (closure != null) {
            // push new node on stack
            Object oldCurrent = current;
            current = node;

            // lets register the builder as the delegate
            setClosureDelegate(closure, node);
            closure.call();

            current = oldCurrent;
        }

        proxyBuilder.nodeCompleted(current, node);
        return proxyBuilder.postNodeCompletion(current, node);
    }

    /**
     * A strategy method to allow derived builders to use
     * builder-trees and switch in different kinds of builders.
     * This method should call the setDelegate() method on the closure
     * which by default passes in this but if node is-a builder
     * we could pass that in instead (or do something wacky too)
     *
     * @param closure the closure on which to call setDelegate()
     * @param node the node value that we've just created, which could be
     * a builder
     */
    protected void setClosureDelegate(Closure closure, Object node) {
        closure.setDelegate(this);
    }

    protected abstract void setParent(Object parent, Object child);
    protected abstract Object createNode(Object name);
    protected abstract Object createNode(Object name, Object value);
    protected abstract Object createNode(Object name, Map attributes);
    protected abstract Object createNode(Object name, Map attributes, Object value);

    /**
     * A hook to allow names to be converted into some other object
     * such as a QName in XML or ObjectName in JMX.
     *
     * @param methodName the name of the desired method
     * @return the object representing the name
     */
    protected Object getName(String methodName) {
        if (nameMappingClosure != null) {
            return nameMappingClosure.call(methodName);
        }
        return methodName;
    }


    /**
     * A hook to allow nodes to be processed once they have had all of their
     * children applied.
     *
     * @param node the current node being processed
     * @param parent the parent of the node being processed
     */
    protected void nodeCompleted(Object parent, Object node) {
    }

    /**
     * A hook to allow nodes to be processed once they have had all of their
     * children applied and allows the actual node object that represents
     * the Markup element to be changed
     *
     * @param node the current node being processed
     * @param parent the parent of the node being processed
     * @return the node, possibly new, that represents the markup element
     */
    protected Object postNodeCompletion(Object parent, Object node) {
        return node;
    }

    protected Object getCurrent() {
        return current;
    }

    protected void setCurrent(Object current) {
        this.current = current;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy