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