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