Home | History | Annotate | Download | only in bytecode
      1 package com.xtremelabs.robolectric.bytecode;
      2 
      3 import javassist.CannotCompileException;
      4 import javassist.ClassPool;
      5 import javassist.LoaderClassPath;
      6 import javassist.NotFoundException;
      7 
      8 import java.io.File;
      9 import java.lang.System;
     10 import java.util.List;
     11 
     12 public class RobolectricClassLoader extends javassist.Loader {
     13     private ClassCache classCache;
     14     private AndroidTranslator androidTranslator;
     15 
     16     public RobolectricClassLoader(ClassHandler classHandler) {
     17         this(classHandler, null);
     18     }
     19 
     20     public RobolectricClassLoader(ClassHandler classHandler, List<String> customClassNames) {
     21         super(RobolectricClassLoader.class.getClassLoader(), null);
     22 
     23         delegateLoadingOf(AndroidTranslator.class.getName());
     24         delegateLoadingOf(ClassHandler.class.getName());
     25 
     26         final String classCachePath = System.getProperty("cached.robolectric.classes.path");
     27         final File classCacheDirectory;
     28         if (null == classCachePath || "".equals(classCachePath.trim())) {
     29             classCacheDirectory = new File("./tmp");
     30         } else {
     31             classCacheDirectory = new File(classCachePath);
     32         }
     33 
     34         classCache = new ClassCache(new File(classCacheDirectory, "cached-robolectric-classes.jar").getAbsolutePath(), AndroidTranslator.CACHE_VERSION);
     35         try {
     36             ClassPool classPool = new ClassPool();
     37             classPool.appendClassPath(new LoaderClassPath(RobolectricClassLoader.class.getClassLoader()));
     38 
     39             androidTranslator = new AndroidTranslator(classHandler, classCache, customClassNames);
     40             addTranslator(classPool, androidTranslator);
     41         } catch (NotFoundException e) {
     42             throw new RuntimeException(e);
     43         } catch (CannotCompileException e) {
     44             throw new RuntimeException(e);
     45         }
     46     }
     47 
     48     public void addCustomShadowClass(String classOrPackageToBeInstrumented) {
     49         androidTranslator.addCustomShadowClass(classOrPackageToBeInstrumented);
     50     }
     51 
     52     @Override
     53     public Class loadClass(String name) throws ClassNotFoundException {
     54         boolean shouldComeFromThisClassLoader = !(name.startsWith("org.junit") || name.startsWith("org.hamcrest")
     55                 || name.startsWith("org.specs2") || name.startsWith("scala.")); //org.specs2 and scala. allows for android projects with mixed scala\java tests to be run with Maven Surefire (see the RoboSpecs project on github)
     56 
     57         Class<?> theClass;
     58         if (shouldComeFromThisClassLoader) {
     59             theClass = super.loadClass(name);
     60         } else {
     61             theClass = getParent().loadClass(name);
     62         }
     63 
     64         return theClass;
     65     }
     66 
     67     public Class<?> bootstrap(Class testClass) {
     68         String testClassName = testClass.getName();
     69 
     70         try {
     71             return loadClass(testClassName);
     72         } catch (ClassNotFoundException e) {
     73             throw new RuntimeException(e);
     74         }
     75     }
     76 
     77     @Override
     78     protected Class findClass(String name) throws ClassNotFoundException {
     79         byte[] classBytes = classCache.getClassBytesFor(name);
     80         if (classBytes != null) {
     81             return defineClass(name, classBytes, 0, classBytes.length);
     82         }
     83         return super.findClass(name);
     84     }
     85 }
     86