Home | History | Annotate | Download | only in bytecode
      1 package com.xtremelabs.robolectric.bytecode;
      2 
      3 import com.xtremelabs.robolectric.internal.Implements;
      4 
      5 import java.lang.reflect.Constructor;
      6 import java.lang.reflect.InvocationTargetException;
      7 import java.util.HashSet;
      8 import java.util.Set;
      9 
     10 @SuppressWarnings({"UnusedDeclaration"})
     11 public 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