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.lang;
47
48 import org.codehaus.groovy.runtime.CurriedClosure;
49 import org.codehaus.groovy.runtime.InvokerHelper;
50 import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
51
52 import java.io.IOException;
53 import java.io.StringWriter;
54 import java.io.Writer;
55 import java.lang.reflect.Method;
56 import java.security.AccessController;
57 import java.security.PrivilegedAction;
58
59 /***
60 * Represents any closure object in Groovy.
61 *
62 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
63 * @author <a href="mailto:tug@wilson.co.uk">John Wilson</a>
64 * @version $Revision: 4546 $
65 */
66 public abstract class Closure extends GroovyObjectSupport implements Cloneable, Runnable {
67
68 private static final Object noParameters[] = new Object[]{null};
69 private static final Object emptyArray[] = new Object[0];
70 private static final Object emptyArrayParameter[] = new Object[]{emptyArray};
71
72 private Object delegate;
73 private final Object owner;
74 private Class[] parameterTypes;
75 protected int maximumNumberOfParameters;
76 private final Object thisObject;
77
78
79 private int directive = 0;
80 public final static int DONE = 1, SKIP = 2;
81
82 public Closure(Object owner, Object thisObject) {
83 this.owner = owner;
84 this.delegate = owner;
85 this.thisObject = thisObject;
86
87 Class closureClass = this.getClass();
88 final Class clazz = closureClass;
89 final Method[] methods = (Method[]) AccessController.doPrivileged(new PrivilegedAction() {
90 public Object run() {
91 return clazz.getDeclaredMethods();
92 }
93 });
94
95
96 maximumNumberOfParameters = -1;
97 for (int j = 0; j < methods.length; j++) {
98 if ("doCall".equals(methods[j].getName()) && methods[j].getParameterTypes().length > maximumNumberOfParameters) {
99 parameterTypes = methods[j].getParameterTypes();
100 maximumNumberOfParameters = parameterTypes.length;
101 }
102 }
103
104 maximumNumberOfParameters = Math.max(maximumNumberOfParameters,0);
105 }
106
107 public Closure(Object owner) {
108 this(owner,null);
109 }
110
111 protected Object getThisObject(){
112 return thisObject;
113 }
114
115 public Object getProperty(String property) {
116 if ("delegate".equals(property)) {
117 return getDelegate();
118 } else if ("owner".equals(property)) {
119 return getOwner();
120 } else if ("getMaximumNumberOfParameters".equals(property)) {
121 return new Integer(getMaximumNumberOfParameters());
122 } else if ("parameterTypes".equals(property)) {
123 return getParameterTypes();
124 } else if ("metaClass".equals(property)) {
125 return getMetaClass();
126 } else if ("class".equals(property)) {
127 return getClass();
128 } else {
129 try {
130
131 return InvokerHelper.getProperty(this.owner, property);
132 } catch (MissingPropertyException e1) {
133 if (this.delegate != null && this.delegate != this && this.delegate != this.owner) {
134 try {
135
136 return InvokerHelper.getProperty(this.delegate, property);
137 } catch (GroovyRuntimeException e2) {
138
139 }
140 }
141
142 throw e1;
143 }
144 }
145 }
146
147 public void setProperty(String property, Object newValue) {
148 if ("delegate".equals(property)) {
149 setDelegate(newValue);
150 } else if ("metaClass".equals(property)) {
151 setMetaClass((MetaClass) newValue);
152 } else {
153 try {
154
155 InvokerHelper.setProperty(this.owner, property, newValue);
156 return;
157 } catch (GroovyRuntimeException e1) {
158 if (this.delegate != null && this.delegate != this && this.delegate != this.owner) {
159 try {
160
161 InvokerHelper.setProperty(this.delegate, property, newValue);
162 return;
163 } catch (GroovyRuntimeException e2) {
164
165 }
166 }
167
168 throw e1;
169 }
170 }
171 }
172
173 public boolean isCase(Object candidate){
174 return DefaultTypeTransformation.castToBoolean(call(candidate));
175 }
176
177 /***
178 * Invokes the closure without any parameters, returning any value if applicable.
179 *
180 * @return the value if applicable or null if there is no return statement in the closure
181 */
182 public Object call() {
183 return call(new Object[]{});
184 }
185
186 public Object call(Object[] args) {
187 try {
188 return getMetaClass().invokeMethod(this,"doCall",args);
189 } catch (Exception e) {
190 return throwRuntimeException(e);
191 }
192 }
193
194 /***
195 * Invokes the closure, returning any value if applicable.
196 *
197 * @param arguments could be a single value or a List of values
198 * @return the value if applicable or null if there is no return statement in the closure
199 */
200 public Object call(final Object arguments) {
201 return call(new Object[]{arguments});
202 }
203
204 protected static Object throwRuntimeException(Throwable throwable) {
205 if (throwable instanceof RuntimeException) {
206 throw (RuntimeException) throwable;
207 } else {
208 throw new GroovyRuntimeException(throwable.getMessage(), throwable);
209 }
210 }
211
212 /***
213 * @return the owner Object to which method calls will go which is
214 * typically the outer class when the closure is constructed
215 */
216 public Object getOwner() {
217 return this.owner;
218 }
219
220 /***
221 * @return the delegate Object to which method calls will go which is
222 * typically the outer class when the closure is constructed
223 */
224 public Object getDelegate() {
225 return this.delegate;
226 }
227
228 /***
229 * Allows the delegate to be changed such as when performing markup building
230 *
231 * @param delegate
232 */
233 public void setDelegate(Object delegate) {
234 this.delegate = delegate;
235 }
236
237 /***
238 * @return the parameter types of the longest doCall method
239 * of this closure
240 */
241 public Class[] getParameterTypes() {
242 return this.parameterTypes;
243 }
244
245 /***
246 * @return the maximum number of parameters a doCall methos
247 * of this closure can take
248 */
249 public int getMaximumNumberOfParameters() {
250 return this.maximumNumberOfParameters;
251 }
252
253 /***
254 * @return a version of this closure which implements Writable
255 */
256 public Closure asWritable() {
257 return new WritableClosure();
258 }
259
260
261
262
263 public void run() {
264 call();
265 }
266
267 /***
268 * Support for closure currying
269 *
270 * @param arguments
271 */
272 public Closure curry(final Object arguments[]) {
273 return new CurriedClosure(this,arguments);
274 }
275
276
277
278
279 public Object clone() {
280 try {
281 return super.clone();
282 } catch (final CloneNotSupportedException e) {
283 return null;
284 }
285 }
286
287 /***
288 * Implementation note:
289 * This has to be an inner class!
290 *
291 * Reason:
292 * Closure.this.call will call the outer call method, bur
293 * with the inner class as executing object. This means any
294 * invokeMethod or getProperty call will be called on this
295 * inner class instead of the outer!
296 */
297 private class WritableClosure extends Closure implements Writable {
298 public WritableClosure() {
299 super(Closure.this);
300 }
301
302
303
304
305 public Writer writeTo(Writer out) throws IOException {
306 Closure.this.call(new Object[]{out});
307
308 return out;
309 }
310
311
312
313
314 public Object invokeMethod(String method, Object arguments) {
315 if ("clone".equals(method)) {
316 return clone();
317 } else if ("curry".equals(method)) {
318 return curry((Object[]) arguments);
319 } else if ("asWritable".equals(method)) {
320 return asWritable();
321 } else {
322 return Closure.this.invokeMethod(method, arguments);
323 }
324 }
325
326
327
328
329 public Object getProperty(String property) {
330 return Closure.this.getProperty(property);
331 }
332
333
334
335
336 public void setProperty(String property, Object newValue) {
337 Closure.this.setProperty(property, newValue);
338 }
339
340
341
342
343 public Object call() {
344 return Closure.this.call();
345 }
346
347
348
349
350 public Object call(Object arguments) {
351 return Closure.this.call(arguments);
352 }
353
354
355
356
357 public Object getDelegate() {
358 return Closure.this.getDelegate();
359 }
360
361
362
363
364 public void setDelegate(Object delegate) {
365 Closure.this.setDelegate(delegate);
366 }
367
368
369
370
371 public Class[] getParameterTypes() {
372 return Closure.this.getParameterTypes();
373 }
374
375
376
377
378 public int getMaximumNumberOfParameters() {
379 return Closure.this.getMaximumNumberOfParameters();
380 }
381
382
383
384
385 public Closure asWritable() {
386 return this;
387 }
388
389
390
391
392 public void run() {
393 Closure.this.run();
394 }
395
396
397
398
399 public Object clone() {
400 return ((Closure) Closure.this.clone()).asWritable();
401 }
402
403
404
405
406 public int hashCode() {
407 return Closure.this.hashCode();
408 }
409
410
411
412
413 public boolean equals(Object arg0) {
414 return Closure.this.equals(arg0);
415 }
416
417
418
419
420 public String toString() {
421 final StringWriter writer = new StringWriter();
422
423 try {
424 writeTo(writer);
425 } catch (IOException e) {
426 return null;
427 }
428
429 return writer.toString();
430 }
431
432 public Closure curry(final Object arguments[]) {
433 return (new CurriedClosure(this,arguments)).asWritable();
434 }
435 }
436
437 /***
438 * @return Returns the directive.
439 */
440 public int getDirective() {
441 return directive;
442 }
443
444 /***
445 * @param directive The directive to set.
446 */
447 public void setDirective(int directive) {
448 this.directive = directive;
449 }
450
451 }