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 java.io.IOException;
49 import java.io.Writer;
50 import java.lang.reflect.InvocationTargetException;
51 import java.lang.reflect.Method;
52 import java.security.AccessController;
53 import java.security.PrivilegedAction;
54
55 import org.codehaus.groovy.runtime.InvokerHelper;
56
57 /***
58 * Represents any closure object in Groovy.
59 *
60 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
61 * @author <a href="mailto:tug@wilson.co.uk">John Wilson</a>
62 * @version $Revision: 1.39 $
63 */
64 public abstract class Closure extends GroovyObjectSupport implements Cloneable, Runnable {
65
66 private static final Object noParameters[] = new Object[] { null };
67 private static final Object emptyArray[] = new Object[0];
68 private static final Object emptyArrayParameter[] = new Object[] { emptyArray };
69
70 private Object delegate;
71 private final Object owner;
72 private final Method doCallMethod;
73 private final boolean supportsVarargs;
74 private final Class parameterTypes[];
75 private final int numberOfParameters;
76 private Object curriedParams[] = emptyArray;
77
78
79 private int directive = 0;
80 public static int DONE = 1;
81 public static int SKIP = 2;
82
83 public Closure(Object delegate) {
84 this.delegate = delegate;
85 this.owner = delegate;
86
87 Class closureClass = this.getClass();
88
89 while (true) {
90 final Method methods[] = closureClass.getDeclaredMethods();
91
92 int i = 0;
93
94 while (!methods[i].getName().equals("doCall") && ++i != methods.length);
95
96 if (i < methods.length) {
97 this.doCallMethod = methods[i];
98 break;
99 }
100
101 closureClass = closureClass.getSuperclass();
102 }
103
104 AccessController.doPrivileged(new PrivilegedAction() {
105 public Object run() {
106 Closure.this.doCallMethod.setAccessible(true);
107 return null;
108 }
109 });
110
111 this.parameterTypes = this.doCallMethod.getParameterTypes();
112
113 this.numberOfParameters = this.parameterTypes.length;
114
115 if (this.numberOfParameters != 0) {
116 this.supportsVarargs = this.parameterTypes[this.numberOfParameters - 1].equals(Object[].class);
117 } else {
118 this.supportsVarargs = false;
119 }
120 }
121
122 public Object invokeMethod(String method, Object arguments) {
123 if ("doCall".equals(method) || "call".equals(method)) {
124 return call(arguments);
125 } else if ("curry".equals(method)) {
126 return curry((Object [])arguments);
127 } else {
128 try {
129 return getMetaClass().invokeMethod(this, method, arguments);
130 } catch (MissingMethodException e) {
131 if (owner != this) {
132 try {
133
134 return InvokerHelper.invokeMethod(this.owner, method, arguments);
135 } catch (GroovyRuntimeException e1) {
136 if (this.delegate != null && this.delegate != this && this.delegate != this.owner) {
137 try {
138
139 return InvokerHelper.invokeMethod(this.delegate, method, arguments);
140 } catch (GroovyRuntimeException e2) {
141
142 }
143 }
144 }
145 }
146 throw e;
147 }
148 }
149
150 }
151
152 public Object getProperty(String property) {
153 if ("delegate".equals(property)) {
154 return getDelegate();
155 } else if ("owner".equals(property)) {
156 return getOwner();
157 } else if ("method".equals(property)) {
158 return getMethod();
159 } else if ("parameterTypes".equals(property)) {
160 return getParameterTypes();
161 } else if ("metaClass".equals(property)) {
162 return getMetaClass();
163 } else if ("class".equals(property)) {
164 return getClass();
165 } else {
166 try {
167
168 return InvokerHelper.getProperty(this.owner, property);
169 } catch (GroovyRuntimeException e1) {
170 if (this.delegate != null && this.delegate != this && this.delegate != this.owner) {
171 try {
172
173 return InvokerHelper.getProperty(this.delegate, property);
174 } catch (GroovyRuntimeException e2) {
175
176 }
177 }
178
179 throw e1;
180 }
181 }
182 }
183
184 public void setProperty(String property, Object newValue) {
185 if ("delegate".equals(property)) {
186 setDelegate(newValue);
187 } else if ("metaClass".equals(property)) {
188 setMetaClass((MetaClass)newValue);
189 } else {
190 try {
191
192 InvokerHelper.setProperty(this.owner, property, newValue);
193 return;
194 } catch (GroovyRuntimeException e1) {
195 if (this.delegate != null && this.delegate != this && this.delegate != this.owner) {
196 try {
197
198 InvokerHelper.setProperty(this.delegate, property, newValue);
199 return;
200 } catch (GroovyRuntimeException e2) {
201
202 }
203 }
204
205 throw e1;
206 }
207 }
208 }
209
210 /***
211 * Invokes the closure without any parameters, returning any value if applicable.
212 *
213 * @return the value if applicable or null if there is no return statement in the closure
214 */
215 public Object call() {
216 return call(emptyArray);
217 }
218
219 /***
220 * Invokes the closure, returning any value if applicable.
221 *
222 * @param arguments could be a single value or a List of values
223 * @return the value if applicable or null if there is no return statement in the closure
224 */
225 public Object call(final Object arguments) {
226 final Object params[];
227
228 if (this.curriedParams.length != 0) {
229 final Object args[];
230
231 if (arguments instanceof Object[]) {
232 args = (Object[])arguments;
233 } else {
234 args = new Object[] {arguments};
235 }
236
237 params = new Object[this.curriedParams.length + args.length];
238
239 System.arraycopy(this.curriedParams, 0, params, 0, this.curriedParams.length);
240 System.arraycopy(args, 0, params, this.curriedParams.length, args.length);
241 } else {
242 if (arguments instanceof Object[]) {
243 params = (Object[])arguments;
244 } else {
245 return doCall(arguments);
246 }
247 }
248
249 final int lastParam = this.numberOfParameters - 1;
250
251 if (this.supportsVarargs && !(this.numberOfParameters == params.length && (params[lastParam] == null || params[lastParam].getClass() == Object [].class))) {
252 final Object actualParameters[] = new Object[this.numberOfParameters];
253
254
255
256
257
258 if (params.length < lastParam) {
259
260
261
262
263
264
265 throw new IncorrectClosureArgumentsException(this, params, this.parameterTypes);
266 } else {
267 final Object rest[] = new Object[params.length - lastParam];
268
269
270 System.arraycopy(params, 0, actualParameters, 0, lastParam);
271
272
273 System.arraycopy(params, lastParam, rest, 0, rest.length);
274
275
276 actualParameters[lastParam] = rest;
277
278 return callViaReflection(actualParameters);
279 }
280 }
281
282 if (params.length == 0) {
283
284 return doCall(null);
285 } else if (params.length == 1) {
286 return doCall(params[0]);
287 } else if (params.length == 2) {
288 return doCall(params[0], params[1]);
289 } else {
290 return callViaReflection(params);
291 }
292 }
293
294 protected static Object throwRuntimeException(Throwable throwable) {
295 if (throwable instanceof RuntimeException) {
296 throw (RuntimeException) throwable;
297 } else {
298 throw new GroovyRuntimeException(throwable.getMessage(), throwable);
299 }
300 }
301
302 /***
303 * An attempt to optimise calling closures with one parameter
304 * If the closure has one untyped parameter then it will overload this function
305 * If not this will be called ans will use reflection to deal with the case of a
306 * single typed parameter
307 * @param p1
308 * @return the result of calling the closure
309 */
310 protected Object doCall(final Object p1) {
311 return callViaReflection(new Object[] {p1});
312 }
313
314 /***
315 * An attempt to optimise calling closures with two parameters
316 * If the closure has two untyped parameters then it will overload this function
317 * If not this will be called ans will use reflection to deal with the case of one
318 * or two typed parameters
319 * @param p1
320 * @return the result of calling the closure
321 */
322
323 protected Object doCall(final Object p1, final Object p2) {
324 return callViaReflection(new Object[] {p1, p2});
325 }
326
327 private Object callViaReflection(final Object params[]) {
328 try {
329
330 return this.doCallMethod.invoke(this, params);
331 } catch (final IllegalArgumentException e) {
332 throw new IncorrectClosureArgumentsException(this, params, this.parameterTypes);
333 } catch (final IllegalAccessException e) {
334 final Throwable cause = e.getCause();
335
336 return throwRuntimeException((cause == null) ? e : cause);
337 } catch (final InvocationTargetException e) {
338 final Throwable cause = e.getCause();
339
340 return throwRuntimeException((cause == null) ? e : cause);
341 }
342 }
343
344 /***
345 * Used when a closure wraps a method on a class
346 *
347 * @return empty string
348 */
349 public String getMethod() {
350 return "";
351 }
352
353 /***
354 * @return the owner Object to which method calls will go which is
355 * typically the outer class when the closure is constructed
356 */
357 public Object getOwner() {
358 return this.owner;
359 }
360
361 /***
362 * @return the delegate Object to which method calls will go which is
363 * typically the outer class when the closure is constructed
364 */
365 public Object getDelegate() {
366 return this.delegate;
367 }
368
369 /***
370 * Allows the delegate to be changed such as when performing markup building
371 * @param delegate
372 */
373 public void setDelegate(Object delegate) {
374 this.delegate = delegate;
375 }
376
377 /***
378 * @return the parameter types of this closure
379 */
380 public Class[] getParameterTypes() {
381 return this.parameterTypes;
382 }
383
384 /***
385 * @return a version of this closure which implements Writable
386 */
387 public Closure asWritable() {
388 return new WritableClosure();
389 }
390
391
392
393
394 public void run() {
395 call();
396 }
397
398 /***
399 * Support for closure currying
400 * @param arguments
401 */
402 public Closure curry(final Object arguments[]) {
403 final Closure curriedClosure= (Closure)this.clone();
404 final Object newCurriedParams[] = new Object[curriedClosure.curriedParams.length + arguments.length];
405
406 System.arraycopy(curriedClosure.curriedParams, 0, newCurriedParams, 0, curriedClosure.curriedParams.length);
407 System.arraycopy(arguments, 0, newCurriedParams, curriedClosure.curriedParams.length, arguments.length);
408
409 curriedClosure.curriedParams = newCurriedParams;
410
411 return curriedClosure;
412 }
413
414
415
416
417 public Object clone() {
418 try {
419 return super.clone();
420 } catch (final CloneNotSupportedException e) {
421 return null;
422 }
423 }
424
425 private class WritableClosure extends Closure implements Writable {
426 public WritableClosure() {
427 super(null);
428 }
429
430
431
432
433 public Writer writeTo(Writer out) throws IOException {
434 Closure.this.call(out);
435
436 return out;
437 }
438
439
440
441
442 public Object invokeMethod(String method, Object arguments) {
443 if ("clone".equals(method)) {
444 return clone();
445 } else if ("curry".equals(method)) {
446 return curry((Object[])arguments);
447 } else if ("asWritable".equals(method)) {
448 return asWritable();
449 } else {
450 return Closure.this.invokeMethod(method, arguments);
451 }
452 }
453
454
455
456
457 public Object getProperty(String property) {
458 return Closure.this.getProperty(property);
459 }
460
461
462
463
464 public void setProperty(String property, Object newValue) {
465 Closure.this.setProperty(property, newValue);
466 }
467
468
469
470
471 public Object call() {
472 return Closure.this.call();
473 }
474
475
476
477
478 public Object call(Object arguments) {
479 return Closure.this.call(arguments);
480 }
481
482
483
484
485 protected Object doCall(Object p1) {
486 return Closure.this.doCall(p1);
487 }
488
489
490
491
492 protected Object doCall(Object p1, Object p2) {
493 return Closure.this.doCall(p1, p2);
494 }
495
496
497
498
499 public Object getDelegate() {
500 return Closure.this.getDelegate();
501 }
502
503
504
505
506 public void setDelegate(Object delegate) {
507 Closure.this.setDelegate(delegate);
508 }
509
510
511
512
513 public Class[] getParameterTypes() {
514 return Closure.this.getParameterTypes();
515 }
516
517
518
519
520 public Closure asWritable() {
521 return this;
522 }
523
524
525
526
527 public void run() {
528 Closure.this.run();
529 }
530
531
532
533
534 public Closure curry(Object[] arguments) {
535 return Closure.this.curry(arguments).asWritable();
536 }
537
538
539
540
541 public Object clone() {
542 return ((Closure)Closure.this.clone()).asWritable();
543 }
544
545
546
547
548 public int hashCode() {
549 return Closure.this.hashCode();
550 }
551
552
553
554
555 public boolean equals(Object arg0) {
556 return Closure.this.equals(arg0);
557 }
558
559
560
561
562 public String toString() {
563 return Closure.this.toString();
564 }
565 }
566 /***
567 * @return Returns the directive.
568 */
569 public int getDirective() {
570 return directive;
571 }
572 /***
573 * @param directive The directive to set.
574 */
575 public void setDirective(int directive) {
576 this.directive = directive;
577 }
578 }