KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > nanocontainer > script > groovy > GroovyNodeBuilder


1 /*****************************************************************************
2  * Copyright (C) NanoContainer Organization. All rights reserved. *
3  * ------------------------------------------------------------------------- *
4  * The software in this package is published under the terms of the BSD *
5  * style license a copy of which has been included with this distribution in *
6  * the LICENSE.txt file. *
7  * *
8  * Original code by James Strachan *
9  *****************************************************************************/

10 package org.nanocontainer.script.groovy;
11
12 import groovy.lang.Closure;
13 import groovy.lang.GroovyObject;
14 import groovy.util.BuilderSupport;
15 import org.codehaus.groovy.runtime.InvokerHelper;
16 import org.nanocontainer.DefaultNanoContainer;
17 import org.nanocontainer.NanoContainer;
18 import org.nanocontainer.script.NanoContainerMarkupException;
19 import org.nanocontainer.script.NodeBuilderDecorationDelegate;
20 import org.nanocontainer.script.NullNodeBuilderDecorationDelegate;
21 import org.nanocontainer.script.groovy.buildernodes.*;
22 import org.picocontainer.MutablePicoContainer;
23
24 import java.util.Collections JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28
29 /**
30  * Builds node trees of PicoContainers and Pico components using GroovyMarkup.
31  * <p>Simple example usage in your groovy script:
32  * <code><pre>
33  * builder = new org.nanocontainer.script.groovy.GroovyNodeBuilder()
34  * pico = builder.container(parent:parent) {
35  * &nbsp;&nbsp;component(class:org.nanocontainer.testmodel.DefaultWebServerConfig)
36  * &nbsp;&nbsp;component(class:org.nanocontainer.testmodel.WebServerImpl)
37  * }
38  * </pre></code>
39  * </p>
40  * <h4>Extending/Enhancing GroovyNodeBuilder</h4>
41  * <p>Often-times people need there own assembly commands that are needed
42  * for extending/enhancing the node builder tree. The perfect example of this
43  * is <tt>DynaopGroovyNodeBuilder</tt> which provides a new vocabulary for
44  * the groovy node builder with terms such as 'aspect', 'pointcut', etc.</p>
45  * <p>GroovyNodeBuilder provides two primary ways of enhancing the nodes supported
46  * by the groovy builder: {@link org.nanocontainer.script.NodeBuilderDecorationDelegate}
47  * and special node handlers {@link BuilderNode}.
48  * Using NodeBuilderDecorationDelegate is often a preferred method because it is
49  * ultimately script independent. However, replacing an existing GroovyNodeBuilder's
50  * behavior is currently the only way to replace the behavior of an existing
51  * groovy node handler.
52  * </p>
53  *
54  * @author James Strachan
55  * @author Paul Hammant
56  * @author Aslak Helles&oslash;y
57  * @author Michael Rimov
58  * @author Mauro Talevi
59  * @version $Revision: 2695 $
60  */

61 public class GroovyNodeBuilder extends BuilderSupport {
62
63     private static final String JavaDoc CLASS = "class";
64
65     private static final String JavaDoc PARENT = "parent";
66
67
68     /**
69      * Flag indicating that the attribute validation should be performed.
70      */

71     public static boolean PERFORM_ATTRIBUTE_VALIDATION = true;
72
73
74     /**
75      * Flag indicating that attribute validation should be skipped.
76      */

77     public static boolean SKIP_ATTRIBUTE_VALIDATION = false;
78
79
80     /**
81      * Decoration delegate. The traditional method of adding functionality to
82      * the Groovy builder.
83      */

84     private final NodeBuilderDecorationDelegate decorationDelegate;
85
86     /**
87      * Map of node handlers.
88      */

89     private Map JavaDoc nodeBuilderHandlers = new HashMap JavaDoc();
90     private Map JavaDoc nodeBuilders = new HashMap JavaDoc();
91
92     private final boolean performAttributeValidation;
93
94
95     /**
96      * Allows the composition of a <tt>{@link NodeBuilderDecorationDelegate}</tt> -- an
97      * object that extends the capabilities of the <tt>GroovyNodeBuilder</tt>
98      * with new tags, new capabilities, etc.
99      *
100      * @param decorationDelegate NodeBuilderDecorationDelegate
101      * @param performAttributeValidation should be set to PERFORM_ATTRIBUTE_VALIDATION
102      * or SKIP_ATTRIBUTE_VALIDATION
103      * @see org.nanocontainer.aop.defaults.AopNodeBuilderDecorationDelegate
104      */

105     public GroovyNodeBuilder(NodeBuilderDecorationDelegate decorationDelegate, boolean performAttributeValidation) {
106         this.decorationDelegate = decorationDelegate;
107         this.performAttributeValidation = performAttributeValidation;
108
109         //Build and register node handlers.
110
this.setNode(new ComponentNode(decorationDelegate))
111                 .setNode(new ChildContainerNode(decorationDelegate))
112                 .setNode(new BeanNode())
113                 .setNode(new ClasspathNode())
114                 .setNode(new DoCallNode())
115                 .setNode(new NewBuilderNode())
116                 .setNode(new ClassLoaderNode())
117                 .setNode(new DecoratingPicoContainerNode())
118                 .setNode(new GrantNode())
119                 .setNode(new AppendContainerNode());
120         NanoContainer factory = new DefaultNanoContainer();
121         try {
122             factory.registerComponentImplementation("wc", "org.nanocontainer.webcontainer.groovy.WebContainerBuilder");
123             setNode((BuilderNode) factory.getPico().getComponentInstance("wc"));
124         } catch (ClassNotFoundException JavaDoc cnfe) {
125         }
126
127     }
128
129     /**
130      * Default constructor.
131      */

132     public GroovyNodeBuilder() {
133         this(new NullNodeBuilderDecorationDelegate(), SKIP_ATTRIBUTE_VALIDATION);
134     }
135
136
137     protected void setParent(Object JavaDoc parent, Object JavaDoc child) {
138     }
139
140     protected Object JavaDoc doInvokeMethod(String JavaDoc s, Object JavaDoc name, Object JavaDoc args) {
141         //TODO use setDelegate() from Groovy JSR
142
Object JavaDoc answer = super.doInvokeMethod(s, name, args);
143         List JavaDoc list = InvokerHelper.asList(args);
144         if (!list.isEmpty()) {
145             Object JavaDoc o = list.get(list.size() - 1);
146             if (o instanceof Closure) {
147                 Closure closure = (Closure) o;
148                 closure.setDelegate(answer);
149             }
150         }
151         return answer;
152     }
153
154     protected Object JavaDoc createNode(Object JavaDoc name) {
155         return createNode(name, Collections.EMPTY_MAP);
156     }
157
158     protected Object JavaDoc createNode(Object JavaDoc name, Object JavaDoc value) {
159         Map JavaDoc attributes = new HashMap JavaDoc();
160         attributes.put(CLASS, value);
161         return createNode(name, attributes);
162     }
163
164     /**
165      * Override of create node. Called by BuilderSupport. It examines the
166      * current state of the builder and the given parameters and dispatches the
167      * code to one of the create private functions in this object.
168      *
169      * @param name The name of the groovy node we're building. Examples are
170      * 'container', and 'grant',
171      * @param attributes Map attributes of the current invocation.
172      * @param value A closure passed into the node. Currently unused.
173      * @return Object the created object.
174      */

175     protected Object JavaDoc createNode(Object JavaDoc name, Map JavaDoc attributes, Object JavaDoc value) {
176         Object JavaDoc current = getCurrent();
177         if (current != null && current instanceof GroovyObject) {
178             GroovyObject groovyObject = (GroovyObject) current;
179             return groovyObject.invokeMethod(name.toString(), attributes);
180         } else if (current == null) {
181             current = extractOrCreateValidRootNanoContainer(attributes);
182         } else {
183             if (attributes.containsKey(PARENT)) {
184                 throw new NanoContainerMarkupException("You can't explicitly specify a parent in a child element.");
185             }
186         }
187         if (name.equals("registerBuilder")) {
188             return registerBuilder(attributes);
189
190         } else {
191             return handleNode(name, attributes, current);
192         }
193
194     }
195
196     private Object JavaDoc registerBuilder(Map JavaDoc attributes) {
197         String JavaDoc builderName = (String JavaDoc) attributes.remove("name");
198         Object JavaDoc clazz = attributes.remove("class");
199         try {
200             if (clazz instanceof String JavaDoc) {
201                 clazz = this.getClass().getClassLoader().loadClass((String JavaDoc) clazz);
202             }
203         } catch (ClassNotFoundException JavaDoc e) {
204             throw new NanoContainerMarkupException("ClassNotFoundException " + clazz);
205         }
206         nodeBuilders.put(builderName, clazz);
207         return clazz;
208     }
209
210     private Object JavaDoc handleNode(Object JavaDoc name, Map JavaDoc attributes, Object JavaDoc current) {
211
212         attributes = new HashMap JavaDoc(attributes);
213
214         BuilderNode nodeHandler = this.getNode(name.toString());
215
216         if (nodeHandler == null) {
217             Class JavaDoc builderClass = (Class JavaDoc) nodeBuilders.get(name);
218             if (builderClass != null) {
219                 nodeHandler = this.getNode("newBuilder");
220                 attributes.put("class",builderClass);
221             }
222         }
223
224         if (nodeHandler == null) {
225             // we don't know how to handle it - delegate to the decorator.
226
return getDecorationDelegate().createNode(name, attributes, current);
227
228         } else {
229             //We found a handler.
230

231             if (performAttributeValidation) {
232                 //Validate
233
nodeHandler.validateScriptedAttributes(attributes);
234             }
235
236             return nodeHandler.createNewNode(current, attributes);
237         }
238     }
239
240     /**
241      * Pulls the nanocontainer from the 'current' method or possibly creates
242      * a new blank one if needed.
243      *
244      * @param attributes Map the attributes of the current node.
245      * @return NanoContainer, never null.
246      * @throws NanoContainerMarkupException
247      */

248     private NanoContainer extractOrCreateValidRootNanoContainer(final Map JavaDoc attributes) throws NanoContainerMarkupException {
249         Object JavaDoc parentAttribute = attributes.get(PARENT);
250         //
251
//NanoPicoContainer implements MutablePicoCotainer AND NanoContainer
252
//So we want to check for NanoContainer first.
253
//
254
if (parentAttribute instanceof NanoContainer) {
255             // we're not in an enclosing scope - look at parent attribute instead
256
return (NanoContainer) parentAttribute;
257         }
258         if (parentAttribute instanceof MutablePicoContainer) {
259             // we're not in an enclosing scope - look at parent attribute instead
260
return new DefaultNanoContainer((MutablePicoContainer) parentAttribute);
261         }
262         return null;
263     }
264
265
266     /**
267      * Retrieve the current decoration delegate.
268      *
269      * @return NodeBuilderDecorationDelegate, should never be null.
270      */

271     public NodeBuilderDecorationDelegate getDecorationDelegate() {
272         return this.decorationDelegate;
273     }
274
275
276     /**
277      * Returns an appropriate node handler for a given node and
278      *
279      * @param tagName String
280      * @return CustomGroovyNode the appropriate node builder for the given
281      * tag name, or null if no handler exists. (In which case, the Delegate
282      * receives the createChildContainer() call)
283      */

284     public synchronized BuilderNode getNode(final String JavaDoc tagName) {
285         Object JavaDoc o = nodeBuilderHandlers.get(tagName);
286         return (BuilderNode) o;
287     }
288
289     /**
290      * Add's a groovy node handler to the table of possible handlers. If a node
291      * handler with the same node name already exists in the map of handlers, then
292      * the <tt>GroovyNode</tt> replaces the existing node handler.
293      *
294      * @param newGroovyNode CustomGroovyNode
295      * @return GroovyNodeBuilder to allow for method chaining.
296      */

297     public synchronized GroovyNodeBuilder setNode(final BuilderNode newGroovyNode) {
298         nodeBuilderHandlers.put(newGroovyNode.getNodeName(), newGroovyNode);
299         return this;
300     }
301
302     protected Object JavaDoc createNode(Object JavaDoc name, Map JavaDoc attributes) {
303         return createNode(name, attributes, null);
304     }
305
306
307 }
308
Popular Tags