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