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

org.picocontainer.script.groovy.GroovyNodeBuilder Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (C) PicoContainer Organization. All rights reserved.
 * ---------------------------------------------------------------------------
 * The software in this package is published under the terms of the BSD style
 * license a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 ******************************************************************************/
package org.picocontainer.script.groovy;

import groovy.lang.Closure;
import groovy.lang.GroovyObject;
import groovy.util.BuilderSupport;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.codehaus.groovy.runtime.InvokerHelper;
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.classname.ClassLoadingPicoContainer;
import org.picocontainer.classname.DefaultClassLoadingPicoContainer;
import org.picocontainer.script.NodeBuilderDecorator;
import org.picocontainer.script.NullNodeBuilderDecorator;
import org.picocontainer.script.ScriptedPicoContainerMarkupException;
import org.picocontainer.script.groovy.nodes.AppendContainerNode;
import org.picocontainer.script.groovy.nodes.BeanNode;
import org.picocontainer.script.groovy.nodes.ChildContainerNode;
import org.picocontainer.script.groovy.nodes.ClassLoaderNode;
import org.picocontainer.script.groovy.nodes.ClasspathNode;
import org.picocontainer.script.groovy.nodes.ComponentNode;
import org.picocontainer.script.groovy.nodes.ConfigNode;
import org.picocontainer.script.groovy.nodes.DoCallNode;
import org.picocontainer.script.groovy.nodes.GrantNode;
import org.picocontainer.script.groovy.nodes.NewBuilderNode;

/**
 * Builds node trees of PicoContainers and Pico components using GroovyMarkup.
 * 

* Simple example usage in your groovy script: * *

 * builder = new org.picocontainer.script.groovy.GroovyNodeBuilder()
 * pico = builder.container(parent:parent) {
 *   component(class:org.picocontainer.script.testmodel.DefaultWebServerConfig)
 *   component(class:org.picocontainer.script.testmodel.WebServerImpl)
 * }
 * 
* *

*

Extending/Enhancing GroovyNodeBuilder

*

* Often-times people need there own assembly commands that are needed for * extending/enhancing the node builder tree. For example, a typical extension * may be to provide AOP vocabulary for the node builder with terms such as * 'aspect', 'pointcut', etc. *

*

* GroovyNodeBuilder provides two primary ways of enhancing the nodes supported * by the groovy builder: * {@link org.picocontainer.script.NodeBuilderDecorator NodeBuilderDecorator} * and special node handlers {@link BuilderNode BuilderNode}. Using * NodeBuilderDecorator is often a preferred method because it is ultimately * script independent. However, replacing an existing GroovyNodeBuilder's * behavior is currently the only way to replace the behavior of an existing * groovy node handler. *

* * @author James Strachan * @author Paul Hammant * @author Aslak Hellesøy * @author Michael Rimov * @author Mauro Talevi */ @SuppressWarnings("unchecked") public class GroovyNodeBuilder extends BuilderSupport { private static final String CLASS = "class"; private static final String PARENT = "parent"; /** * Flag indicating that the attribute validation should be performed. */ public static final boolean PERFORM_ATTRIBUTE_VALIDATION = true; /** * Flag indicating that attribute validation should be skipped. */ public static final boolean SKIP_ATTRIBUTE_VALIDATION = false; /** * Decoration delegate. The traditional method of adding functionality to * the Groovy builder. */ private final NodeBuilderDecorator decorator; /** * Map of node handlers. */ private final Map nodeBuilderHandlers = new HashMap(); private final Map nodeBuilders = new HashMap(); private final boolean performAttributeValidation; /** * Allows the composition of a {@link NodeBuilderDecorator} -- an * object that extends the capabilities of the GroovyNodeBuilder * with new tags, new capabilities, etc. * * @param decorator NodeBuilderDecorator * @param performAttributeValidation should be set to * PERFORM_ATTRIBUTE_VALIDATION or SKIP_ATTRIBUTE_VALIDATION */ public GroovyNodeBuilder(NodeBuilderDecorator decorator, boolean performAttributeValidation) { this.decorator = decorator; this.performAttributeValidation = performAttributeValidation; // Build and register node handlers. this.setNode(new ComponentNode(decorator)).setNode(new ChildContainerNode(decorator)).setNode(new BeanNode()) .setNode(new ConfigNode()).setNode(new ClasspathNode()).setNode(new DoCallNode()).setNode( new NewBuilderNode()).setNode(new ClassLoaderNode()).setNode(new GrantNode()).setNode( new AppendContainerNode()); } public GroovyNodeBuilder(NodeBuilderDecorator decorator) { this(decorator, SKIP_ATTRIBUTE_VALIDATION); } /** * Default constructor. */ public GroovyNodeBuilder() { this(new NullNodeBuilderDecorator(), SKIP_ATTRIBUTE_VALIDATION); } protected void setParent(Object parent, Object child) { } protected Object doInvokeMethod(String s, Object name, Object args) { // TODO use setDelegate() from Groovy JSR Object answer = super.doInvokeMethod(s, name, args); List list = InvokerHelper.asList(args); if (!list.isEmpty()) { Object o = list.get(list.size() - 1); if (o instanceof Closure) { Closure closure = (Closure) o; closure.setDelegate(answer); } } return answer; } protected Object createNode(Object name) { return createNode(name, Collections.EMPTY_MAP); } protected Object createNode(Object name, Object value) { Map attributes = new HashMap(); attributes.put(CLASS, value); return createNode(name, attributes); } /** * Override of create node. Called by BuilderSupport. It examines the * current state of the builder and the given parameters and dispatches the * code to one of the create private functions in this object. * * @param name The name of the groovy node we're building. Examples are * 'container', and 'grant', * @param attributes Map attributes of the current invocation. * @param value A closure passed into the node. Currently unused. * @return Object the created object. */ protected Object createNode(Object name, Map attributes, Object value) { Object current = getCurrent(); if (current != null && current instanceof GroovyObject) { GroovyObject groovyObject = (GroovyObject) current; return groovyObject.invokeMethod(name.toString(), attributes); } else if (current == null) { current = extractOrCreateValidRootPicoContainer(attributes); } else { if (attributes.containsKey(PARENT)) { throw new ScriptedPicoContainerMarkupException( "You can't explicitly specify a parent in a child element."); } } if (name.equals("registerBuilder")) { return registerBuilder(attributes); } else { return handleNode(name, attributes, current); } } private Object registerBuilder(Map attributes) { String builderName = (String) attributes.remove("name"); Object clazz = attributes.remove("class"); try { if (clazz instanceof String) { clazz = this.getClass().getClassLoader().loadClass((String) clazz); } } catch (ClassNotFoundException e) { throw new ScriptedPicoContainerMarkupException("ClassNotFoundException " + clazz); } nodeBuilders.put(builderName, clazz); return clazz; } private Object handleNode(Object name, Map attributes, Object current) { attributes = new HashMap(attributes); BuilderNode nodeHandler = this.getNode(name.toString()); if (nodeHandler == null) { Class builderClass = (Class) nodeBuilders.get(name); if (builderClass != null) { nodeHandler = this.getNode("newBuilder"); attributes.put("class", builderClass); } } if (nodeHandler == null) { // we don't know how to handle it - delegate to the decorator. return getDecorator().createNode(name, attributes, current); } else { // We found a handler. if (performAttributeValidation) { // Validate nodeHandler.validateScriptedAttributes(attributes); } return nodeHandler.createNewNode(current, attributes); } } /** * Pulls the scripted container from the 'current' method or possibly * creates a new blank one if needed. * * @param attributes Map the attributes of the current node. * @return ScriptedPicoContainer, never null. * @throws ScriptedPicoContainerMarkupException */ private ClassLoadingPicoContainer extractOrCreateValidRootPicoContainer(final Map attributes) throws ScriptedPicoContainerMarkupException { Object parentAttribute = attributes.get(PARENT); // // NanoPicoContainer implements MutablePicoCotainer AND PicoContainer // So we want to check for PicoContainer first. // if (parentAttribute instanceof ClassLoadingPicoContainer) { // we're not in an enclosing scope - look at parent attribute // instead return (ClassLoadingPicoContainer) parentAttribute; } if (parentAttribute instanceof MutablePicoContainer) { // we're not in an enclosing scope - look at parent attribute // instead return new DefaultClassLoadingPicoContainer((MutablePicoContainer) parentAttribute); } return null; } /** * Returns the current decorator * * @return A NodeBuilderDecorator, should never be null. */ public NodeBuilderDecorator getDecorator() { return this.decorator; } /** * Returns an appropriate node handler for a given node and * * @param tagName String * @return BuilderNode the appropriate node builder for the given tag name, * or null if no handler exists. (In which case, the Delegate * receives the createChildContainer() call) */ public synchronized BuilderNode getNode(final String tagName) { Object o = nodeBuilderHandlers.get(tagName); return (BuilderNode) o; } /** * Add's a groovy node handler to the table of possible handlers. If a node * handler with the same node name already exists in the map of handlers, * then the GroovyNode replaces the existing node handler. * * @param newGroovyNode CustomGroovyNode * @return GroovyNodeBuilder to allow for method chaining. */ public synchronized GroovyNodeBuilder setNode(final BuilderNode newGroovyNode) { nodeBuilderHandlers.put(newGroovyNode.getNodeName(), newGroovyNode); return this; } protected Object createNode(Object name, Map attributes) { return createNode(name, attributes, null); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy