001: /*
002: $Id: Closure.java 4546 2006-12-21 19:07:22Z blackdrag $
003:
004: Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005:
006: Redistribution and use of this software and associated documentation
007: ("Software"), with or without modification, are permitted provided
008: that the following conditions are met:
009:
010: 1. Redistributions of source code must retain copyright
011: statements and notices. Redistributions must also contain a
012: copy of this document.
013:
014: 2. Redistributions in binary form must reproduce the
015: above copyright notice, this list of conditions and the
016: following disclaimer in the documentation and/or other
017: materials provided with the distribution.
018:
019: 3. The name "groovy" must not be used to endorse or promote
020: products derived from this Software without prior written
021: permission of The Codehaus. For written permission,
022: please contact info@codehaus.org.
023:
024: 4. Products derived from this Software may not be called "groovy"
025: nor may "groovy" appear in their names without prior written
026: permission of The Codehaus. "groovy" is a registered
027: trademark of The Codehaus.
028:
029: 5. Due credit should be given to The Codehaus -
030: http://groovy.codehaus.org/
031:
032: THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033: ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034: NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036: THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042: ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043: OF THE POSSIBILITY OF SUCH DAMAGE.
044:
045: */
046: package groovy.lang;
047:
048: import org.codehaus.groovy.runtime.CurriedClosure;
049: import org.codehaus.groovy.runtime.InvokerHelper;
050: import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
051:
052: import java.io.IOException;
053: import java.io.StringWriter;
054: import java.io.Writer;
055: import java.lang.reflect.Method;
056: import java.security.AccessController;
057: import java.security.PrivilegedAction;
058:
059: /**
060: * Represents any closure object in Groovy.
061: *
062: * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
063: * @author <a href="mailto:tug@wilson.co.uk">John Wilson</a>
064: * @version $Revision: 4546 $
065: */
066: public abstract class Closure extends GroovyObjectSupport implements
067: Cloneable, Runnable {
068:
069: private static final Object noParameters[] = new Object[] { null };
070: private static final Object emptyArray[] = new Object[0];
071: private static final Object emptyArrayParameter[] = new Object[] { emptyArray };
072:
073: private Object delegate;
074: private final Object owner;
075: private Class[] parameterTypes;
076: protected int maximumNumberOfParameters;
077: private final Object this Object;
078:
079: private int directive = 0;
080: public final static int DONE = 1, SKIP = 2;
081:
082: public Closure(Object owner, Object this Object) {
083: this .owner = owner;
084: this .delegate = owner;
085: this .this Object = this Object;
086:
087: Class closureClass = this .getClass();
088: final Class clazz = closureClass;
089: final Method[] methods = (Method[]) AccessController
090: .doPrivileged(new PrivilegedAction() {
091: public Object run() {
092: return clazz.getDeclaredMethods();
093: }
094: });
095:
096: // set it to -1 for starters so parameterTypes will always get a type
097: maximumNumberOfParameters = -1;
098: for (int j = 0; j < methods.length; j++) {
099: if ("doCall".equals(methods[j].getName())
100: && methods[j].getParameterTypes().length > maximumNumberOfParameters) {
101: parameterTypes = methods[j].getParameterTypes();
102: maximumNumberOfParameters = parameterTypes.length;
103: }
104: }
105: // this line should be useless, but well, just in case
106: maximumNumberOfParameters = Math.max(maximumNumberOfParameters,
107: 0);
108: }
109:
110: public Closure(Object owner) {
111: this (owner, null);
112: }
113:
114: protected Object getThisObject() {
115: return this Object;
116: }
117:
118: public Object getProperty(String property) {
119: if ("delegate".equals(property)) {
120: return getDelegate();
121: } else if ("owner".equals(property)) {
122: return getOwner();
123: } else if ("getMaximumNumberOfParameters".equals(property)) {
124: return new Integer(getMaximumNumberOfParameters());
125: } else if ("parameterTypes".equals(property)) {
126: return getParameterTypes();
127: } else if ("metaClass".equals(property)) {
128: return getMetaClass();
129: } else if ("class".equals(property)) {
130: return getClass();
131: } else {
132: try {
133: // lets try getting the property on the owner
134: return InvokerHelper.getProperty(this .owner, property);
135: } catch (MissingPropertyException e1) {
136: if (this .delegate != null && this .delegate != this
137: && this .delegate != this .owner) {
138: try {
139: // lets try getting the property on the delegate
140: return InvokerHelper.getProperty(this .delegate,
141: property);
142: } catch (GroovyRuntimeException e2) {
143: // ignore, we'll throw e1
144: }
145: }
146:
147: throw e1;
148: }
149: }
150: }
151:
152: public void setProperty(String property, Object newValue) {
153: if ("delegate".equals(property)) {
154: setDelegate(newValue);
155: } else if ("metaClass".equals(property)) {
156: setMetaClass((MetaClass) newValue);
157: } else {
158: try {
159: // lets try setting the property on the owner
160: InvokerHelper.setProperty(this .owner, property,
161: newValue);
162: return;
163: } catch (GroovyRuntimeException e1) {
164: if (this .delegate != null && this .delegate != this
165: && this .delegate != this .owner) {
166: try {
167: // lets try setting the property on the delegate
168: InvokerHelper.setProperty(this .delegate,
169: property, newValue);
170: return;
171: } catch (GroovyRuntimeException e2) {
172: // ignore, we'll throw e1
173: }
174: }
175:
176: throw e1;
177: }
178: }
179: }
180:
181: public boolean isCase(Object candidate) {
182: return DefaultTypeTransformation.castToBoolean(call(candidate));
183: }
184:
185: /**
186: * Invokes the closure without any parameters, returning any value if applicable.
187: *
188: * @return the value if applicable or null if there is no return statement in the closure
189: */
190: public Object call() {
191: return call(new Object[] {});
192: }
193:
194: public Object call(Object[] args) {
195: try {
196: return getMetaClass().invokeMethod(this , "doCall", args);
197: } catch (Exception e) {
198: return throwRuntimeException(e);
199: }
200: }
201:
202: /**
203: * Invokes the closure, returning any value if applicable.
204: *
205: * @param arguments could be a single value or a List of values
206: * @return the value if applicable or null if there is no return statement in the closure
207: */
208: public Object call(final Object arguments) {
209: return call(new Object[] { arguments });
210: }
211:
212: protected static Object throwRuntimeException(Throwable throwable) {
213: if (throwable instanceof RuntimeException) {
214: throw (RuntimeException) throwable;
215: } else {
216: throw new GroovyRuntimeException(throwable.getMessage(),
217: throwable);
218: }
219: }
220:
221: /**
222: * @return the owner Object to which method calls will go which is
223: * typically the outer class when the closure is constructed
224: */
225: public Object getOwner() {
226: return this .owner;
227: }
228:
229: /**
230: * @return the delegate Object to which method calls will go which is
231: * typically the outer class when the closure is constructed
232: */
233: public Object getDelegate() {
234: return this .delegate;
235: }
236:
237: /**
238: * Allows the delegate to be changed such as when performing markup building
239: *
240: * @param delegate
241: */
242: public void setDelegate(Object delegate) {
243: this .delegate = delegate;
244: }
245:
246: /**
247: * @return the parameter types of the longest doCall method
248: * of this closure
249: */
250: public Class[] getParameterTypes() {
251: return this .parameterTypes;
252: }
253:
254: /**
255: * @return the maximum number of parameters a doCall methos
256: * of this closure can take
257: */
258: public int getMaximumNumberOfParameters() {
259: return this .maximumNumberOfParameters;
260: }
261:
262: /**
263: * @return a version of this closure which implements Writable
264: */
265: public Closure asWritable() {
266: return new WritableClosure();
267: }
268:
269: /* (non-Javadoc)
270: * @see java.lang.Runnable#run()
271: */
272: public void run() {
273: call();
274: }
275:
276: /**
277: * Support for closure currying
278: *
279: * @param arguments
280: */
281: public Closure curry(final Object arguments[]) {
282: return new CurriedClosure(this , arguments);
283: }
284:
285: /* (non-Javadoc)
286: * @see java.lang.Object#clone()
287: */
288: public Object clone() {
289: try {
290: return super .clone();
291: } catch (final CloneNotSupportedException e) {
292: return null;
293: }
294: }
295:
296: /**
297: * Implementation note:
298: * This has to be an inner class!
299: *
300: * Reason:
301: * Closure.this.call will call the outer call method, bur
302: * with the inner class as executing object. This means any
303: * invokeMethod or getProperty call will be called on this
304: * inner class instead of the outer!
305: */
306: private class WritableClosure extends Closure implements Writable {
307: public WritableClosure() {
308: super (Closure.this );
309: }
310:
311: /* (non-Javadoc)
312: * @see groovy.lang.Writable#writeTo(java.io.Writer)
313: */
314: public Writer writeTo(Writer out) throws IOException {
315: Closure.this .call(new Object[] { out });
316:
317: return out;
318: }
319:
320: /* (non-Javadoc)
321: * @see groovy.lang.GroovyObject#invokeMethod(java.lang.String, java.lang.Object)
322: */
323: public Object invokeMethod(String method, Object arguments) {
324: if ("clone".equals(method)) {
325: return clone();
326: } else if ("curry".equals(method)) {
327: return curry((Object[]) arguments);
328: } else if ("asWritable".equals(method)) {
329: return asWritable();
330: } else {
331: return Closure.this .invokeMethod(method, arguments);
332: }
333: }
334:
335: /* (non-Javadoc)
336: * @see groovy.lang.GroovyObject#getProperty(java.lang.String)
337: */
338: public Object getProperty(String property) {
339: return Closure.this .getProperty(property);
340: }
341:
342: /* (non-Javadoc)
343: * @see groovy.lang.GroovyObject#setProperty(java.lang.String, java.lang.Object)
344: */
345: public void setProperty(String property, Object newValue) {
346: Closure.this .setProperty(property, newValue);
347: }
348:
349: /* (non-Javadoc)
350: * @see groovy.lang.Closure#call()
351: */
352: public Object call() {
353: return Closure.this .call();
354: }
355:
356: /* (non-Javadoc)
357: * @see groovy.lang.Closure#call(java.lang.Object)
358: */
359: public Object call(Object arguments) {
360: return Closure.this .call(arguments);
361: }
362:
363: /* (non-Javadoc)
364: * @see groovy.lang.Closure#getDelegate()
365: */
366: public Object getDelegate() {
367: return Closure.this .getDelegate();
368: }
369:
370: /* (non-Javadoc)
371: * @see groovy.lang.Closure#setDelegate(java.lang.Object)
372: */
373: public void setDelegate(Object delegate) {
374: Closure.this .setDelegate(delegate);
375: }
376:
377: /* (non-Javadoc)
378: * @see groovy.lang.Closure#getParameterTypes()
379: */
380: public Class[] getParameterTypes() {
381: return Closure.this .getParameterTypes();
382: }
383:
384: /* (non-Javadoc)
385: * @see groovy.lang.Closure#getParameterTypes()
386: */
387: public int getMaximumNumberOfParameters() {
388: return Closure.this .getMaximumNumberOfParameters();
389: }
390:
391: /* (non-Javadoc)
392: * @see groovy.lang.Closure#asWritable()
393: */
394: public Closure asWritable() {
395: return this ;
396: }
397:
398: /* (non-Javadoc)
399: * @see java.lang.Runnable#run()
400: */
401: public void run() {
402: Closure.this .run();
403: }
404:
405: /* (non-Javadoc)
406: * @see java.lang.Object#clone()
407: */
408: public Object clone() {
409: return ((Closure) Closure.this .clone()).asWritable();
410: }
411:
412: /* (non-Javadoc)
413: * @see java.lang.Object#hashCode()
414: */
415: public int hashCode() {
416: return Closure.this .hashCode();
417: }
418:
419: /* (non-Javadoc)
420: * @see java.lang.Object#equals(java.lang.Object)
421: */
422: public boolean equals(Object arg0) {
423: return Closure.this .equals(arg0);
424: }
425:
426: /* (non-Javadoc)
427: * @see java.lang.Object#toString()
428: */
429: public String toString() {
430: final StringWriter writer = new StringWriter();
431:
432: try {
433: writeTo(writer);
434: } catch (IOException e) {
435: return null;
436: }
437:
438: return writer.toString();
439: }
440:
441: public Closure curry(final Object arguments[]) {
442: return (new CurriedClosure(this , arguments)).asWritable();
443: }
444: }
445:
446: /**
447: * @return Returns the directive.
448: */
449: public int getDirective() {
450: return directive;
451: }
452:
453: /**
454: * @param directive The directive to set.
455: */
456: public void setDirective(int directive) {
457: this.directive = directive;
458: }
459:
460: }
|