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