View Javadoc

    1   /*
    2    $Id: Closure.java,v 1.59 2006/06/06 14:33:46 blackdrag Exp $
    3
    4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
    5
    6    Redistribution and use of this software and associated documentation
    7    ("Software"), with or without modification, are permitted provided
    8    that the following conditions are met:
    9
    10   1. Redistributions of source code must retain copyright
    11      statements and notices.  Redistributions must also contain a
    12      copy of this document.
    13
    14   2. Redistributions in binary form must reproduce the
    15      above copyright notice, this list of conditions and the
    16      following disclaimer in the documentation and/or other
    17      materials provided with the distribution.
    18
    19   3. The name "groovy" must not be used to endorse or promote
    20      products derived from this Software without prior written
    21      permission of The Codehaus.  For written permission,
    22      please contact info@codehaus.org.
    23
    24   4. Products derived from this Software may not be called "groovy"
    25      nor may "groovy" appear in their names without prior written
    26      permission of The Codehaus. "groovy" is a registered
    27      trademark of The Codehaus.
    28
    29   5. Due credit should be given to The Codehaus -
    30      http://groovy.codehaus.org/
    31
    32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
    33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
    34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
    36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    43   OF THE POSSIBILITY OF SUCH DAMAGE.
    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                 // lets try getting the property on the owner
    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                         // lets try getting the property on the delegate
    123                         return InvokerHelper.getProperty(this.delegate, property);
    124                     } catch (GroovyRuntimeException e2) {
    125                         // ignore, we'll throw e1
    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                 // lets try setting the property on the owner
    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                         // lets try setting the property on the delegate
    148                         InvokerHelper.setProperty(this.delegate, property, newValue);
    149                         return;
    150                     } catch (GroovyRuntimeException e2) {
    151                         // ignore, we'll throw e1
    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     /* (non-Javadoc)
    248      * @see java.lang.Runnable#run()
    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     /* (non-Javadoc)
    264      * @see java.lang.Object#clone()
    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         /* (non-Javadoc)
    290          * @see groovy.lang.Writable#writeTo(java.io.Writer)
    291          */
    292         public Writer writeTo(Writer out) throws IOException {
    293             Closure.this.call(new Object[]{out});
    294
    295             return out;
    296         }
    297
    298         /* (non-Javadoc)
    299          * @see groovy.lang.GroovyObject#invokeMethod(java.lang.String, java.lang.Object)
    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         /* (non-Javadoc)
    314          * @see groovy.lang.GroovyObject#getProperty(java.lang.String)
    315          */
    316         public Object getProperty(String property) {
    317             return Closure.this.getProperty(property);
    318         }
    319
    320         /* (non-Javadoc)
    321          * @see groovy.lang.GroovyObject#setProperty(java.lang.String, java.lang.Object)
    322          */
    323         public void setProperty(String property, Object newValue) {
    324             Closure.this.setProperty(property, newValue);
    325         }
    326
    327         /* (non-Javadoc)
    328          * @see groovy.lang.Closure#call()
    329          */
    330         public Object call() {
    331             return Closure.this.call();
    332         }
    333
    334         /* (non-Javadoc)
    335          * @see groovy.lang.Closure#call(java.lang.Object)
    336          */
    337         public Object call(Object arguments) {
    338             return Closure.this.call(arguments);
    339         }
    340
    341         /* (non-Javadoc)
    342          * @see groovy.lang.Closure#getDelegate()
    343          */
    344         public Object getDelegate() {
    345             return Closure.this.getDelegate();
    346         }
    347
    348         /* (non-Javadoc)
    349          * @see groovy.lang.Closure#setDelegate(java.lang.Object)
    350          */
    351         public void setDelegate(Object delegate) {
    352             Closure.this.setDelegate(delegate);
    353         }
    354
    355         /* (non-Javadoc)
    356          * @see groovy.lang.Closure#getParameterTypes()
    357          */
    358         public Class[] getParameterTypes() {
    359             return Closure.this.getParameterTypes();
    360         }
    361
    362         /* (non-Javadoc)
    363          * @see groovy.lang.Closure#getParameterTypes()
    364          */
    365         public int getMaximumNumberOfParameters() {
    366             return Closure.this.getMaximumNumberOfParameters();
    367         }
    368
    369         /* (non-Javadoc)
    370          * @see groovy.lang.Closure#asWritable()
    371          */
    372         public Closure asWritable() {
    373             return this;
    374         }
    375
    376         /* (non-Javadoc)
    377          * @see java.lang.Runnable#run()
    378          */
    379         public void run() {
    380             Closure.this.run();
    381         }
    382
    383         /* (non-Javadoc)
    384          * @see java.lang.Object#clone()
    385          */
    386         public Object clone() {
    387             return ((Closure) Closure.this.clone()).asWritable();
    388         }
    389
    390         /* (non-Javadoc)
    391          * @see java.lang.Object#hashCode()
    392          */
    393         public int hashCode() {
    394             return Closure.this.hashCode();
    395         }
    396
    397         /* (non-Javadoc)
    398          * @see java.lang.Object#equals(java.lang.Object)
    399          */
    400         public boolean equals(Object arg0) {
    401             return Closure.this.equals(arg0);
    402         }
    403
    404         /* (non-Javadoc)
    405          * @see java.lang.Object#toString()
    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 }