1package com.xtremelabs.robolectric.bytecode;
2
3import javassist.CannotCompileException;
4import javassist.ClassPool;
5import javassist.LoaderClassPath;
6import javassist.NotFoundException;
7
8import java.io.File;
9import java.lang.System;
10import java.util.List;
11
12public 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