Clover coverage report - groovy - 1.0-beta-7
Coverage timestamp: Wed Sep 29 2004 16:55:52 BST
file stats: LOC: 588   Methods: 41
NCLOC: 312   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.40 2004/09/03 09:20:27 tug 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.StringWriter;
 50   
 import java.io.Writer;
 51   
 import java.lang.reflect.InvocationTargetException;
 52   
 import java.lang.reflect.Method;
 53   
 import java.security.AccessController;
 54   
 import java.security.PrivilegedAction;
 55   
 
 56   
 import org.codehaus.groovy.runtime.InvokerHelper;
 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.40 $
 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  0
                             try {
 139   
                                 // lets try invoke method on the delegate
 140  0
                                 return InvokerHelper.invokeMethod(this.delegate, method, arguments);
 141   
                             } catch (GroovyRuntimeException e2) {
 142   
                                 // ignore, we'll throw e
 143   
                             }
 144   
                         }
 145   
                     }
 146   
                  }
 147  0
                 throw e;
 148   
             }
 149   
         }
 150   
 
 151   
     }
 152   
 
 153  0
     public Object getProperty(String property) {
 154  0
             if ("delegate".equals(property)) {
 155  0
                 return getDelegate();
 156  0
             } else if ("owner".equals(property)) {
 157  0
                 return getOwner();
 158  0
             } else if ("method".equals(property)) {
 159  0
                 return getMethod();
 160  0
             } else if ("parameterTypes".equals(property)) {
 161  0
                 return getParameterTypes();
 162  0
             } else if ("metaClass".equals(property)) {
 163  0
                 return getMetaClass();
 164  0
             } else if ("class".equals(property)) {
 165  0
                 return getClass();
 166   
             } else {
 167  0
             try {
 168   
                 // lets try getting the property on the owner
 169  0
                 return InvokerHelper.getProperty(this.owner, property);
 170   
             } catch (GroovyRuntimeException e1) {
 171  0
                 if (this.delegate != null && this.delegate != this && this.delegate != this.owner) {
 172  0
                     try {
 173   
                         // lets try getting the property on the delegate
 174  0
                         return InvokerHelper.getProperty(this.delegate, property);
 175   
                     } catch (GroovyRuntimeException e2) {
 176   
                         // ignore, we'll throw e1
 177   
                     }
 178   
                 }
 179   
                 
 180  0
                 throw e1;
 181   
             }
 182   
          }
 183   
     }
 184   
 
 185  0
     public void setProperty(String property, Object newValue) {
 186  0
             if ("delegate".equals(property)) {
 187  0
                 setDelegate(newValue);
 188  0
             } else if ("metaClass".equals(property)) {
 189  0
                 setMetaClass((MetaClass)newValue);
 190   
             } else {
 191  0
             try {
 192   
                 // lets try setting the property on the owner
 193  0
                 InvokerHelper.setProperty(this.owner, property, newValue);
 194  0
                 return;
 195   
             } catch (GroovyRuntimeException e1) {
 196  0
                 if (this.delegate != null && this.delegate != this && this.delegate != this.owner) {
 197  0
                     try {
 198   
                         // lets try setting the property on the delegate
 199  0
                         InvokerHelper.setProperty(this.delegate, property, newValue);
 200  0
                         return;
 201   
                     } catch (GroovyRuntimeException e2) {
 202   
                         // ignore, we'll throw e1
 203   
                     }
 204   
                  }
 205   
                 
 206  0
                 throw e1;
 207   
            }
 208   
         }
 209   
     }
 210   
 
 211   
     /**
 212   
      * Invokes the closure without any parameters, returning any value if applicable.
 213   
      * 
 214   
      * @return the value if applicable or null if there is no return statement in the closure
 215   
      */
 216  0
     public Object call() {
 217  0
         return call(emptyArray);
 218   
     }
 219   
 
 220   
     /**
 221   
      * Invokes the closure, returning any value if applicable.
 222   
      * 
 223   
      * @param arguments could be a single value or a List of values
 224   
      * @return the value if applicable or null if there is no return statement in the closure
 225   
      */
 226  0
     public Object call(final Object arguments) {
 227  0
         final Object params[];
 228   
 
 229  0
         if (this.curriedParams.length != 0) {
 230  0
             final Object args[];
 231   
 
 232  0
             if (arguments instanceof Object[]) {
 233  0
                 args = (Object[])arguments;
 234   
             } else {
 235  0
                 args = new Object[] {arguments};
 236   
             }
 237   
 
 238  0
             params = new Object[this.curriedParams.length + args.length];
 239   
 
 240  0
             System.arraycopy(this.curriedParams, 0, params, 0, this.curriedParams.length);
 241  0
             System.arraycopy(args, 0, params, this.curriedParams.length, args.length);
 242   
         } else {
 243  0
             if (arguments instanceof Object[]) {
 244  0
                 params = (Object[])arguments;
 245   
             } else {
 246  0
                 return doCall(arguments);
 247   
             }
 248   
         }
 249   
 
 250  0
         final int lastParam = this.numberOfParameters - 1;
 251   
 
 252  0
         if (this.supportsVarargs && !(this.numberOfParameters == params.length && (params[lastParam] == null || params[lastParam].getClass() == Object [].class))) {
 253  0
             final Object actualParameters[] = new Object[this.numberOfParameters];
 254   
 
 255   
             //
 256   
             // We have a closure which supports variable arguments and we haven't got actual
 257   
             // parameters which have exactly the right number of parameters and ends with a null or an Object[]
 258   
             //
 259  0
             if (params.length < lastParam) {
 260   
                 //
 261   
                 // Not enough parameters throw exception
 262   
                 //
 263   
                 // Note we allow there to be one fewer actual parameter than the number of formal parameters
 264   
                 // in this case we pass an zero length Object[] as the last parameter
 265   
                 //
 266  0
                 throw new IncorrectClosureArgumentsException(this, params, this.parameterTypes);
 267   
             } else {
 268  0
                 final Object rest[] = new Object[params.length - lastParam];     // array used to pass the rest of the paraters
 269   
 
 270   
                 // fill the parameter array up to but not including the last one
 271  0
                 System.arraycopy(params, 0, actualParameters, 0, lastParam);
 272   
 
 273   
                 // put the rest of the parameters in the overflow araay
 274  0
                 System.arraycopy(params, lastParam, rest, 0, rest.length);
 275   
 
 276   
                 // pass the overflow array as the last parameter
 277  0
                 actualParameters[lastParam] = rest;
 278   
 
 279  0
                 return callViaReflection(actualParameters);
 280   
             }
 281   
         }
 282   
 
 283  0
         if (params.length == 0) {
 284   
             // pass a single null parameter if no parameters specified
 285  0
             return doCall(null);
 286  0
         } else if (params.length == 1) {
 287  0
             return doCall(params[0]);
 288  0
         } else if (params.length == 2) {
 289  0
             return doCall(params[0], params[1]);
 290   
         } else {
 291  0
             return callViaReflection(params);
 292   
         }
 293   
     }
 294   
 
 295  0
     protected static Object throwRuntimeException(Throwable throwable) {
 296  0
         if (throwable instanceof RuntimeException) {
 297  0
             throw (RuntimeException) throwable;
 298   
         } else {
 299  0
             throw new GroovyRuntimeException(throwable.getMessage(), throwable);
 300   
         }
 301   
     }
 302   
     
 303   
     /**
 304   
      * An attempt to optimise calling closures with one parameter
 305   
      * If the closure has one untyped parameter then it will overload this function
 306   
      * If not this will be called ans will use reflection to deal with the case of a
 307   
      * single typed parameter
 308   
      * @param p1
 309   
      * @return the result of calling the closure
 310   
      */
 311  0
     protected Object doCall(final Object p1) {
 312  0
         return callViaReflection(new Object[] {p1});
 313   
     }
 314   
     
 315   
     /**
 316   
      * An attempt to optimise calling closures with two parameters
 317   
      * If the closure has two untyped parameters then it will overload this function
 318   
      * If not this will be called ans will use reflection to deal with the case of one
 319   
      * or two typed parameters
 320   
      * @param p1
 321   
      * @return the result of calling the closure
 322   
      */
 323   
     
 324  0
     protected Object doCall(final Object p1, final Object p2) {
 325  0
             return callViaReflection(new Object[] {p1, p2});
 326   
     }
 327   
     
 328  0
     private Object callViaReflection(final Object params[]) {       
 329  0
         try {
 330   
             // invoke the closure
 331  0
             return this.doCallMethod.invoke(this, params);
 332   
         } catch (final IllegalArgumentException e) {
 333  0
             throw new IncorrectClosureArgumentsException(this, params, this.parameterTypes);
 334   
         } catch (final IllegalAccessException e) {
 335  0
         final Throwable cause = e.getCause();
 336   
         
 337  0
             return throwRuntimeException((cause == null) ? e : cause);
 338   
         } catch (final InvocationTargetException e) {
 339  0
         final Throwable cause = e.getCause();
 340   
         
 341  0
             return throwRuntimeException((cause == null) ? e : cause);
 342   
         }
 343   
     }
 344   
     
 345   
     /**
 346   
      * Used when a closure wraps a method on a class
 347   
      * 
 348   
      * @return empty string
 349   
      */
 350  0
     public String getMethod() {
 351  0
             return "";
 352   
     }
 353   
 
 354   
     /**
 355   
      * @return the owner Object to which method calls will go which is
 356   
      * typically the outer class when the closure is constructed
 357   
      */
 358  0
     public Object getOwner() {
 359  0
         return this.owner;
 360   
     }
 361   
 
 362   
     /**
 363   
      * @return the delegate Object to which method calls will go which is
 364   
      * typically the outer class when the closure is constructed
 365   
      */
 366  0
     public Object getDelegate() {
 367  0
         return this.delegate;
 368   
     }
 369   
 
 370   
     /**
 371   
      * Allows the delegate to be changed such as when performing markup building
 372   
      * @param delegate
 373   
      */
 374  0
     public void setDelegate(Object delegate) {
 375  0
         this.delegate = delegate;
 376   
     }
 377   
 
 378   
     /**
 379   
      * @return the parameter types of this closure
 380   
      */
 381  0
     public Class[] getParameterTypes() {
 382  0
         return this.parameterTypes;
 383   
     }
 384   
 
 385   
     /**
 386   
      * @return a version of this closure which implements Writable
 387   
      */
 388  0
     public Closure asWritable() {
 389  0
             return new WritableClosure();
 390   
     }
 391   
 
 392   
     /* (non-Javadoc)
 393   
      * @see java.lang.Runnable#run()
 394   
      */
 395  0
     public void run() {
 396  0
         call();
 397   
     }
 398   
 
 399   
     /**
 400   
      * Support for closure currying
 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   
      * @return Returns the directive.
 577   
      */
 578  0
     public int getDirective() {
 579  0
         return directive;
 580   
     }
 581   
     /**
 582   
      * @param directive The directive to set.
 583   
      */
 584  0
     public void setDirective(int directive) {
 585  0
         this.directive = directive;
 586   
     }
 587   
 }
 588