1package com.xtremelabs.robolectric.bytecode;
2
3import com.xtremelabs.robolectric.internal.Implements;
4
5import java.lang.reflect.Constructor;
6import java.lang.reflect.InvocationTargetException;
7import java.util.HashSet;
8import java.util.Set;
9
10@SuppressWarnings({"UnusedDeclaration"})
11public class RobolectricInternals {
12    // initialized via magic by AndroidTranslator
13    private static ClassHandler classHandler;
14    private static Set<String> unloadableClassNames = new HashSet<String>();
15
16    private static final ThreadLocal<Vars> ALL_VARS = new ThreadLocal<Vars>() {
17        @Override protected Vars initialValue() {
18            return new Vars();
19        }
20    };
21
22    private static class Vars {
23        Object callDirectly;
24    }
25
26    public static <T> T newInstanceOf(Class<T> clazz) {
27        try {
28            Constructor<T> defaultConstructor = clazz.getDeclaredConstructor();
29            defaultConstructor.setAccessible(true);
30            return defaultConstructor.newInstance();
31        } catch (InstantiationException e) {
32            throw new RuntimeException(e);
33        } catch (IllegalAccessException e) {
34            throw new RuntimeException(e);
35        } catch (NoSuchMethodException e) {
36            throw new RuntimeException(e);
37        } catch (InvocationTargetException e) {
38            throw new RuntimeException(e);
39        }
40    }
41
42    public static void bindShadowClass(Class<?> shadowClass) {
43        Implements realClass = shadowClass.getAnnotation(Implements.class);
44        if (realClass == null) {
45            throw new IllegalArgumentException(shadowClass + " is not annotated with @Implements");
46        }
47
48        try {
49            ShadowWrangler.getInstance().bindShadowClass(realClass.value(), shadowClass);
50        } catch (TypeNotPresentException typeLoadingException) {
51            String unloadableClassName = shadowClass.getSimpleName();
52            if (isIgnorableClassLoadingException(typeLoadingException)) {
53                //this allows users of the robolectric.jar file to use the non-Google APIs version of the api
54                if (unloadableClassNames.add(unloadableClassName)) {
55                    System.out.println("Warning: an error occurred while binding shadow class: " + unloadableClassName);
56                }
57            } else {
58                throw typeLoadingException;
59            }
60        }
61    }
62
63    private static boolean isIgnorableClassLoadingException(Throwable typeLoadingException) {
64        if (typeLoadingException != null) {
65            // instanceof doesn't work here. Are we in different classloaders?
66            if (typeLoadingException.getClass().getName().equals(IgnorableClassNotFoundException.class.getName())) {
67                return true;
68            }
69
70            if (typeLoadingException instanceof NoClassDefFoundError
71                    || typeLoadingException instanceof ClassNotFoundException
72                    || typeLoadingException instanceof TypeNotPresentException) {
73                return isIgnorableClassLoadingException(typeLoadingException.getCause());
74            }
75        }
76        return false;
77    }
78
79    public static <T> T directlyOn(T shadowedObject) {
80        Vars vars = ALL_VARS.get();
81
82        if (vars.callDirectly != null) {
83            Object expectedInstance = vars.callDirectly;
84            vars.callDirectly = null;
85            throw new RuntimeException("already expecting a direct call on <" + expectedInstance + "> but here's a new request for <" + shadowedObject + ">");
86        }
87
88        vars.callDirectly = shadowedObject;
89        return shadowedObject;
90    }
91
92    public static boolean shouldCallDirectly(Object directInstance) {
93        Vars vars = ALL_VARS.get();
94        if (vars.callDirectly != null) {
95            if (vars.callDirectly != directInstance) {
96                Object expectedInstance = vars.callDirectly;
97                vars.callDirectly = null;
98                throw new RuntimeException("expected to perform direct call on <" + expectedInstance + "> but got <" + directInstance + ">");
99            } else {
100                vars.callDirectly = null;
101            }
102            return true;
103        } else {
104            return false;
105        }
106    }
107
108    @SuppressWarnings({"UnusedDeclaration"})
109    public static Object methodInvoked(Class clazz, String methodName, Object instance, String[] paramTypes, Object[] params) throws Throwable {
110        try {
111          return classHandler.methodInvoked(clazz, methodName, instance, paramTypes, params);
112        } catch(java.lang.LinkageError e) {
113          throw new Exception(e);
114        }
115    }
116
117    @SuppressWarnings({"UnusedDeclaration"})
118    public static Object autobox(Object o) {
119        return o;
120    }
121
122    @SuppressWarnings({"UnusedDeclaration"})
123    public static Object autobox(boolean o) {
124        return o;
125    }
126
127    @SuppressWarnings({"UnusedDeclaration"})
128    public static Object autobox(byte o) {
129        return o;
130    }
131
132    @SuppressWarnings({"UnusedDeclaration"})
133    public static Object autobox(char o) {
134        return o;
135    }
136
137    @SuppressWarnings({"UnusedDeclaration"})
138    public static Object autobox(short o) {
139        return o;
140    }
141
142    @SuppressWarnings({"UnusedDeclaration"})
143    public static Object autobox(int o) {
144        return o;
145    }
146
147    @SuppressWarnings({"UnusedDeclaration"})
148    public static Object autobox(long o) {
149        return o;
150    }
151
152    @SuppressWarnings({"UnusedDeclaration"})
153    public static Object autobox(float o) {
154        return o;
155    }
156
157    @SuppressWarnings({"UnusedDeclaration"})
158    public static Object autobox(double o) {
159        return o;
160    }
161}
162