Clover coverage report - groovy - 1.0-beta-7
Coverage timestamp: Wed Sep 29 2004 16:55:52 BST
file stats: LOC: 534   Methods: 43
NCLOC: 279   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
GroovyShell.java 0% 0% 0% 0%
coverage
 1   
 /*
 2   
  $Id: GroovyShell.java,v 1.34 2004/07/21 21:42:33 jstump 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.ByteArrayInputStream;
 49   
 import java.io.File;
 50   
 import java.io.IOException;
 51   
 import java.io.InputStream;
 52   
 import java.security.AccessController;
 53   
 import java.security.PrivilegedAction;
 54   
 import java.security.PrivilegedActionException;
 55   
 import java.security.PrivilegedExceptionAction;
 56   
 import java.util.List;
 57   
 import java.lang.reflect.Constructor;
 58   
 import java.lang.reflect.InvocationTargetException;
 59   
 
 60   
 import org.codehaus.groovy.control.CompilationFailedException;
 61   
 import org.codehaus.groovy.control.CompilerConfiguration;
 62   
 import org.codehaus.groovy.runtime.InvokerHelper;
 63   
 import groovy.ui.GroovyMain;
 64   
 
 65   
 /**
 66   
  * Represents a groovy shell capable of running arbitrary groovy scripts
 67   
  * 
 68   
  * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
 69   
  * @author Guillaume Laforge
 70   
  * @version $Revision: 1.34 $
 71   
  */
 72   
 public class GroovyShell extends GroovyObjectSupport {
 73   
     public static final String[] EMPTY_ARGS = {
 74   
     };
 75   
 
 76   
     private GroovyClassLoader loader;
 77   
     private Binding context;
 78   
     private int counter;
 79   
 
 80  0
     public static void main(String[] args) {
 81  0
         GroovyMain.main(args);
 82   
     }
 83   
 
 84  0
     public GroovyShell() {
 85  0
         this(null, new Binding());
 86   
     }
 87   
 
 88  0
     public GroovyShell(Binding binding) {
 89  0
         this(null, binding);
 90   
     }
 91   
 
 92  0
     public GroovyShell(CompilerConfiguration config) {
 93  0
         this(new Binding(), config);
 94   
     }
 95   
 
 96  0
     public GroovyShell(Binding binding, CompilerConfiguration config) {
 97  0
         this(null, binding, config);
 98   
     }
 99   
 
 100  0
     public GroovyShell(ClassLoader parent, Binding binding) {
 101  0
         this(parent, binding, null);
 102   
     }
 103   
 
 104  0
     public GroovyShell(ClassLoader parent) {
 105  0
         this(parent, new Binding(), null);
 106   
     }
 107   
 
 108  0
     public GroovyShell(final ClassLoader parent, Binding binding, final CompilerConfiguration config) {
 109  0
         this.loader = 
 110   
                 (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
 111  0
                     public Object run() {
 112  0
                         ClassLoader pcl = parent;
 113  0
                         if (pcl == null) {
 114  0
                             pcl = Thread.currentThread().getContextClassLoader();
 115  0
                             if (pcl == null) {
 116  0
                                 pcl = GroovyShell.class.getClassLoader();
 117   
                             }
 118   
                         }
 119  0
                         return new GroovyClassLoader(pcl, (config == null) ? new CompilerConfiguration() : config);
 120   
                     }
 121   
                 });
 122  0
         this.context = binding;
 123   
     }
 124   
 
 125   
     /**
 126   
      * Creates a child shell using a new ClassLoader which uses the parent shell's 
 127   
      * class loader as its parent
 128   
      * 
 129   
      * @param shell is the parent shell used for the variable bindings and the parent class loader
 130   
      */
 131  0
     public GroovyShell(GroovyShell shell) {
 132  0
         this(shell.loader, shell.context);
 133   
     }
 134   
 
 135  0
     public Binding getContext() {
 136  0
         return context;
 137   
     }
 138   
 
 139  0
     public Object getProperty(String property) {
 140  0
         Object answer = getVariable(property);
 141  0
         if (answer == null) {
 142  0
             answer = super.getProperty(property);
 143   
         }
 144  0
         return answer;
 145   
     }
 146   
 
 147  0
     public void setProperty(String property, Object newValue) {
 148  0
         setVariable(property, newValue);
 149  0
         try {
 150  0
             super.setProperty(property, newValue);
 151   
         }
 152   
         catch (GroovyRuntimeException e) {
 153   
             // ignore, was probably a dynamic property
 154   
         }
 155   
     }
 156   
 
 157   
     /**
 158   
      * A helper method which runs the given script file with the given command line arguments
 159   
      * 
 160   
      * @param scriptFile the file of the script to run
 161   
      * @param list the command line arguments to pass in
 162   
      */
 163  0
     public void run(File scriptFile, List list) throws CompilationFailedException, IOException {
 164  0
         String[] args = new String[list.size()];
 165  0
         run(scriptFile, (String[])list.toArray(args));
 166   
     }
 167   
 
 168   
     /**
 169   
      * A helper method which runs the given cl script with the given command line arguments
 170   
      * 
 171   
      * @param scriptText is the text content of the script
 172   
      * @param fileName is the logical file name of the script (which is used to create the class name of the script)
 173   
      * @param list the command line arguments to pass in
 174   
      */
 175  0
     public void run(String scriptText, String fileName, List list) throws CompilationFailedException, IOException {
 176  0
         String[] args = new String[list.size()];
 177  0
         list.toArray(args);
 178  0
         run(scriptText, fileName, args);
 179   
     }
 180   
 
 181   
     /**
 182   
      * Runs the given script file name with the given command line arguments
 183   
      * 
 184   
      * @param scriptFile the file name of the script to run
 185   
      * @param args the command line arguments to pass in
 186   
      */
 187  0
     public void run(final File scriptFile, String[] args) throws CompilationFailedException, IOException {
 188  0
         String scriptName = scriptFile.getName();
 189  0
         int p = scriptName.lastIndexOf(".");
 190  0
         if ( p++ >= 0) {
 191  0
             if (scriptName.substring(p).equals("java")) {
 192  0
                 System.err.println( "error: cannot compile file with .java extension: " + scriptName );
 193  0
                 throw new CompilationFailedException(0, null);
 194   
             }
 195   
         }
 196   
 
 197   
         // Get the current context classloader and save it on the stack
 198  0
         final Thread thread = Thread.currentThread();
 199  0
         ClassLoader currentClassLoader = thread.getContextClassLoader();
 200   
         
 201   
         class DoSetContext implements PrivilegedAction {
 202   
             ClassLoader classLoader;
 203  0
             public DoSetContext(ClassLoader loader) {
 204  0
                 classLoader = loader;
 205   
             }
 206  0
             public Object run() {
 207  0
                 thread.setContextClassLoader(classLoader);
 208  0
                 return null;
 209   
             }
 210   
         };
 211   
         
 212  0
         AccessController.doPrivileged(new DoSetContext(loader));
 213   
 
 214   
         // Parse the script, generate the class, and invoke the main method.  This is a little looser than
 215   
         // if you are compiling the script because the JVM isn't executing the main method.
 216  0
         Class scriptClass;
 217  0
         try {
 218  0
             scriptClass = (Class) AccessController.doPrivileged( new PrivilegedExceptionAction() {
 219  0
                 public Object run() throws CompilationFailedException, IOException {
 220  0
                     return loader.parseClass(scriptFile);
 221   
                 }
 222   
             });
 223   
         } catch (PrivilegedActionException pae) {
 224  0
             Exception e = pae.getException();
 225  0
             if (e instanceof CompilationFailedException) {
 226  0
                 throw (CompilationFailedException)e;
 227   
             }
 228  0
             else if (e instanceof IOException) {
 229  0
                 throw (IOException) e;
 230   
             }
 231   
             else {
 232  0
                 throw (RuntimeException) pae.getException();
 233   
             }
 234   
         }
 235   
 
 236  0
         runMainOrTestOrRunnable(scriptClass, args);
 237   
 
 238   
         // Set the context classloader back to what it was.
 239  0
         AccessController.doPrivileged(new DoSetContext(currentClassLoader));
 240   
     }
 241   
 
 242   
     /**
 243   
      * if (theClass has a main method) {
 244   
      *      run the main method
 245   
      * } else if (theClass instanceof GroovyTestCase) {
 246   
      *      use the test runner to run it
 247   
      * } else if (theClass implements Runnable) {
 248   
      *      if (theClass has a constructor with String[] params)
 249   
      *          instanciate theClass with this constructor and run
 250   
      *      else if (theClass has a no-args constructor)
 251   
      *          instanciate theClass with the no-args constructor and run
 252   
      * }
 253   
      */
 254  0
     private void runMainOrTestOrRunnable(Class scriptClass, String[] args) {
 255  0
         try {
 256   
             // let's find a main method
 257  0
             scriptClass.getMethod("main", new Class[] { String[].class} );
 258   
             // if that main method exist, invoke it
 259  0
             InvokerHelper.invokeMethod(scriptClass, "main", new Object[]{args});
 260   
         }
 261   
         catch (NoSuchMethodException e) {
 262   
             // As no main() method was found, let's see if it's a unit test
 263   
             // if it's a unit test extending GroovyTestCase, run it with JUnit's TextRunner
 264  0
             if (isUnitTestCase(scriptClass)) {
 265  0
                 runTest(scriptClass);
 266   
             }
 267   
             // no main() method, not a unit test,
 268   
             // if it implements Runnable, try to instanciate it
 269  0
             else if (Runnable.class.isAssignableFrom(scriptClass)) {
 270  0
                 Constructor constructor = null;
 271  0
                 Runnable runnable = null;
 272  0
                 Throwable reason = null;
 273  0
                 try {
 274   
                     // first, fetch the constructor taking String[] as parameter
 275  0
                     constructor = scriptClass.getConstructor(new Class[] { (new String[] {}).getClass() });
 276  0
                     try {
 277   
                         // instanciate a runnable and run it
 278  0
                         runnable = (Runnable) constructor.newInstance(new Object[]{args});
 279   
                     }
 280   
                     catch (Throwable t) {
 281  0
                         reason = t;
 282   
                     }
 283   
                 }
 284   
                 catch (NoSuchMethodException e1) {
 285  0
                     try {
 286   
                         // otherwise, find the default constructor
 287  0
                         constructor = scriptClass.getConstructor(new Class[]{});
 288  0
                         try {
 289   
                             // instanciate a runnable and run it
 290  0
                             runnable = (Runnable) constructor.newInstance(new Object[]{});
 291   
                         }
 292   
                         catch (Throwable t) {
 293  0
                             reason = t;
 294   
                         }
 295   
                     }
 296   
                     catch (NoSuchMethodException nsme) {
 297  0
                         reason = nsme;
 298   
                     }
 299   
                 }
 300  0
                 if (constructor != null && runnable != null) {
 301  0
                     runnable.run();
 302   
                 } else {
 303  0
                     throw new GroovyRuntimeException("This script or class could not be run. ", reason);
 304   
                 }
 305   
             } else {
 306  0
                 throw new GroovyRuntimeException("This script or class could not be run. \n" +
 307   
                                                  "It should either: \n" +
 308   
                                                  "- have a main method, \n" +
 309   
                                                  "- be a class extending GroovyTestCase, \n" +
 310   
                                                  "- or implement the Runnable interface.");
 311   
             }
 312   
         }
 313   
     }
 314   
 
 315   
     /**
 316   
      * Run the specified class extending GroovyTestCase as a unit test.
 317   
      * This is done through reflection, to avoid adding a dependency to the JUnit framework.
 318   
      * Otherwise, developers embedding Groovy and using GroovyShell to load/parse/compile
 319   
      * groovy scripts and classes would have to add another dependency on their classpath.
 320   
      *
 321   
      * @param scriptClass the class to be run as a unit test
 322   
      */
 323  0
     private void runTest(Class scriptClass) {
 324  0
         try {
 325  0
             InvokerHelper.invokeStaticMethod("junit.textui.TestRunner", "run", new Object[] {scriptClass});
 326   
         }
 327   
         catch (Exception e) {
 328  0
             throw new GroovyRuntimeException("Failed to run the unit test. JUnit is not on the Classpath.");
 329   
         }
 330   
     }
 331   
 
 332   
     /**
 333   
      * Utility method to check through reflection if the parsed class extends GroovyTestCase.
 334   
      *
 335   
      * @param scriptClass the class we want to know if it extends GroovyTestCase
 336   
      * @return true if the class extends groovy.util.GroovyTestCase
 337   
      */
 338  0
     private boolean isUnitTestCase(Class scriptClass) {
 339   
         // check if the parsed class is a GroovyTestCase,
 340   
         // so that it is possible to run it as a JUnit test
 341  0
         boolean isUnitTestCase = false;
 342  0
         try {
 343  0
             try {
 344  0
                 Class testCaseClass = this.loader.loadClass("groovy.util.GroovyTestCase");
 345   
                 // if scriptClass extends testCaseClass
 346  0
                 if (testCaseClass.isAssignableFrom(scriptClass)) {
 347  0
                     isUnitTestCase = true;
 348   
                 }
 349   
             }
 350   
             catch (ClassNotFoundException e) {
 351   
                 // fall through
 352   
             }
 353   
         }
 354   
         catch (Exception e) {
 355   
             // fall through
 356   
         }
 357  0
         return isUnitTestCase;
 358   
     }
 359   
 
 360   
     /**
 361   
      * Runs the given script text with command line arguments
 362   
      * 
 363   
      * @param scriptText is the text content of the script
 364   
      * @param fileName is the logical file name of the script (which is used to create the class name of the script)
 365   
      * @param args the command line arguments to pass in
 366   
      */
 367  0
     public void run(String scriptText, String fileName, String[] args) throws CompilationFailedException, IOException {
 368  0
         run(new ByteArrayInputStream(scriptText.getBytes()), fileName, args);
 369   
     }
 370   
 
 371   
     /**
 372   
      * Runs the given script with command line arguments
 373   
      * 
 374   
      * @param in the stream reading the script
 375   
      * @param fileName is the logical file name of the script (which is used to create the class name of the script)
 376   
      * @param args the command line arguments to pass in
 377   
      */
 378  0
     public Object run(final InputStream in, final String fileName, String[] args) throws CompilationFailedException, IOException {
 379  0
         GroovyCodeSource gcs = (GroovyCodeSource) AccessController.doPrivileged(new PrivilegedAction() {
 380  0
             public Object run() {
 381  0
                 return new GroovyCodeSource(in, fileName, "/groovy/shell");
 382   
             }
 383   
         });
 384  0
         Class scriptClass = parseClass(gcs);
 385  0
         runMainOrTestOrRunnable(scriptClass, args);
 386  0
         return null;
 387   
     }
 388   
 
 389  0
     public Object getVariable(String name) {
 390  0
         return context.getVariable(name);
 391   
     }
 392   
 
 393  0
     public void setVariable(String name, Object value) {
 394  0
         context.setVariable(name, value);
 395   
     }
 396   
 
 397   
     /**
 398   
      * Evaluates some script against the current Binding and returns the result
 399   
      * @param codeSource
 400   
      * @return
 401   
      * @throws CompilationFailedException
 402   
      * @throws IOException
 403   
      */
 404  0
     public Object evaluate(GroovyCodeSource codeSource) throws CompilationFailedException, IOException {
 405  0
         Script script = parse(codeSource);
 406  0
         return script.run();
 407   
     }
 408   
 
 409   
     /**
 410   
      * Evaluates some script against the current Binding and returns the result
 411   
      * 
 412   
      * @param scriptText the text of the script
 413   
      * @param fileName is the logical file name of the script (which is used to create the class name of the script)
 414   
      */
 415  0
     public Object evaluate(String scriptText, String fileName) throws CompilationFailedException, ClassNotFoundException, IOException {
 416  0
         return evaluate(new ByteArrayInputStream(scriptText.getBytes()), fileName);
 417   
     }
 418   
 
 419   
     /**
 420   
      * Evaluates some script against the current Binding and returns the result.
 421   
      * The .class file created from the script is given the supplied codeBase
 422   
      */
 423  0
     public Object evaluate(String scriptText, String fileName, String codeBase) throws CompilationFailedException, IOException {
 424  0
         return evaluate(new GroovyCodeSource(new ByteArrayInputStream(scriptText.getBytes()), fileName, codeBase));
 425   
     }
 426   
 
 427   
     /**
 428   
      * Evaluates some script against the current Binding and returns the result
 429   
      * 
 430   
      * @param file is the file of the script (which is used to create the class name of the script)
 431   
      */
 432  0
     public Object evaluate(File file) throws CompilationFailedException, IOException {
 433  0
         return evaluate(new GroovyCodeSource(file));
 434   
     }
 435   
 
 436   
     /**
 437   
      * Evaluates some script against the current Binding and returns the result
 438   
      *
 439   
      * @param scriptText the text of the script
 440   
      */
 441  0
     public Object evaluate(String scriptText) throws CompilationFailedException, IOException {
 442  0
         return evaluate(new ByteArrayInputStream(scriptText.getBytes()), generateScriptName());
 443   
     }
 444   
 
 445   
     /**
 446   
      * Evaluates some script against the current Binding and returns the result
 447   
      *
 448   
      * @param in the stream reading the script
 449   
      */
 450  0
     public Object evaluate(InputStream in) throws CompilationFailedException, IOException {
 451  0
         return evaluate(in, generateScriptName());
 452   
     }
 453   
 
 454   
     /**
 455   
      * Evaluates some script against the current Binding and returns the result
 456   
      * 
 457   
      * @param in the stream reading the script
 458   
      * @param fileName is the logical file name of the script (which is used to create the class name of the script)
 459   
      */
 460  0
     public Object evaluate(InputStream in, String fileName) throws CompilationFailedException, IOException {
 461  0
         Script script = parse(in, fileName);
 462  0
         return script.run();
 463   
     }
 464   
 
 465   
     /**
 466   
      * Parses the given script and returns it ready to be run
 467   
      * 
 468   
      * @param in the stream reading the script
 469   
      * @param fileName is the logical file name of the script (which is used to create the class name of the script)
 470   
      * @return the parsed script which is ready to be run via @link Script.run()
 471   
      */
 472  0
     public Script parse(final InputStream in, final String fileName) throws CompilationFailedException, IOException {
 473  0
         GroovyCodeSource gcs = (GroovyCodeSource) AccessController.doPrivileged(new PrivilegedAction() {
 474  0
             public Object run() {
 475  0
                 return new GroovyCodeSource(in, fileName, "/groovy/shell");
 476   
             }
 477   
         });
 478  0
         return parse(gcs);
 479   
     }
 480   
 
 481   
     /**
 482   
      * Parses the groovy code contained in codeSource and returns a java class.
 483   
      */
 484  0
     private Class parseClass(final GroovyCodeSource codeSource) throws CompilationFailedException, IOException {
 485  0
         return loader.parseClass(codeSource);
 486   
     }
 487   
     
 488   
     /**
 489   
      * Parses the given script and returns it ready to be run.  When running in a secure environment
 490   
      * (-Djava.security.manager) codeSource.getCodeSource() determines what policy grants should be
 491   
      * given to the script.
 492   
      * @param codeSource
 493   
      * @return
 494   
      */
 495  0
     public Script parse(final GroovyCodeSource codeSource) throws CompilationFailedException, IOException {
 496  0
         return InvokerHelper.createScript(parseClass(codeSource), context);
 497   
     }
 498   
     
 499   
     /**
 500   
      * Parses the given script and returns it ready to be run
 501   
      * 
 502   
      * @param file is the file of the script (which is used to create the class name of the script)
 503   
      */
 504  0
     public Script parse(File file) throws CompilationFailedException, IOException {
 505  0
         return parse(new GroovyCodeSource(file));
 506   
     }
 507   
 
 508   
     /**
 509   
      * Parses the given script and returns it ready to be run
 510   
      *
 511   
      * @param scriptText the text of the script
 512   
      */
 513  0
     public Script parse(String scriptText) throws CompilationFailedException, IOException {
 514  0
         return parse(new ByteArrayInputStream(scriptText.getBytes()), generateScriptName());
 515   
     }
 516   
 
 517  0
     public Script parse(String scriptText, String fileName) throws CompilationFailedException, IOException {
 518  0
         return parse(new ByteArrayInputStream(scriptText.getBytes()), fileName);
 519   
     }
 520   
     
 521   
     /**
 522   
      * Parses the given script and returns it ready to be run
 523   
      *
 524   
      * @param in the stream reading the script
 525   
      */
 526  0
     public Script parse(InputStream in) throws CompilationFailedException, IOException {
 527  0
         return parse(in, generateScriptName());
 528   
     }
 529   
 
 530  0
     protected synchronized String generateScriptName() {
 531  0
         return "Script" + (++counter) + ".groovy";
 532   
     }
 533   
 }
 534