Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core_internal / src / org / xwalk / core / internal / ReflectionHelper.java
1 // Copyright (c) 2014 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.xwalk.core.internal;
6
7 import java.lang.reflect.Constructor;
8 import java.lang.reflect.InvocationTargetException;
9 import java.lang.reflect.Method;
10 import java.util.Map;
11 import java.util.HashMap;
12
13 import android.content.Context;
14 import android.content.pm.PackageManager;
15
16 /**
17  * This class is used to encapsulate the reflection invoking for bridge and wrapper.
18  *
19  * org.xwalk.core.internal.ReflectionHelper is for bridge to use. A copy will be
20  * made to org.xwalk.core.ReflectionHelper at build time for wrapper to use.
21  */
22 public class ReflectionHelper {
23     /**
24      * This class contains the identical information of a constructor.
25      *
26      * Constructor of bridge and wrapper class will be initialized in the
27      * static block.
28      * It might happen before the RefletionHelper itself being initialized.
29      * For those cases, record the information of the Constructor and load
30      * them after ReflectionHelper initialized.
31      */
32     static class ConstructorHelper {
33         private String fullClassName;
34         private Object[] paramTypes;
35
36         Constructor<?> loadConstructor() {
37             Class<?> clazz = loadClass(fullClassName);
38             if (clazz == null) return null;
39             Class<?>[] params = new Class<?>[paramTypes.length];
40             for (int i = 0; i < paramTypes.length; i++) {
41                 Object type = paramTypes[i];
42                 // paramTypes can be string or Class<?>, if it's string,
43                 // it means it's not in bridge/wrapper's classLoader,
44                 // we need to load it from wrapper/bridge's loader.
45                 if (type instanceof Class<?>) {
46                     params[i] = (Class<?>) type;
47                 } else if (type instanceof String) {
48                     params[i] = loadClass((String) type);
49                 }
50             }
51             try {
52                 return clazz.getConstructor(params);
53             } catch (NoSuchMethodException e) {
54                 ReflectionHelper.handleException(e);
55                 return null;
56             }
57         }
58
59         ConstructorHelper(String className, Object... paramTypes) {
60             this.fullClassName = className;
61             this.paramTypes = paramTypes;
62         }
63     }
64
65     private static Map<Class<?>, Method> sBridgeWrapperMap = new HashMap<Class<?>, Method>();
66     private static Map<String, Constructor<?>> sConstructorMap = new HashMap<String, Constructor<?>>();
67     private static Map<String, ConstructorHelper> sConstructorHelperMap =
68             new HashMap<String, ConstructorHelper>();
69     private static ClassLoader sBridgeOrWrapperLoader = null;
70     private static Context sBridgeContext = null;
71     private static boolean sIsWrapper;
72     private final static String INTERNAL_PACKAGE = "org.xwalk.core.internal";
73     private final static String LIBRARY_APK_PACKAGE = "org.xwalk.core";
74     /* Wrapper Only
75     private static boolean sAllowCrossPackage = false;
76     private static SharedXWalkExceptionHandler sExceptionHandler = null;
77
78     static void setExceptionHandler(SharedXWalkExceptionHandler handler) {
79         sExceptionHandler = handler;
80     }
81
82     public static boolean shouldUseLibrary() {
83         // TODO(wang16): There are many other conditions here.
84         // e.g. Whether application uses the ApplicationClass we provided,
85         //      Whether native library arch is correct.
86         assert isWrapper();
87         try {
88             ReflectionHelper.class.getClassLoader().loadClass(
89                     INTERNAL_PACKAGE + "." + "ReflectionHelper");
90             return false;
91         } catch (ClassNotFoundException e) {
92             return true;
93         }
94     }
95     Wrapper Only */
96
97     public static Context getBridgeContext() {
98         return sBridgeContext;
99     }
100
101     /* Wrapper Only
102     public static void allowCrossPackage() {
103         sAllowCrossPackage = true;
104     }
105     Wrapper Only */
106
107     public static void init() {
108         assert isWrapper();
109         /* Wrapper Only
110         if (shouldUseLibrary()) {
111             if (!sAllowCrossPackage) {
112                 handleException("Use SharedXWalkView if you want to support shared mode");
113             }
114             XWalkApplication app = XWalkApplication.getApplication();
115             if (app == null) {
116                 // TODO(wang16): Handle this well.
117                 handleException("Shared mode requires XWalkApplication");
118                 return;
119             }
120             try {
121                 sBridgeContext = app.createPackageContext(
122                         LIBRARY_APK_PACKAGE,
123                         Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
124             } catch (PackageManager.NameNotFoundException e) {
125                 handleException(e);
126             }
127             if (sBridgeContext != null) {
128                 app.addResource(sBridgeContext.getResources());
129                 initClassLoader(sBridgeContext.getClassLoader(), sBridgeContext);
130             }
131         } else {
132             initClassLoader(ReflectionHelper.class.getClassLoader(), null);
133         }
134         Wrapper Only */
135     }
136
137     public static void initClassLoader(ClassLoader loader, Context bridgeContext) {
138         sBridgeOrWrapperLoader = loader;
139         sBridgeContext = bridgeContext;
140         sBridgeWrapperMap.clear();
141         sConstructorMap.clear();
142         try {
143             for (String name : sConstructorHelperMap.keySet()) {
144                 ConstructorHelper helper = sConstructorHelperMap.get(name);
145                 if (helper != null) sConstructorMap.put(name, helper.loadConstructor());
146             }
147             if (sIsWrapper) {
148                 // Load the helper in bridge side and invoke the initClassLoader method of it
149                 // with wrapper's classloader via reflection.
150                 Class<?> helperInBridge =
151                         sBridgeOrWrapperLoader.loadClass(INTERNAL_PACKAGE + "." + "ReflectionHelper");
152                 Method initInBridge = helperInBridge.getMethod(
153                         "initClassLoader", ClassLoader.class, Context.class);
154                 initInBridge.invoke(null, ReflectionHelper.class.getClassLoader(), sBridgeContext);
155             } else {
156                 // JavascriptInterface is an annotation class bridge will use but declared in
157                 // wrapper.
158                 Class<?> javascriptInterface =
159                         sBridgeOrWrapperLoader.loadClass("org.xwalk.core.JavascriptInterface");
160                 Class<?> xwalkContentInInternal =
161                         ReflectionHelper.class.getClassLoader().loadClass(
162                                 INTERNAL_PACKAGE + "." + "XWalkContent");
163                 Method setJavascriptInterface = xwalkContentInInternal.getDeclaredMethod(
164                         "setJavascriptInterfaceClass", javascriptInterface.getClass());
165                 setJavascriptInterface.invoke(null, javascriptInterface);
166             }
167         } catch (Exception e) {
168             handleException(e);
169         }
170     }
171
172     public static void registerConstructor(String name, String clazz, Object... params) {
173         sConstructorHelperMap.put(name, new ConstructorHelper(clazz, params));
174     }
175
176     public static Class<?> loadClass(String clazz) {
177         // Any embedder using Embedding API should only use the exposed APIs which are
178         // in wrapper, so the initialization process is always starting from wrapper.
179         if (sBridgeOrWrapperLoader == null) init();
180         if (sBridgeOrWrapperLoader == null) return null;
181         try {
182             return sBridgeOrWrapperLoader.loadClass(clazz);
183         } catch (ClassNotFoundException e) {
184             handleException(e);
185             return null;
186         }
187     }
188
189     public static Method loadMethod(Class<?> clazz, String name, Object... paramTypes) {
190         if (sBridgeOrWrapperLoader == null) return null;
191         Class<?>[] params = new Class<?>[paramTypes.length];
192         for (int i = 0; i < paramTypes.length; i++) {
193             Object type = paramTypes[i];
194             if (type instanceof Class<?>) {
195                 params[i] = (Class<?>) type;
196             } else if (type instanceof String) {
197                 params[i] = loadClass((String) type);
198             }
199         }
200         try {
201             return clazz.getMethod(name, params);
202         } catch (NoSuchMethodException e) {
203             handleException(e);
204             return null;
205         }
206     }
207
208     public static void handleException(Exception e) {
209         e.printStackTrace();
210         /* Wrapper Only
211         if (isWrapper() && sExceptionHandler != null) {
212             if (sExceptionHandler.handleException(e)) return;
213         }
214         Wrapper Only */
215         throw new RuntimeException(e);
216     }
217
218     public static void handleException(String e) {
219         handleException(new RuntimeException(e));
220     }
221
222     public static Object createInstance(String name, Object... parameters) {
223         Object ret = null;
224         Constructor<?> creator = sConstructorMap.get(name);
225         if (creator == null) {
226             ConstructorHelper helper = sConstructorHelperMap.get(name);
227             if (helper != null) {
228                 creator = helper.loadConstructor();
229                 sConstructorMap.put(name, creator);
230             }
231         }
232         if (creator != null) {
233             try {
234                 ret = creator.newInstance(parameters);
235             } catch (IllegalArgumentException e) {
236                 handleException(e);
237             } catch (InstantiationException e) {
238                 handleException(e);
239             } catch (IllegalAccessException e) {
240                 handleException(e);
241             } catch (InvocationTargetException e) {
242                 handleException(e);
243             }
244         }
245         return ret;
246     }
247
248     public static Object invokeMethod(Method m, Object instance, Object... parameters) {
249         if (sBridgeOrWrapperLoader == null) return null;
250         Object ret = null;
251         if (m != null) {
252             try {
253                 ret = m.invoke(instance, parameters);
254             } catch (IllegalArgumentException e) {
255                 handleException(e);
256             } catch (IllegalAccessException e) {
257                 handleException(e);
258             } catch (InvocationTargetException e) {
259                 handleException(e);
260             } catch (NullPointerException e) {
261                 handleException(e);
262             }
263         }
264         return ret;
265     }
266
267     // Convert between wrapper and bridge instance.
268     public static Object getBridgeOrWrapper(Object instance) {
269         if (sBridgeOrWrapperLoader == null) return null;
270         if (instance == null) return null;
271         Class<?> clazz = instance.getClass();
272         Method method = sBridgeWrapperMap.get(clazz);
273         if (method == null) {
274             String methodName = "getBridge";
275             if (sIsWrapper) {
276                 methodName = "getWrapper";
277             }
278             try {
279                 method = clazz.getMethod(methodName);
280             } catch (NoSuchMethodException e) {
281                 handleException(e);
282             }
283             if (method != null) sBridgeWrapperMap.put(clazz, method);
284         }
285         return invokeMethod(method, instance);
286     }
287
288     private static boolean isWrapper() {
289         return !ReflectionHelper.class.getPackage().getName().equals(INTERNAL_PACKAGE);
290     }
291
292     static {
293         sIsWrapper = isWrapper();
294     }
295 }