Clover coverage report - groovy - 1.0-beta-8
Coverage timestamp: Fri Dec 17 2004 14:55:55 GMT
file stats: LOC: 632   Methods: 32
NCLOC: 368   Classes: 4
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
GroovyClassLoader.java 0% 0% 0% 0%
coverage
 1   
 /*
 2   
  * $Id: GroovyClassLoader.java,v 1.30 2004/12/14 00:08:26 spullara Exp $
 3   
  *
 4   
  * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
 5   
  *
 6   
  * Redistribution and use of this software and associated documentation
 7   
  * ("Software"), with or without modification, are permitted provided that the
 8   
  * following conditions are met:
 9   
  *  1. Redistributions of source code must retain copyright statements and
 10   
  * notices. Redistributions must also contain a copy of this document.
 11   
  *  2. Redistributions in binary form must reproduce the above copyright
 12   
  * notice, this list of conditions and the following disclaimer in the
 13   
  * documentation and/or other materials provided with the distribution.
 14   
  *  3. The name "groovy" must not be used to endorse or promote products
 15   
  * derived from this Software without prior written permission of The Codehaus.
 16   
  * For written permission, please contact info@codehaus.org.
 17   
  *  4. Products derived from this Software may not be called "groovy" nor may
 18   
  * "groovy" appear in their names without prior written permission of The
 19   
  * Codehaus. "groovy" is a registered trademark of The Codehaus.
 20   
  *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
 21   
  *
 22   
  * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
 23   
  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 24   
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 25   
  * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
 26   
  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 27   
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 28   
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 29   
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 30   
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 31   
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 32   
  * DAMAGE.
 33   
  *
 34   
  */
 35   
 package groovy.lang;
 36   
 
 37   
 import org.codehaus.groovy.ast.ClassNode;
 38   
 import org.codehaus.groovy.classgen.Verifier;
 39   
 import org.codehaus.groovy.control.CompilationFailedException;
 40   
 import org.codehaus.groovy.control.CompilationUnit;
 41   
 import org.codehaus.groovy.control.CompilerConfiguration;
 42   
 import org.codehaus.groovy.control.Phases;
 43   
 import org.objectweb.asm.ClassVisitor;
 44   
 import org.objectweb.asm.ClassWriter;
 45   
 
 46   
 import java.io.*;
 47   
 import java.lang.reflect.Field;
 48   
 import java.net.MalformedURLException;
 49   
 import java.net.URL;
 50   
 import java.security.*;
 51   
 import java.security.cert.Certificate;
 52   
 import java.util.ArrayList;
 53   
 import java.util.HashMap;
 54   
 import java.util.List;
 55   
 import java.util.Map;
 56   
 import java.util.jar.Attributes;
 57   
 import java.util.jar.JarEntry;
 58   
 import java.util.jar.JarFile;
 59   
 import java.util.jar.Manifest;
 60   
 
 61   
 /**
 62   
  * A ClassLoader which can load Groovy classes
 63   
  *
 64   
  * @author <a href="mailto:james@coredevelopers.net">James Strachan </a>
 65   
  * @author Guillaume Laforge
 66   
  * @author Steve Goetze
 67   
  * @author Bing Ran
 68   
  * @version $Revision: 1.30 $
 69   
  */
 70   
 public class GroovyClassLoader extends SecureClassLoader {
 71   
 
 72   
     private Map cache = new HashMap();
 73   
 
 74  0
     public void removeFromCache(Class aClass) {
 75  0
         cache.remove(aClass);
 76   
     }
 77   
 
 78   
     private class PARSING {
 79   
     };
 80   
 
 81   
     private class NOT_RESOLVED {
 82   
     };
 83   
 
 84   
     private CompilerConfiguration config;
 85   
 
 86   
     private String[] searchPaths;
 87   
 
 88  0
     public GroovyClassLoader() {
 89  0
         this(Thread.currentThread().getContextClassLoader());
 90   
     }
 91   
 
 92  0
     public GroovyClassLoader(ClassLoader loader) {
 93  0
         this(loader, new CompilerConfiguration());
 94   
     }
 95   
 
 96  0
     public GroovyClassLoader(GroovyClassLoader parent) {
 97  0
         this(parent, parent.config);
 98   
     }
 99   
 
 100  0
     public GroovyClassLoader(ClassLoader loader, CompilerConfiguration config) {
 101  0
         super(loader);
 102  0
         this.config = config;
 103   
     }
 104   
 
 105   
     /**
 106   
      * Loads the given class node returning the implementation Class
 107   
      *
 108   
      * @param classNode
 109   
      * @return
 110   
      */
 111  0
     public Class defineClass(ClassNode classNode, String file) {
 112  0
         return defineClass(classNode, file, "/groovy/defineClass");
 113   
     }
 114   
 
 115   
     /**
 116   
      * Loads the given class node returning the implementation Class
 117   
      *
 118   
      * @param classNode
 119   
      * @return
 120   
      */
 121  0
     public Class defineClass(ClassNode classNode, String file, String newCodeBase) {
 122  0
         CodeSource codeSource = null;
 123  0
         try {
 124  0
             codeSource = new CodeSource(new URL("file", "", newCodeBase), (java.security.cert.Certificate[]) null);
 125   
         } catch (MalformedURLException e) {
 126   
             //swallow
 127   
         }
 128   
 
 129   
         //
 130   
         // BUG: Why is this passing getParent() as the ClassLoader???
 131   
 
 132  0
         CompilationUnit unit = new CompilationUnit(config, codeSource, getParent());
 133  0
         try {
 134  0
             ClassCollector collector = createCollector(unit);
 135   
 
 136  0
             unit.addClassNode(classNode);
 137  0
             unit.setClassgenCallback(collector);
 138  0
             unit.compile(Phases.CLASS_GENERATION);
 139   
 
 140  0
             return collector.generatedClass;
 141   
         } catch (CompilationFailedException e) {
 142  0
             throw new RuntimeException(e);
 143   
         }
 144   
     }
 145   
 
 146   
     /**
 147   
      * Parses the given file into a Java class capable of being run
 148   
      *
 149   
      * @param file the file name to parse
 150   
      * @return the main class defined in the given script
 151   
      */
 152  0
     public Class parseClass(File file) throws CompilationFailedException, IOException {
 153  0
         return parseClass(new GroovyCodeSource(file));
 154   
     }
 155   
 
 156   
     /**
 157   
      * Parses the given text into a Java class capable of being run
 158   
      *
 159   
      * @param text     the text of the script/class to parse
 160   
      * @param fileName the file name to use as the name of the class
 161   
      * @return the main class defined in the given script
 162   
      */
 163  0
     public Class parseClass(String text, String fileName) throws CompilationFailedException, IOException {
 164  0
         return parseClass(new ByteArrayInputStream(text.getBytes()), fileName);
 165   
     }
 166   
 
 167   
     /**
 168   
      * Parses the given text into a Java class capable of being run
 169   
      *
 170   
      * @param text the text of the script/class to parse
 171   
      * @return the main class defined in the given script
 172   
      */
 173  0
     public Class parseClass(String text) throws CompilationFailedException, IOException {
 174  0
         return parseClass(new ByteArrayInputStream(text.getBytes()), "script" + System.currentTimeMillis() + ".groovy");
 175   
     }
 176   
 
 177   
     /**
 178   
      * Parses the given character stream into a Java class capable of being run
 179   
      *
 180   
      * @param in an InputStream
 181   
      * @return the main class defined in the given script
 182   
      */
 183  0
     public Class parseClass(InputStream in) throws CompilationFailedException, IOException {
 184  0
         return parseClass(in, "script" + System.currentTimeMillis() + ".groovy");
 185   
     }
 186   
 
 187  0
     public Class parseClass(final InputStream in, final String fileName) throws CompilationFailedException, IOException {
 188   
         //For generic input streams, provide a catch-all codebase of
 189   
         // GroovyScript
 190   
         //Security for these classes can be administered via policy grants with
 191   
         // a codebase
 192   
         //of file:groovy.script
 193  0
         GroovyCodeSource gcs = (GroovyCodeSource) AccessController.doPrivileged(new PrivilegedAction() {
 194  0
             public Object run() {
 195  0
                 return new GroovyCodeSource(in, fileName, "/groovy/script");
 196   
             }
 197   
         });
 198  0
         return parseClass(gcs);
 199   
     }
 200   
 
 201   
 
 202  0
     public Class parseClass(GroovyCodeSource codeSource) throws IOException, CompilationFailedException {
 203  0
         return parseClass(codeSource, true);
 204   
     }
 205   
 
 206   
     /**
 207   
      * Parses the given code source into a Java class capable of being run
 208   
      *
 209   
      * @return the main class defined in the given script
 210   
      */
 211  0
     public Class parseClass(GroovyCodeSource codeSource, boolean shouldCache) throws CompilationFailedException, IOException {
 212  0
         String name = codeSource.getName();
 213  0
         Class answer = null;
 214   
         //ASTBuilder.resolveName can call this recursively -- for example when
 215   
         // resolving a Constructor
 216   
         //invocation for a class that is currently being compiled.
 217  0
         synchronized (cache) {
 218  0
             answer = (Class) cache.get(name);
 219  0
             if (answer != null) {
 220  0
                 return (answer == PARSING.class ? null : answer);
 221   
             } else {
 222  0
                 cache.put(name, PARSING.class);
 223   
             }
 224   
         }
 225   
         //Was neither already loaded nor compiling, so compile and add to
 226   
         // cache.
 227  0
         try {
 228  0
             CompilationUnit unit = new CompilationUnit(config, codeSource.getCodeSource(), this);
 229   
             // try {
 230  0
             ClassCollector collector = createCollector(unit);
 231   
 
 232  0
             unit.addSource(name, codeSource.getInputStream());
 233  0
             unit.setClassgenCallback(collector);
 234  0
             unit.compile(Phases.CLASS_GENERATION);
 235   
 
 236  0
             answer = collector.generatedClass;
 237   
             // }
 238   
             // catch( CompilationFailedException e ) {
 239   
             //     throw new RuntimeException( e );
 240   
             // }
 241   
         } finally {
 242  0
             synchronized (cache) {
 243  0
                 if (answer == null || !shouldCache) {
 244  0
                     cache.remove(name);
 245   
                 } else {
 246  0
                     cache.put(name, answer);
 247   
                 }
 248   
             }
 249   
         }
 250  0
         return answer;
 251   
     }
 252   
 
 253   
     /**
 254   
      * Using this classloader you can load groovy classes from the system
 255   
      * classpath as though they were already compiled. Note that .groovy classes
 256   
      * found with this mechanism need to conform to the standard java naming
 257   
      * convention - i.e. the public class inside the file must match the
 258   
      * filename and the file must be located in a directory structure that
 259   
      * matches the package structure.
 260   
      */
 261  0
     protected Class findClass(final String name) throws ClassNotFoundException {
 262  0
         SecurityManager sm = System.getSecurityManager();
 263  0
         if (sm != null) {
 264  0
             String className = name.replace('/', '.');
 265  0
             int i = className.lastIndexOf('.');
 266  0
             if (i != -1) {
 267  0
                 sm.checkPackageDefinition(className.substring(0, i));
 268   
             }
 269   
         }
 270  0
         try {
 271  0
             return (Class) AccessController.doPrivileged(new PrivilegedExceptionAction() {
 272  0
                 public Object run() throws ClassNotFoundException {
 273  0
                     return findGroovyClass(name);
 274   
                 }
 275   
             });
 276   
         } catch (PrivilegedActionException pae) {
 277  0
             throw (ClassNotFoundException) pae.getException();
 278   
         }
 279   
     }
 280   
 
 281  0
     protected Class findGroovyClass(String name) throws ClassNotFoundException {
 282   
         //Use a forward slash here for the path separator. It will work as a
 283   
         // separator
 284   
         //for the File class on all platforms, AND it is required as a jar file
 285   
         // entry separator.
 286  0
         String filename = name.replace('.', '/') + ".groovy";
 287  0
         String[] paths = getClassPath();
 288   
         // put the absolute classname in a File object so we can easily
 289   
         // pluck off the class name and the package path
 290  0
         File classnameAsFile = new File(filename);
 291   
         // pluck off the classname without the package
 292  0
         String classname = classnameAsFile.getName();
 293  0
         String pkg = classnameAsFile.getParent();
 294  0
         String pkgdir;
 295  0
         for (int i = 0; i < paths.length; i++) {
 296  0
             String pathName = paths[i];
 297  0
             File path = new File(pathName);
 298  0
             if (path.exists()) {
 299  0
                 if (path.isDirectory()) {
 300   
                     // patch to fix case preserving but case insensitive file
 301   
                     // systems (like macosx)
 302   
                     // JIRA issue 414
 303   
                     //
 304   
                     // first see if the file even exists, no matter what the
 305   
                     // case is
 306  0
                     File nocasefile = new File(path, filename);
 307  0
                     if (!nocasefile.exists())
 308  0
                         continue;
 309   
 
 310   
                     // now we know the file is there is some form or another, so
 311   
                     // let's look up all the files to see if the one we're
 312   
                     // really
 313   
                     // looking for is there
 314  0
                     if (pkg == null)
 315  0
                         pkgdir = pathName;
 316   
                     else
 317  0
                         pkgdir = pathName + "/" + pkg;
 318  0
                     File pkgdirF = new File(pkgdir);
 319   
                     // make sure the resulting path is there and is a dir
 320  0
                     if (pkgdirF.exists() && pkgdirF.isDirectory()) {
 321  0
                         File files[] = pkgdirF.listFiles();
 322  0
                         for (int j = 0; j < files.length; j++) {
 323   
                             // do the case sensitive comparison
 324  0
                             if (files[j].getName().equals(classname)) {
 325  0
                                 try {
 326  0
                                     return parseClass(files[j]);
 327   
                                 } catch (CompilationFailedException e) {
 328  0
                                     throw new ClassNotFoundException("Syntax error in groovy file: " + files[j].getAbsolutePath(), e);
 329   
                                 } catch (IOException e) {
 330  0
                                     throw new ClassNotFoundException("Error reading groovy file: " + files[j].getAbsolutePath(), e);
 331   
                                 }
 332   
                             }
 333   
                         }
 334   
                     }
 335   
                 } else {
 336  0
                     try {
 337  0
                         JarFile jarFile = new JarFile(path);
 338  0
                         JarEntry entry = jarFile.getJarEntry(filename);
 339  0
                         if (entry != null) {
 340  0
                             byte[] bytes = extractBytes(jarFile, entry);
 341  0
                             Certificate[] certs = entry.getCertificates();
 342  0
                             try {
 343  0
                                 return parseClass(new GroovyCodeSource(new ByteArrayInputStream(bytes), filename, path, certs));
 344   
                             } catch (CompilationFailedException e1) {
 345  0
                                 throw new ClassNotFoundException("Syntax error in groovy file: " + filename, e1);
 346   
                             } catch (IOException e1) {
 347  0
                                 throw new ClassNotFoundException("Error reading groovy file: " + filename, e1);
 348   
                             }
 349   
                         }
 350   
 
 351   
                     } catch (IOException e) {
 352   
                         // Bad jar in classpath, ignore
 353   
                     }
 354   
                 }
 355   
             }
 356   
         }
 357  0
         throw new ClassNotFoundException(name);
 358   
     }
 359   
 
 360   
     //Read the bytes from a non-null JarEntry. This is done here because the
 361   
     // entry must be read completely
 362   
     //in order to get verified certificates, which can only be obtained after a
 363   
     // full read.
 364  0
     private byte[] extractBytes(JarFile jarFile, JarEntry entry) {
 365  0
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 366  0
         int b;
 367  0
         try {
 368  0
             BufferedInputStream bis = new BufferedInputStream(jarFile.getInputStream(entry));
 369  0
             while ((b = bis.read()) != -1) {
 370  0
                 baos.write(b);
 371   
             }
 372   
         } catch (IOException ioe) {
 373  0
             throw new GroovyRuntimeException("Could not read the jar bytes for " + entry.getName());
 374   
         }
 375  0
         return baos.toByteArray();
 376   
     }
 377   
 
 378   
     /**
 379   
      * @return
 380   
      */
 381  0
     protected String[] getClassPath() {
 382  0
         if (searchPaths == null) {
 383  0
             List pathList = new ArrayList();
 384  0
             String classpath = System.getProperty("java.class.path", ".");
 385  0
             expandClassPath(pathList, null, classpath);
 386  0
             searchPaths = new String[pathList.size()];
 387  0
             searchPaths = (String[]) pathList.toArray(searchPaths);
 388   
         }
 389  0
         return searchPaths;
 390   
     }
 391   
 
 392   
     /**
 393   
      * @param pathList
 394   
      * @param classpath
 395   
      */
 396  0
     protected void expandClassPath(List pathList, String base, String classpath) {
 397   
 
 398   
         // checking against null prevents an NPE when recursevely expanding the
 399   
         // classpath
 400   
         // in case the classpath is malformed
 401  0
         if (classpath != null) {
 402   
 
 403   
             // Sun's convention for the class-path attribute is to seperate each
 404   
             // entry with spaces
 405   
             // but some libraries don't respect that convention and add commas,
 406   
             // colons, semi-colons
 407  0
             String[] paths = classpath.split("[\\ ,:;]");
 408   
 
 409  0
             for (int i = 0; i < paths.length; i++) {
 410  0
                 if (paths.length > 0) {
 411  0
                     File path = null;
 412   
 
 413  0
                     if ("".equals(base)) {
 414  0
                         path = new File(paths[i]);
 415   
                     } else {
 416  0
                         path = new File(base, paths[i]);
 417   
                     }
 418   
 
 419  0
                     if (path.exists()) {
 420  0
                         if (!path.isDirectory()) {
 421  0
                             try {
 422  0
                                 JarFile jar = new JarFile(path);
 423  0
                                 pathList.add(paths[i]);
 424   
 
 425  0
                                 Manifest manifest = jar.getManifest();
 426  0
                                 if (manifest != null) {
 427  0
                                     Attributes classPathAttributes = manifest.getMainAttributes();
 428  0
                                     String manifestClassPath = classPathAttributes.getValue("Class-Path");
 429   
 
 430  0
                                     if (manifestClassPath != null)
 431  0
                                         expandClassPath(pathList, paths[i], manifestClassPath);
 432   
                                 }
 433   
                             } catch (IOException e) {
 434   
                                 // Bad jar, ignore
 435  0
                                 continue;
 436   
                             }
 437   
                         } else {
 438  0
                             pathList.add(paths[i]);
 439   
                         }
 440   
                     }
 441   
                 }
 442   
             }
 443   
         }
 444   
     }
 445   
 
 446   
     /**
 447   
      * A helper method to allow bytecode to be loaded. spg changed name to
 448   
      * defineClass to make it more consistent with other ClassLoader methods
 449   
      */
 450  0
     protected Class defineClass(String name, byte[] bytecode, ProtectionDomain domain) {
 451  0
         return defineClass(name, bytecode, 0, bytecode.length, domain);
 452   
     }
 453   
 
 454  0
     protected ClassCollector createCollector(CompilationUnit unit) {
 455  0
         return new ClassCollector(this, unit);
 456   
     }
 457   
 
 458   
     public static class ClassCollector extends CompilationUnit.ClassgenCallback {
 459   
         private Class generatedClass;
 460   
 
 461   
         private GroovyClassLoader cl;
 462   
 
 463   
         private CompilationUnit unit;
 464   
 
 465  0
         protected ClassCollector(GroovyClassLoader cl, CompilationUnit unit) {
 466  0
             this.cl = cl;
 467  0
             this.unit = unit;
 468   
         }
 469   
 
 470  0
         protected Class onClassNode(ClassWriter classWriter, ClassNode classNode) {
 471  0
             byte[] code = classWriter.toByteArray();
 472   
 
 473   
             //
 474   
             // BUG: Why is this not conditional?
 475   
 
 476   
             // cl.debugWriteClassfile(classNode, code);
 477   
 
 478  0
             Class theClass = cl.defineClass(classNode.getName(), code, 0, code.length, unit.getAST().getCodeSource());
 479   
 
 480  0
             if (generatedClass == null) {
 481  0
                 generatedClass = theClass;
 482   
             }
 483   
 
 484  0
             return theClass;
 485   
         }
 486   
 
 487  0
         public void call(ClassVisitor classWriter, ClassNode classNode) {
 488  0
             onClassNode((ClassWriter) classWriter, classNode);
 489   
         }
 490   
     }
 491   
 
 492   
     //
 493   
     // BUG: Should this be replaced with CompilationUnit.output()?
 494   
 
 495   
     /*
 496   
      * private void debugWriteClassfile(ClassNode classNode, byte[] code) { if
 497   
      * (config.getTargetDirectory().length() > 0 ) { File targetDir = new
 498   
      * File(config.getTargetDirectory());
 499   
      * 
 500   
      * String filename = classNode.getName().replace('.', File.separatorChar) +
 501   
      * ".class"; int index = filename.lastIndexOf(File.separator); String
 502   
      * dirname; if (index != -1) { dirname = filename.substring(0, index); }
 503   
      * else { dirname = ""; } File outputFile = new File(targetDir, filename);
 504   
      * System.err.println("Writing: " + outputFile); try { new File(targetDir,
 505   
      * dirname).mkdirs(); FileOutputStream fos = new
 506   
      * FileOutputStream(outputFile); fos.write(code, 0, code.length);
 507   
      * fos.close(); } catch (IOException ioe) { ioe.printStackTrace(); } } } /**
 508   
      * open up the super class define that takes raw bytes
 509   
      *  
 510   
      */
 511  0
     public Class defineClass(String name, byte[] b) {
 512  0
         return super.defineClass(name, b, 0, b.length);
 513   
     }
 514   
 
 515   
     /*
 516   
      * (non-Javadoc)
 517   
      * 
 518   
      * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
 519   
      *      Implemented here to check package access prior to returning an
 520   
      *      already loaded class. todo : br shall we search for the source
 521   
      *      groovy here to see if the soource file has been updated first?
 522   
      */
 523  0
     protected synchronized Class loadClass(final String name, boolean resolve) throws ClassNotFoundException {
 524  0
         SecurityManager sm = System.getSecurityManager();
 525  0
         if (sm != null) {
 526  0
             String className = name.replace('/', '.');
 527  0
             int i = className.lastIndexOf('.');
 528  0
             if (i != -1) {
 529  0
                 sm.checkPackageAccess(className.substring(0, i));
 530   
             }
 531   
         }
 532  0
         Class cls = super.loadClass(name, resolve);
 533   
 
 534  0
         if (getTimeStamp(cls) < Long.MAX_VALUE) {
 535  0
             Class[] inters = cls.getInterfaces();
 536  0
             boolean isGroovyObject = false;
 537  0
             for (int i = 0; i < inters.length; i++) {
 538  0
                 if (inters[i].getName().equals(GroovyObject.class.getName())) {
 539  0
                     isGroovyObject = true;
 540  0
                     break;
 541   
                 }
 542   
             }
 543   
 
 544  0
             if (isGroovyObject) {
 545  0
                 try {
 546  0
                     File source = (File) AccessController.doPrivileged(new PrivilegedAction() {
 547  0
                         public Object run() {
 548  0
                             return getSourceFile(name);
 549   
                         }
 550   
                     });
 551  0
                     if (source != null && cls != null && isSourceNewer(source, cls)) {
 552  0
                         cls = parseClass(source);
 553   
                     }
 554   
                 } catch (Exception e) {
 555  0
                     synchronized (cache) {
 556  0
                         cache.put(name, NOT_RESOLVED.class);
 557   
                     }
 558  0
                     throw new ClassNotFoundException("Failed to parse groovy file: " + name, e);
 559   
                 }
 560   
             }
 561   
         }
 562  0
         return cls;
 563   
     }
 564   
 
 565  0
     private long getTimeStamp(Class cls) {
 566  0
         Field field;
 567  0
         Long o;
 568  0
         try {
 569  0
             field = cls.getField(Verifier.__TIMESTAMP);
 570  0
             o = (Long) field.get(null);
 571   
         } catch (Exception e) {
 572   
             //throw new RuntimeException(e);
 573  0
             return Long.MAX_VALUE;
 574   
         }
 575  0
         return o.longValue();
 576   
     }
 577   
 
 578   
     //    static class ClassWithTimeTag {
 579   
     //        final static ClassWithTimeTag NOT_RESOLVED = new ClassWithTimeTag(null,
 580   
     // 0);
 581   
     //        Class cls;
 582   
     //        long lastModified;
 583   
     //
 584   
     //        public ClassWithTimeTag(Class cls, long lastModified) {
 585   
     //            this.cls = cls;
 586   
     //            this.lastModified = lastModified;
 587   
     //        }
 588   
     //    }
 589   
 
 590  0
     private File getSourceFile(String name) {
 591  0
         File source = null;
 592  0
         String filename = name.replace('.', '/') + ".groovy";
 593  0
         String[] paths = getClassPath();
 594  0
         for (int i = 0; i < paths.length; i++) {
 595  0
             String pathName = paths[i];
 596  0
             File path = new File(pathName);
 597  0
             if (path.exists()) { // case sensitivity depending on OS!
 598  0
                 if (path.isDirectory()) {
 599  0
                     File file = new File(path, filename);
 600  0
                     if (file.exists()) {
 601   
                         // file.exists() might be case insensitive. Let's do
 602   
                         // case sensitive match for the filename
 603  0
                         boolean fileExists = false;
 604  0
                         int sepp = filename.lastIndexOf('/');
 605  0
                         String fn = filename;
 606  0
                         if (sepp >= 0) {
 607  0
                             fn = filename.substring(++sepp);
 608   
                         }
 609  0
                         File parent = file.getParentFile();
 610  0
                         String[] files = parent.list();
 611  0
                         for (int j = 0; j < files.length; j++) {
 612  0
                             if (files[j].equals(fn)) {
 613  0
                                 fileExists = true;
 614  0
                                 break;
 615   
                             }
 616   
                         }
 617   
 
 618  0
                         if (fileExists) {
 619  0
                             source = file;
 620  0
                             break;
 621   
                         }
 622   
                     }
 623   
                 }
 624   
             }
 625   
         }
 626  0
         return source;
 627   
     }
 628   
 
 629  0
     private boolean isSourceNewer(File source, Class cls) {
 630  0
         return source.lastModified() > getTimeStamp(cls);
 631   
     }
 632   
 }