Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * 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 java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.TreeSet;
Mix of BuilderSupport and SwingBuilder's factory support. Warning: this implementation is not thread safe and should not be used across threads in a multi-threaded environment. A locking mechanism should be implemented by the subclass if use is expected across multiple threads.

Author(s):
James Strachan
Andres Almiray <aalmiray@users.sourceforge.com>
Danno Ferrin
  
  public abstract class FactoryBuilderSupport extends Binding {
      public static final String CURRENT_FACTORY = "_CURRENT_FACTORY_";
      public static final String PARENT_FACTORY = "_PARENT_FACTORY_";
      public static final String PARENT_NODE = "_PARENT_NODE_";
      public static final String CURRENT_NODE = "_CURRENT_NODE_";
      public static final String PARENT_CONTEXT = "_PARENT_CONTEXT_";
      public static final String PARENT_NAME = "_PARENT_NAME_";
      public static final String CURRENT_NAME = "_CURRENT_NAME_";
      public static final String OWNER = "owner";
      public static final String PARENT_BUILDER = "_PARENT_BUILDER_";
      public static final String CURRENT_BUILDER = "_CURRENT_BUILDER_";
      public static final String CHILD_BUILDER = "_CHILD_BUILDER_";
      private static final Logger LOG = Logger.getLogger(FactoryBuilderSupport.class.getName());

    
Throws an exception if value is null.

Parameters:
value the node's value
name the node's name
  
      public static void checkValueIsNull(Object valueObject name) {
          if (value != null) {
              throw new RuntimeException("'" + name + "' elements do not accept a value argument.");
          }
      }

    
Checks type of value against buidler type

Parameters:
value the node's value
name the node's name
type a Class that may be assignable to the value's class
Returns:
true if type is assignalbe to the value's class, false if value is null.
  
      public static boolean checkValueIsType(Object valueObject nameClass type) {
          if (value != null) {
              if (type.isAssignableFrom(value.getClass())) {
                  return true;
              } else {
                  throw new RuntimeException("The value argument of '" + name + "' must be of type "
                         + type.getName());
             }
         } else {
             return false;
         }
     }

    
Checks values against factory's type

Parameters:
value the node's value
name the node's name
type a Class that may be assignable to the value's class
Returns:
Returns true if type is assignale to the value's class, false if value is null or a String.
 
     public static boolean checkValueIsTypeNotString(Object valueObject nameClass type) {
         if (value != null) {
             if (type.isAssignableFrom(value.getClass())) {
                 return true;
             } else if (value instanceof String) {
                 return false;
             } else {
                 throw new RuntimeException("The value argument of '" + name + "' must be of type "
                         + type.getName() + " or a String.");
             }
         } else {
             return false;
         }
     }
 
     protected LinkedList<ClosureattributeDelegates = new LinkedList<Closure>(); //
     private List<ClosuredisposalClosures = new ArrayList<Closure>(); // because of reverse iteration use ArrayList
     private Map<StringFactoryfactories = new HashMap<StringFactory>();
     private Closure nameMappingClosure;
     protected LinkedList<ClosurepreInstantiateDelegates = new LinkedList<Closure>();
     protected Map<StringClosure[]> explicitProperties = new HashMap<StringClosure[]>();
     protected Map<StringClosureexplicitMethods = new HashMap<StringClosure>();
     protected Map<StringSet<String>> registrationGroup = new HashMap<StringSet<String>>();
     protected String registringGroupName = ""// use binding to store?
 
     protected boolean autoRegistrationRunning = false;
     protected boolean autoRegistrationComplete = false;
 
     public FactoryBuilderSupport() {
         this(false);
     }
 
     public FactoryBuilderSupport(boolean init) {
          = this;
         if (init) {
             autoRegisterNodes();
         }
     }

    
Ask the nodes to be registered
 
     public void autoRegisterNodes() {
         // if java did atomic blocks, this would be one
         synchronized (this) {
             if ( || ) {
                 // registration already done or in process, abort
                 return;
             }
         }
          = true;
         try {
             callAutoRegisterMethods(getClass());
         } finally {
              = true;
              = false;
         }
     }
 
     private void callAutoRegisterMethods(Class declaredClass) {
         if (declaredClass == null) {
             return;
         }
         callAutoRegisterMethods(declaredClass.getSuperclass());
 
         for (Method method : declaredClass.getDeclaredMethods()) {
             if (method.getName().startsWith("register") && method.getParameterTypes().length == 0) {
                  = method.getName().substring("register".length());
                 .put(new TreeSet<String>());
                 try {
                     if (Modifier.isPublic(method.getModifiers())) {
                         method.invoke(this);
                     }
                 } catch (IllegalAccessException e) {
                     throw new RuntimeException("Cound not init " + getClass().getName() + " because of an access error in " + declaredClass.getName() + "." + method.getName(), e);
                 } catch (InvocationTargetException e) {
                     throw new RuntimeException("Cound not init " + getClass().getName() + " because of an exception in " + declaredClass.getName() + "." + method.getName(), e);
                 } finally {
                      = "";
                 }
             }
         }
     }
 
     @Deprecated
     public FactoryBuilderSupport(Closure nameMappingClosure) {
         this. = nameMappingClosure;
     }

    

Parameters:
name the name of the variable to lookup
Returns:
the variable value
 
     public Object getVariable(String name) {
         return getProxyBuilder().doGetVariable(name);
     }
 
     private Object doGetVariable(String name) {
         return super.getVariable(name);
     }

    
Sets the value of the given variable

Parameters:
name the name of the variable to set
value the new value for the given variable
 
     public void setVariable(String nameObject value) {
         getProxyBuilder().doSetVariable(namevalue);
     }
 
     private void doSetVariable(String nameObject value) {
         super.setVariable(namevalue);
     }
 
     public Map getVariables() {
         return getProxyBuilder().doGetVariables();
     }
 
     private Map doGetVariables() {
         return super.getVariables();
     }

    
Overloaded to make variables appear as bean properties or via the subscript operator
 
     public Object getProperty(String property) {
         try {
             return getProxyBuilder().doGetProperty(property);
         } catch (MissingPropertyException mpe) {
             if ((getContext() != null) && (getContext().containsKey(property))) {
                 return getContext().get(property);
             } else {
                 return getMetaClass().getProperty(thisproperty);
             }
         }
     }
 
     private Object doGetProperty(String property) {
         Closure[] accessors = resolveExplicitProperty(property);
         if (accessors != null) {
             if (accessors[0] == null) {
                 // write only property
                 throw new MissingPropertyException(property + " is declared as write only");
             } else {
                 return accessors[0].call();
             }
         } else {
             return super.getProperty(property);
         }
     }

    
Overloaded to make variables appear as bean properties or via the subscript operator
 
     public void setProperty(String propertyObject newValue) {
         getProxyBuilder().doSetProperty(propertynewValue);
     }
 
     private void doSetProperty(String propertyObject newValue) {
         Closure[] accessors = resolveExplicitProperty(property);
         if (accessors != null) {
             if (accessors[1] == null) {
                 // read only property
                 throw new MissingPropertyException(property + " is declared as read only");
             } else {
                 accessors[1].call(newValue);
             }
         } else {
             super.setProperty(propertynewValue);
         }
     }

    

Returns:
the factory map (Unmodifiable Map).
 
     public Map<StringFactorygetFactories() {
         return Collections.unmodifiableMap(getProxyBuilder().);
     }

    

Returns:
the explicit methods map (Unmodifiable Map).
 
     public Map<StringClosuregetExplicitMethods() {
         return Collections.unmodifiableMap(getProxyBuilder().);
     }

    

Returns:
the explicit properties map (Unmodifiable Map).
 
     public Map<StringClosure[]> getExplicitProperties() {
         return Collections.unmodifiableMap(getProxyBuilder().);
     }

    

Returns:
the factory map (Unmodifiable Map).
 
     public Map<StringFactorygetLocalFactories() {
         return Collections.unmodifiableMap();
     }

    

Returns:
the explicit methods map (Unmodifiable Map).
 
     public Map<StringClosuregetLocalExplicitMethods() {
         return Collections.unmodifiableMap();
     }

    

Returns:
the explicit properties map (Unmodifiable Map).
 
     public Map<StringClosure[]> getLocalExplicitProperties() {
         return Collections.unmodifiableMap();
     }
 
     public Set<StringgetRegistrationGroups() {
         return Collections.unmodifiableSet(.keySet());
     }
 
     public Set<StringgetRegistrationGroupItems(String group) {
         Set<StringgroupSet = .get(group);
         if (groupSet != null) {
             return Collections.unmodifiableSet(groupSet);
         } else {
             return Collections.emptySet();
         }
     }
 
     public List<ClosuregetAttributeDelegates() {
         return Collections.unmodifiableList();
     }
 
     public List<ClosuregetPreInstantiateDelegates() {
         return Collections.unmodifiableList();
     }
 
         return Collections.unmodifiableList();
     }
 
         return Collections.unmodifiableList();
     }

    

Returns:
the context of the current node.
 
     public Map<StringObjectgetContext() {
         LinkedList<Map<StringObject>> contexts = getProxyBuilder()..get();
         if (contexts != null && !contexts.isEmpty()) {
             return contexts.getFirst();
         }
         return null;
     }

    

Returns:
the current node being built.
 
     public Object getCurrent() {
         return getContextAttribute();
     }

    

Returns:
the factory that built the current node.
 
     public Factory getCurrentFactory() {
         return (FactorygetContextAttribute();
     }

    

Returns:
the factory of the parent of the current node.
 
     public String getCurrentName() {
         return (StringgetContextAttribute();
     }

    

Returns:
the builder that built the current node.
 
     }

    

Returns:
the node of the parent of the current node.
 
     public Object getParentNode() {
         return getContextAttribute();
     }

    

Returns:
the factory of the parent of the current node.
 
     public Factory getParentFactory() {
         return (FactorygetContextAttribute();
     }

    

Returns:
the context of the parent of the current node.
 
     public Map getParentContext() {
         return (MapgetContextAttribute();
     }

    

Returns:
the name of the parent of the current node.
 
     public String getParentName() {
         return (StringgetContextAttribute();
     }
 
     }
 
     public Object getContextAttribute(String key) {
         Map context = getContext();
         if (context != null) {
             return context.get(key);
         }
         return null;
     }

    
Convenience method when no arguments are required

Parameters:
methodName the name of the method to invoke
Returns:
the result of the call
 
     public Object invokeMethod(String methodName) {
         return getProxyBuilder().invokeMethod(methodNamenull);
     }
 
     public Object invokeMethod(String methodNameObject args) {
         Object name = getProxyBuilder().getName(methodName);
         Object result;
         Object previousContext = getProxyBuilder().getContext();
         try {
             result = getProxyBuilder().doInvokeMethod(methodNamenameargs);
         } catch (RuntimeException e) {
             // remove contexts created after we started
             if (getContexts().contains(previousContext)) {
                 Map<StringObjectcontext = getProxyBuilder().getContext();
                 while (context != null && context != previousContext) {
                     getProxyBuilder().popContext();
                     context = getProxyBuilder().getContext();
                 }
             }
             throw e;
         }
         return result;
     }

    
Add an attribute delegate so it can intercept attributes being set. Attribute delegates are fired in a FILO pattern, so that nested delegates get first crack.

Parameters:
attrDelegate the closure to be called
Returns:
attrDelegate
 
     public Closure addAttributeDelegate(Closure attrDelegate) {
         getProxyBuilder()..addFirst(attrDelegate);
         return attrDelegate;
     }

    
Remove the most recently added instance of the attribute delegate.

Parameters:
attrDelegate the instance of the closure to be removed
 
     public void removeAttributeDelegate(Closure attrDelegate) {
         getProxyBuilder()..remove(attrDelegate);
     }

    
Add a preInstantiate delegate so it can intercept nodes before they are created. PreInstantiate delegates are fired in a FILO pattern, so that nested delegates get first crack.

Parameters:
delegate the closure to invoke
Returns:
delegate
 
     public Closure addPreInstantiateDelegate(Closure delegate) {
         getProxyBuilder()..addFirst(delegate);
         return delegate;
     }

    
Remove the most recently added instance of the preInstantiate delegate.

Parameters:
delegate the closure to invoke
 
     public void removePreInstantiateDelegate(Closure delegate) {
         getProxyBuilder()..remove(delegate);
     }

    
Add a postInstantiate delegate so it can intercept nodes after they are created. PostInstantiate delegates are fired in a FILO pattern, so that nested delegates get first crack.

Parameters:
delegate the closure to invoke
Returns:
delegate
 
     public Closure addPostInstantiateDelegate(Closure delegate) {
         return delegate;
     }

    
Remove the most recently added instance of the postInstantiate delegate.

Parameters:
delegate the closure to invoke
 
     public void removePostInstantiateDelegate(Closure delegate) {
         getProxyBuilder()..remove(delegate);
     }

    
Add a nodeCompletion delegate so it can intercept nodes after they done with building. NodeCompletion delegates are fired in a FILO pattern, so that nested delegates get first crack.

Parameters:
delegate the closure to invoke
Returns:
delegate
 
     public Closure addPostNodeCompletionDelegate(Closure delegate) {
         return delegate;
     }

    
Remove the most recently added instance of the nodeCompletion delegate.

Parameters:
delegate the closure to be removed
 
     public void removePostNodeCompletionDelegate(Closure delegate) {
     }
 
     public void registerExplicitProperty(String nameClosure getterClosure setter) {
         registerExplicitProperty(namegettersetter);
     }
 
     public void registerExplicitProperty(String nameString groupNameClosure getterClosure setter) {
         // set the delegate to FBS so the closure closes over the builder
         if (getter != nullgetter.setDelegate(this);
         if (setter != nullsetter.setDelegate(this);
         .put(namenew Closure[]{gettersetter});
         String methodNameBase = MetaClassHelper.capitalize(name);
         if (getter != null) {
             .get(groupName).add("get" + methodNameBase);
         }
         if (setter != null) {
             .get(groupName).add("set" + methodNameBase);
         }
     }
 
     public void registerExplicitMethod(String nameClosure closure) {
         registerExplicitMethod(nameclosure);
     }
 
     public void registerExplicitMethod(String nameString groupNameClosure closure) {
         // set the delegate to FBS so the closure closes over the builder
         closure.setDelegate(this);
         .put(nameclosure);
         .get(groupName).add(name);
     }

    
Registers a factory for a JavaBean.
The JavaBean clas should have a no-args constructor.

Parameters:
theName name of the node
beanClass the factory to handle the name
 
     public void registerBeanFactory(String theNameClass beanClass) {
         registerBeanFactory(theNamebeanClass);
     }

    
Registers a factory for a JavaBean.
The JavaBean clas should have a no-args constructor.

Parameters:
theName name of the node
groupName thr group to register this node in
beanClass the factory to handle the name
 
     public void registerBeanFactory(String theNameString groupNamefinal Class beanClass) {
         getProxyBuilder().registerFactory(theNamenew AbstractFactory() {
             public Object newInstance(FactoryBuilderSupport builderObject nameObject value,
                                       Map propertiesthrows InstantiationExceptionIllegalAccessException {
                 if (checkValueIsTypeNotString(valuenamebeanClass)) {
                     return value;
                 } else {
                     return beanClass.newInstance();
                 }
             }
 
         });
         .get(groupName).add(theName);
     }

    
Registers a factory for a node name.

Parameters:
name the name of the node
factory the factory to return the values
 
     public void registerFactory(String nameFactory factory) {
         registerFactory(namefactory);
     }

    
Registers a factory for a node name.

Parameters:
name the name of the node
groupName thr group to register this node in
factory the factory to return the values
 
     public void registerFactory(String nameString groupNameFactory factory) {
         getProxyBuilder()..put(namefactory);
         .get(groupName).add(name);
         factory.onFactoryRegistration(thisnamegroupName);
     }

    
This method is responsible for instanciating a node and configure its properties.

Parameters:
name the name of the node
attributes the attributes for the node
value the value arguments for the node
Returns:
the object return from the factory
 
     protected Object createNode(Object nameMap attributesObject value) {
         Object node;
 
         Factory factory = getProxyBuilder().resolveFactory(nameattributesvalue);
         if (factory == null) {
             .log(."Could not find match for name '" + name + "'");
             throw new MissingMethodExceptionNoStack((StringnameObject.classnew Object[]{attributesvalue});
             //return null;
         }
         getProxyBuilder().getContext().put(factory);
         getProxyBuilder().getContext().put(, String.valueOf(name));
         getProxyBuilder().preInstantiate(nameattributesvalue);
         try {
             node = factory.newInstance(getProxyBuilder().getChildBuilder(), namevalueattributes);
             if (node == null) {
                 .log(."Factory for name '" + name + "' returned null");
                 return null;
             }
 
             if (.isLoggable(.)) {
                 .fine("For name: " + name + " created node: " + node);
             }
         } catch (Exception e) {
             throw new RuntimeException("Failed to create component for '" + name + "' reason: "
                     + ee);
         }
         getProxyBuilder().postInstantiate(nameattributesnode);
         getProxyBuilder().handleNodeAttributes(nodeattributes);
         return node;
     }

    
This is a hook for subclasses to plugin a custom strategy for mapping names to factories.

Parameters:
name the name of the factory
attributes the attributes from the node
value value arguments from te node
Returns:
the Factory associated with name.
 
     protected Factory resolveFactory(Object nameMap attributesObject value) {
         return getProxyBuilder()..get(name);
     }

    
This is a hook for subclasses to plugin a custom strategy for mapping names to explicit methods.

Parameters:
methodName the name of the explicit method
args the arguments for the method
Returns:
the closure for the matched explicit method.
 
     @SuppressWarnings({"UnusedDeclaration"})
     protected Closure resolveExplicitMethod(String methodNameObject args) {
         return .get(methodName);
     }

    
This is a hook for subclasses to plugin a custom strategy for mapping names to property methods.

Parameters:
propertyName the name of the explicit method
Returns:
the get and set closures (in that order) for the matched explicit property.
 
     protected Closure[] resolveExplicitProperty(String propertyName) {
         return .get(propertyName);
     }

    
This method is the workhorse of the builder.

Parameters:
methodName the name of the method being invoked
name the name of the node
args the arguments passed into the node
Returns:
the object from the factory
 
     private Object doInvokeMethod(String methodNameObject nameObject args) {
         Reference explicitResult = new Reference();
         if (checkExplicitMethod(methodNameargsexplicitResult)) {
             return explicitResult.get();
         } else {
             return dispathNodeCall(nameargs);
         }
     }
 
     protected boolean checkExplicitMethod(String methodNameObject argsReference result) {
         Closure explicitMethod = resolveExplicitMethod(methodNameargs);
         if (explicitMethod != null) {
             if (args instanceof Object[]) {
                 result.set(explicitMethod.call((Object[]) args));
             } else {
                 //todo push through InvokerHelper.asList?
                 result.set(explicitMethod.call(args));
             }
             return true;
         } else {
             return false;
         }
     }
 
     protected Object dispathNodeCall(Object nameObject args) {
         Object node;
         Closure closure = null;
         List list = InvokerHelper.asList(args);
 
         final boolean needToPopContext;
         if (getProxyBuilder().getContexts().isEmpty()) {
             // should be called on first build method only
             getProxyBuilder().newContext();
             needToPopContext = true;
         } else {
             needToPopContext = false;
         }
 
         try {
             Map namedArgs = .;
 
             // the arguments come in like [named_args?, args..., closure?]
             // so peel off a hashmap from the front, and a closure from the
             // end and presume that is what they meant, since there is
             // no way to distinguish node(a:b,c,d) {..} from
             // node([a:b],[c,d], {..}), i.e. the user can deliberatly confuse
             // the builder and there is nothing we can really do to prevent
             // that
 
             if ((list.size() > 0)
                     && (list.get(0) instanceof LinkedHashMap)) {
                 namedArgs = (Maplist.get(0);
                 list = list.subList(1, list.size());
             }
             if ((list.size() > 0)
                     && (list.get(list.size() - 1) instanceof Closure)) {
                 closure = (Closurelist.get(list.size() - 1);
                 list = list.subList(0, list.size() - 1);
             }
             Object arg;
             if (list.size() == 0) {
                 arg = null;
             } else if (list.size() == 1) {
                 arg = list.get(0);
             } else {
                 arg = list;
             }
             node = getProxyBuilder().createNode(namenamedArgsarg);
 
             Object current = getProxyBuilder().getCurrent();
             if (current != null) {
                 getProxyBuilder().setParent(currentnode);
             }
 
             if (closure != null) {
                 Factory parentFactory = getProxyBuilder().getCurrentFactory();
                 if (parentFactory.isLeaf()) {
                     throw new RuntimeException("'" + name + "' doesn't support nesting.");
                 }
                 boolean processContent = true;
                 if (parentFactory.isHandlesNodeChildren()) {
                     processContent = parentFactory.onNodeChildren(thisnodeclosure);
                 }
                 if (processContent) {
                     // push new node on stack
                     String parentName = getProxyBuilder().getCurrentName();
                     Map parentContext = getProxyBuilder().getContext();
                     getProxyBuilder().newContext();
                     try {
                         getProxyBuilder().getContext().put(closure.getOwner());
                         getProxyBuilder().getContext().put(node);
                         getProxyBuilder().getContext().put(parentFactory);
                         getProxyBuilder().getContext().put(current);
                         getProxyBuilder().getContext().put(parentContext);
                         getProxyBuilder().getContext().put(parentName);
                         getProxyBuilder().getContext().put(parentContext.get());
                         getProxyBuilder().getContext().put(parentContext.get());
                         // lets register the builder as the delegate
                         getProxyBuilder().setClosureDelegate(closurenode);
                         closure.call();
                     } finally {
                         getProxyBuilder().popContext();
                     }
                 }
             }
 
             getProxyBuilder().nodeCompleted(currentnode);
             node = getProxyBuilder().postNodeCompletion(currentnode);
         } finally {
             if (needToPopContext) {
                 // pop the first context
                 getProxyBuilder().popContext();
             }
         }
         return node;
     }

    
A hook to allow names to be converted into some other object such as a QName in XML or ObjectName in JMX.

Parameters:
methodName the name of the desired method
Returns:
the object representing the name
 
     public Object getName(String methodName) {
         if (getProxyBuilder(). != null) {
             return getProxyBuilder()..call(methodName);
         }
         return methodName;
     }

    
Proxy builders are useful for changing the building context, thus enabling mix & match builders.

Returns:
the current builder that serves as a proxy.
 
     protected FactoryBuilderSupport getProxyBuilder() {
         FactoryBuilderSupport proxy = .get();
         if (proxy == null) {
             return ;
         } else {
             return proxy;
         }
     }

    
Sets the builder to be used as a proxy.

Parameters:
proxyBuilder the new proxy
 
     protected void setProxyBuilder(FactoryBuilderSupport proxyBuilder) {
          = proxyBuilder;
     }
 
     public Closure getNameMappingClosure() {
         return ;
     }
 
     public void setNameMappingClosure(Closure nameMappingClosure) {
         this. = nameMappingClosure;
     }

    
Assigns any existing properties to the node.
It will call attributeDelegates before passing control to the factory that built the node.

Parameters:
node the object returned by tne node factory
attributes the attributes for the node
 
     protected void handleNodeAttributes(Object nodeMap attributes) {
         // first, short circuit
         if (node == null) {
             return;
         }
 
         for (Closure attrDelegate : getProxyBuilder().) {
             FactoryBuilderSupport builder = this;
             if (attrDelegate.getOwner() instanceof FactoryBuilderSupport) {
                 builder = (FactoryBuilderSupportattrDelegate.getOwner();
             } else if (attrDelegate.getDelegate() instanceof FactoryBuilderSupport) {
                 builder = (FactoryBuilderSupportattrDelegate.getDelegate();
             }
 
             attrDelegate.call(new Object[]{buildernodeattributes});
         }
 
         if (getProxyBuilder().getCurrentFactory().onHandleNodeAttributes(getProxyBuilder().getChildBuilder(), nodeattributes)) {
             getProxyBuilder().setNodeAttributes(nodeattributes);
         }
     }

    
Pushes a new context on the stack.
 
     protected void newContext() {
         getContexts().addFirst(new HashMap<StringObject>());
     }

    
A hook to allow nodes to be processed once they have had all of their children applied.

Parameters:
node the current node being processed
parent the parent of the node being processed
 
     protected void nodeCompleted(Object parentObject node) {
     }

    
Removes the last context from the stack.

Returns:
the contet just removed
 
     protected Map<StringObjectpopContext() {
         if (!getProxyBuilder().getContexts().isEmpty()) {
             return getProxyBuilder().getContexts().removeFirst();
         }
         return null;
     }

    
A hook after the factory creates the node and before attributes are set.
It will call any registered postInstantiateDelegates, if you override this method be sure to call this impl somewhere in your code.

Parameters:
name the name of the node
attributes the attributes for the node
node the object created by teh node factory
 
     protected void postInstantiate(Object nameMap attributesObject node) {
         for (Closure postInstantiateDelegate : getProxyBuilder().) {
             (postInstantiateDelegate).call(new Object[]{thisattributesnode});
         }
     }

    
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.
It will call any registered postNodeCompletionDelegates, if you override this method be sure to call this impl at the end of your code.

Parameters:
node the current node being processed
parent the parent of the node being processed
Returns:
the node, possibly new, that represents the markup element
 
     protected Object postNodeCompletion(Object parentObject node) {
         for (Closure postNodeCompletionDelegate : getProxyBuilder().) {
             (postNodeCompletionDelegate).call(new Object[]{thisparentnode});
         }
 
         return node;
     }

    
A hook before the factory creates the node.
It will call any registered preInstantiateDelegates, if you override this method be sure to call this impl somewhere in your code.

Parameters:
name the name of the node
attributes the attributes of the node
value the value argument(s) of the node
    protected void preInstantiate(Object nameMap attributesObject value) {
        for (Closure preInstantiateDelegate : getProxyBuilder().) {
            (preInstantiateDelegate).call(new Object[]{thisattributesvalue});
        }
    }

    
Clears the context stack.
    protected void reset() {
        getProxyBuilder().getContexts().clear();
    }

    
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)

Parameters:
closure the closure on which to call setDelegate()
node the node value that we've just created, which could be a builder
    @SuppressWarnings({"UnusedDeclaration"})
    protected void setClosureDelegate(Closure closureObject node) {
        closure.setDelegate(this);
    }

    
Maps attributes key/values to properties on node.

Parameters:
node the object from the node
attributes the attributtes to be set
    protected void setNodeAttributes(Object nodeMap attributes) {
        // set the properties
        //noinspection unchecked
        for (Map.Entry entry : (Set<Map.Entry>) attributes.entrySet()) {
            String property = entry.getKey().toString();
            Object value = entry.getValue();
            InvokerHelper.setProperty(nodepropertyvalue);
        }
    }

    
Strategy method to stablish parent/child relationships.

Parameters:
parent the object from the parent node
child the object from the child node
    protected void setParent(Object parentObject child) {
        Factory parentFactory = getProxyBuilder().getParentFactory();
        if (parentFactory != null) {
            parentFactory.setChild(getProxyBuilder().getCurrentBuilder(), parentchild);
        }
    }

    

Returns:
the stack of available contexts.
    protected LinkedList<Map<StringObject>> getContexts() {
        LinkedList<Map<StringObject>> contexts = getProxyBuilder()..get();
        if (contexts == null) {
            contexts = new LinkedList<Map<StringObject>>();
            getProxyBuilder()..set(contexts);
        }
        return contexts;
    }

    
Stores the thread local states in a Map that can be passed across threads

Returns:
the map
    protected Map<StringObjectgetContinuationData() {
        Map<StringObjectdata = new HashMap<StringObject>();
        data.put("proxyBuilder".get());
        data.put("contexts".get());
        return data;
    }

    
Restores the state of the curent builder to the same state as an older build. Caution, this will destroy rather than merge the current build context if there is any,

Parameters:
data the data retrieved from a compatible getContinuationData call
    protected void restoreFromContinuationData(Map<StringObjectdata) {
        //noinspection unchecked
        .set((FactoryBuilderSupportdata.get("proxyBuilder"));
        //noinspection unchecked
        .set((LinkedList<Map<StringObject>>) data.get("contexts"));
    }
    public Object build(Class viewClass) {
        if (Script.class.isAssignableFrom(viewClass)) {
            Script script = InvokerHelper.createScript(viewClassthis);
            return build(script);
        } else {
            throw new RuntimeException("Only scripts can be executed via build(Class)");
        }
    }
    public Object build(Script script) {
        // this used to be synchronized, but we also used to remove the
        // metaclass.  Since adding the metaclass is now a side effect, we
        // don't need to ensure the meta-class won't be observed and don't
        // need to hide the side effect.
        MetaClass scriptMetaClass = script.getMetaClass();
        script.setMetaClass(new FactoryInterceptorMetaClass(scriptMetaClassthis));
        script.setBinding(this);
        return script.run();
    }
    public Object build(final String scriptGroovyClassLoader loader) {
        return build(loader.parseClass(script));
    }

    
Switches the builder's proxyBuilder during the execution of a closure.
This is useful to temporary change the building context to another builder without the need for a contrived setup. It will also take care of restoring the previous proxyBuilder when the execution finishes, even if an exception was thrown from inside the closure.

Parameters:
builder the temporary builder to switch to as proxyBuilder.
closure the closure to be executed under the temporary builder.
Returns:
the execution result of the closure.
Throws:
java.lang.RuntimeException - any exception the closure might have thrown during execution.
    public Object withBuilder(FactoryBuilderSupport builderClosure closure) {
        if (builder == null || closure == null) {
            return null;
        }
        Object result = null;
        Object previousContext = getProxyBuilder().getContext();
        FactoryBuilderSupport previousProxyBuilder = .get();
        try {
            .set(builder);
            closure.setDelegate(builder);
            result = closure.call();
        }
        catch (RuntimeException e) {
            // remove contexts created after we started
            .set(previousProxyBuilder);
            if (getProxyBuilder().getContexts().contains(previousContext)) {
                Map<StringObjectcontext = getProxyBuilder().getContext();
                while (context != null && context != previousContext) {
                    getProxyBuilder().popContext();
                    context = getProxyBuilder().getContext();
                }
            }
            throw e;
        }
        finally {
            .set(previousProxyBuilder);
        }
        return result;
    }

    
Switches the builder's proxyBuilder during the execution of a closure.
This is useful to temporary change the building context to another builder without the need for a contrived setup. It will also take care of restoring the previous proxyBuilder when the execution finishes, even if an exception was thrown from inside the closure. Additionally it will use the closure's result as the value for the node identified by 'name'.

Parameters:
builder the temporary builder to switch to as proxyBuilder.
name the node to build on the 'parent' builder.
closure the closure to be executed under the temporary builder.
Returns:
a node that responds to value of name with the closure's result as its value.
Throws:
java.lang.RuntimeException - any exception the closure might have thrown during execution.
    public Object withBuilder(FactoryBuilderSupport builderString nameClosure closure) {
        if (name == null) {
            return null;
        }
        Object result = getProxyBuilder().withBuilder(builderclosure);
        return getProxyBuilder().invokeMethod(namenew Object[]{result});
    }

    
Switches the builder's proxyBuilder during the execution of a closure.
This is useful to temporary change the building context to another builder without the need for a contrived setup. It will also take care of restoring the previous proxyBuilder when the execution finishes, even if an exception was thrown from inside the closure. Additionally it will use the closure's result as the value for the node identified by 'name' and assign any attributes that might have been set.

Parameters:
attributes additional properties for the node on the parent builder.
builder the temporary builder to switch to as proxyBuilder.
name the node to build on the 'parent' builder.
closure the closure to be executed under the temporary builder.
Returns:
a node that responds to value of name with the closure's result as its value.
Throws:
java.lang.RuntimeException - any exception the closure might have thrown during execution.
    public Object withBuilder(Map attributesFactoryBuilderSupport builderString nameClosure closure) {
        if (name == null) {
            return null;
        }
        Object result = getProxyBuilder().withBuilder(builderclosure);
        return getProxyBuilder().invokeMethod(namenew Object[]{attributesresult});
    }
    public void addDisposalClosure(Closure closure) {
        .add(closure);
    }
    public void dispose() {
        for (int i = .size() - 1; i >= 0; i--) {
            .get(i).call();
        }
    }
    public FactoryInterceptorMetaClass(MetaClass delegateFactoryBuilderSupport factory) {
        super(delegate);
        this. = factory;
    }
    public Object invokeMethod(Object objectString methodNameObject arguments) {
        try {
            return .invokeMethod(objectmethodNamearguments);
        } catch (MissingMethodException mme) {
            // attempt factory resolution
            try {
                if (.getMetaClass().respondsTo(methodName).isEmpty()) {
                    // dispatch to factories if it is not a literal method
                    return .invokeMethod(methodNamearguments);
                } else {
                    return InvokerHelper.invokeMethod(methodNamearguments);
                }
            } catch (MissingMethodException mme2) {
                // chain secondary exception
                Throwable root = mme;
                while (root.getCause() != null) {
                    root = root.getCause();
                }
                root.initCause(mme2);
                // throw original
                throw mme;
            }
        }
    }
    public Object invokeMethod(Object objectString methodNameObject[] arguments) {
        try {
            return .invokeMethod(objectmethodNamearguments);
        } catch (MissingMethodException mme) {
            // attempt factory resolution
            try {
                if (.getMetaClass().respondsTo(methodName).isEmpty()) {
                    // dispatch to factories if it is not a literal method
                    return .invokeMethod(methodNamearguments);
                } else {
                    return InvokerHelper.invokeMethod(methodNamearguments);
                }
            } catch (MissingMethodException mme2) {
                // chain secondary exception
                Throwable root = mme;
                while (root.getCause() != null) {
                    root = root.getCause();
                }
                root.initCause(mme2);
                // throw original
                throw mme;
            }
        }
    }