View Javadoc

    1   /*
    2    $Id: BuilderSupport.java 4247 2006-11-19 19:00:19Z mcspanky $
    3
    4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
    5
    6    Redistribution and use of this software and associated documentation
    7    ("Software"), with or without modification, are permitted provided
    8    that the following conditions are met:
    9
    10   1. Redistributions of source code must retain copyright
    11      statements and notices.  Redistributions must also contain a
    12      copy of this document.
    13
    14   2. Redistributions in binary form must reproduce the
    15      above copyright notice, this list of conditions and the
    16      following disclaimer in the documentation and/or other
    17      materials provided with the distribution.
    18
    19   3. The name "groovy" must not be used to endorse or promote
    20      products derived from this Software without prior written
    21      permission of The Codehaus.  For written permission,
    22      please contact info@codehaus.org.
    23
    24   4. Products derived from this Software may not be called "groovy"
    25      nor may "groovy" appear in their names without prior written
    26      permission of The Codehaus. "groovy" is a registered
    27      trademark of The Codehaus.
    28
    29   5. Due credit should be given to The Codehaus -
    30      http://groovy.codehaus.org/
    31
    32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
    33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
    34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
    36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    43   OF THE POSSIBILITY OF SUCH DAMAGE.
    44
    45   */
    46  package groovy.util;
    47
    48
    49  import groovy.lang.Closure;
    50  import groovy.lang.GroovyObjectSupport;
    51  import groovy.lang.MissingMethodException;
    52
    53  import java.util.List;
    54  import java.util.Map;
    55
    56  import org.codehaus.groovy.runtime.InvokerHelper;
    57
    58  /***
    59   * An abstract base class for creating arbitrary nested trees of objects
    60   * or events
    61   *
    62   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    63   * @version $Revision: 4247 $
    64   */
    65  public abstract class BuilderSupport extends GroovyObjectSupport {
    66
    67      private Object current;
    68      private Closure nameMappingClosure;
    69      private BuilderSupport proxyBuilder;
    70
    71      public BuilderSupport() {
    72          this.proxyBuilder = this;
    73      }
    74
    75      public BuilderSupport(BuilderSupport proxyBuilder) {
    76          this(null, proxyBuilder);
    77      }
    78
    79      public BuilderSupport(Closure nameMappingClosure, BuilderSupport proxyBuilder) {
    80          this.nameMappingClosure = nameMappingClosure;
    81          this.proxyBuilder = proxyBuilder;
    82      }
    83
    84      /***
    85       * Convenience method when no arguments are required
    86       * @return the result of the call
    87       * @param methodName the name of the method to invoke
    88       */
    89      public Object invokeMethod(String methodName) {
    90          return invokeMethod(methodName, null);
    91      }
    92
    93      public Object invokeMethod(String methodName, Object args) {
    94          Object name = getName(methodName);
    95          return doInvokeMethod(methodName, name, args);
    96      }
    97
    98      protected Object doInvokeMethod(String methodName, Object name, Object args) {
    99          Object node = null;
    100         Closure closure = null;
    101         List list = InvokerHelper.asList(args);
    102
    103         //System.out.println("Called invokeMethod with name: " + name + " arguments: " + list);
    104
    105         switch (list.size()) {
    106         		case 0:
    107 	    	            node = proxyBuilder.createNode(name);
    108         		    break;
    109         	    	case 1:
    110         	    	{
    111         	    	    	Object object = list.get(0);
    112         	    	    	if (object instanceof Map) {
    113         	    	    	    node = proxyBuilder.createNode(name, (Map) object);
    114         	    	    	} else if (object instanceof Closure) {
    115         	    	    	    closure = (Closure) object;
    116         	    	    	    node = proxyBuilder.createNode(name);
    117         	    	    	} else {
    118         	    	    	    node = proxyBuilder.createNode(name, object);
    119         	    	    	}
    120         	    	}
    121         	    	break;
    122         	    	case 2:
    123         	    	{
    124         	    	    Object object1 = list.get(0);
    125     	    	        Object object2 = list.get(1);
    126         	    	    if (object1 instanceof Map) {
    127         	    	        if (object2 instanceof Closure) {
    128         	    	            closure = (Closure) object2;
    129         	    	            node = proxyBuilder.createNode(name, (Map) object1);
    130         	    	        } else {
    131         	    	            node = proxyBuilder.createNode(name, (Map) object1, object2);
    132         	    	        }
    133         	    	    } else {
    134         	    	        if (object2 instanceof Closure) {
    135         	    	            closure = (Closure) object2;
    136         	    	            node = proxyBuilder.createNode(name, object1);
    137 				} else if (object2 instanceof Map) {
    138 				    node = proxyBuilder.createNode(name, (Map) object2, object1);
    139         	    	        } else {
    140 				    throw new MissingMethodException(name.toString(), getClass(), list.toArray(), false);
    141 				}
    142         	    	    }
    143         	    	}
    144         	    	break;
    145         	    	case 3:
    146         	    	{
    147         	    	    Object arg0 = list.get(0);
    148         	    	    Object arg1 = list.get(1);
    149         	    	    Object arg2 = list.get(2);
    150         	    	    if (arg0 instanceof Map && arg2 instanceof Closure) {
    151         	    	        closure = (Closure) arg2;
    152         	    	        node = proxyBuilder.createNode(name, (Map) arg0, arg1);
    153 			    } else if (arg1 instanceof Map && arg2 instanceof Closure) {
    154         	    	        closure = (Closure) arg2;
    155         	    	        node = proxyBuilder.createNode(name, (Map) arg1, arg0);
    156 			    } else {
    157 				throw new MissingMethodException(name.toString(), getClass(), list.toArray(), false);
    158 			   }
    159         	    	}
    160         	    	break;
    161         	    	default:
    162         	    	{
    163 			    throw new MissingMethodException(name.toString(), getClass(), list.toArray(), false);
    164 			}
    165
    166         }
    167
    168         if (current != null) {
    169             proxyBuilder.setParent(current, node);
    170         }
    171
    172         if (closure != null) {
    173             // push new node on stack
    174             Object oldCurrent = current;
    175             current = node;
    176
    177             // lets register the builder as the delegate
    178             setClosureDelegate(closure, node);
    179             closure.call();
    180
    181             current = oldCurrent;
    182         }
    183
    184         proxyBuilder.nodeCompleted(current, node);
    185         return node;
    186     }
    187
    188     /***
    189      * A strategy method to allow derived builders to use
    190      * builder-trees and switch in different kinds of builders.
    191      * This method should call the setDelegate() method on the closure
    192      * which by default passes in this but if node is-a builder
    193      * we could pass that in instead (or do something wacky too)
    194      *
    195      * @param closure the closure on which to call setDelegate()
    196      * @param node the node value that we've just created, which could be
    197      * a builder
    198      */
    199     protected void setClosureDelegate(Closure closure, Object node) {
    200         closure.setDelegate(this);
    201     }
    202
    203     protected abstract void setParent(Object parent, Object child);
    204     protected abstract Object createNode(Object name);
    205     protected abstract Object createNode(Object name, Object value);
    206     protected abstract Object createNode(Object name, Map attributes);
    207     protected abstract Object createNode(Object name, Map attributes, Object value);
    208
    209     /***
    210      * A hook to allow names to be converted into some other object
    211      * such as a QName in XML or ObjectName in JMX
    212      * @param methodName
    213      */
    214     protected Object getName(String methodName) {
    215         if (nameMappingClosure != null) {
    216             return nameMappingClosure.call(methodName);
    217         }
    218         return methodName;
    219     }
    220
    221
    222     /***
    223      * A hook to allow nodes to be processed once they have had all of their
    224      * children applied
    225      */
    226     protected void nodeCompleted(Object parent, Object node) {
    227     }
    228
    229     protected Object getCurrent() {
    230         return current;
    231     }
    232
    233     protected void setCurrent(Object current) {
    234         this.current = current;
    235     }
    236 }