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