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