1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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: 1.10 $
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 public Object invokeMethod(String methodName, Object args) {
85 Object name = getName(methodName);
86 return doInvokeMethod(methodName, name, args);
87 }
88
89 protected Object doInvokeMethod(String methodName, Object name, Object args) {
90 Object node = null;
91 Closure closure = null;
92 List list = InvokerHelper.asList(args);
93
94
95
96 switch (list.size()) {
97 case 0:
98 node = proxyBuilder.createNode(name);
99 break;
100 case 1:
101 {
102 Object object = list.get(0);
103 if (object instanceof Map) {
104 node = proxyBuilder.createNode(name, (Map) object);
105 } else if (object instanceof Closure) {
106 closure = (Closure) object;
107 node = proxyBuilder.createNode(name);
108 } else {
109 node = proxyBuilder.createNode(name, object);
110 }
111 }
112 break;
113 case 2:
114 {
115 Object object1 = list.get(0);
116 Object object2 = list.get(1);
117 if (object1 instanceof Map) {
118 if (object2 instanceof Closure) {
119 closure = (Closure) object2;
120 node = proxyBuilder.createNode(name, (Map) object1);
121 } else {
122 node = proxyBuilder.createNode(name, (Map) object1, object2);
123 }
124 } else {
125 if (object2 instanceof Closure) {
126 closure = (Closure) object2;
127 node = proxyBuilder.createNode(name, object1);
128 } else if (object2 instanceof Map) {
129 node = proxyBuilder.createNode(name, (Map) object2, object1);
130 } else {
131 throw new MissingMethodException(name.toString(), getClass(), list.toArray());
132 }
133 }
134 }
135 break;
136 case 3:
137 {
138 Object arg0 = list.get(0);
139 Object arg1 = list.get(1);
140 Object arg2 = list.get(2);
141 if (arg0 instanceof Map && arg2 instanceof Closure) {
142 closure = (Closure) arg2;
143 node = proxyBuilder.createNode(name, (Map) arg0, arg1);
144 } else if (arg1 instanceof Map && arg2 instanceof Closure) {
145 closure = (Closure) arg2;
146 node = proxyBuilder.createNode(name, (Map) arg1, arg0);
147 } else {
148 throw new MissingMethodException(name.toString(), getClass(), list.toArray());
149 }
150 }
151 break;
152 default:
153 {
154 throw new MissingMethodException(name.toString(), getClass(), list.toArray());
155 }
156
157 }
158
159 if (current != null) {
160 proxyBuilder.setParent(current, node);
161 }
162
163 if (closure != null) {
164
165 Object oldCurrent = current;
166 current = node;
167
168
169 setClosureDelegate(closure, node);
170 closure.call();
171
172 current = oldCurrent;
173 }
174
175 proxyBuilder.nodeCompleted(current, node);
176 return node;
177 }
178
179 /***
180 * A strategy method to allow derived builders to use
181 * builder-trees and switch in different kinds of builders.
182 * This method should call the setDelegate() method on the closure
183 * which by default passes in this but if node is-a builder
184 * we could pass that in instead (or do something wacky too)
185 *
186 * @param closure the closure on which to call setDelegate()
187 * @param node the node value that we've just created, which could be
188 * a builder
189 */
190 protected void setClosureDelegate(Closure closure, Object node) {
191 closure.setDelegate(this);
192 }
193
194 protected abstract void setParent(Object parent, Object child);
195 protected abstract Object createNode(Object name);
196 protected abstract Object createNode(Object name, Object value);
197 protected abstract Object createNode(Object name, Map attributes);
198 protected abstract Object createNode(Object name, Map attributes, Object value);
199
200 /***
201 * A hook to allow names to be converted into some other object
202 * such as a QName in XML or ObjectName in JMX
203 * @param methodName
204 * @return
205 */
206 protected Object getName(String methodName) {
207 if (nameMappingClosure != null) {
208 return nameMappingClosure.call(methodName);
209 }
210 return methodName;
211 }
212
213
214 /***
215 * A hook to allow nodes to be processed once they have had all of their
216 * children applied
217 */
218 protected void nodeCompleted(Object parent, Object node) {
219 }
220
221 protected Object getCurrent() {
222 return current;
223 }
224
225 protected void setCurrent(Object current) {
226 this.current = current;
227 }
228 }