Upstream version 10.39.226.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 boolean sAlreadyUsingLibrary = false;
77     private static SharedXWalkExceptionHandler sExceptionHandler = null;
78
79     static void setExceptionHandler(SharedXWalkExceptionHandler handler) {
80         sExceptionHandler = handler;
81     }
82
83     static boolean isUsingLibrary() {
84         return sAlreadyUsingLibrary;
85     }
86
87     static boolean shouldUseLibrary() {
88         if (sAlreadyUsingLibrary) return true;
89
90         // TODO(wang16): There are many other conditions here.
91         // e.g. Whether application uses the ApplicationClass we provided,
92         //      Whether native library arch is correct.
93         assert isWrapper();
94         Class<?> delegateClass = null;
95         try {
96             ClassLoader classLoader = ReflectionHelper.class.getClassLoader();
97             delegateClass = classLoader.loadClass(
98                     INTERNAL_PACKAGE + "." + "XWalkViewDelegate");
99         } catch (ClassNotFoundException e) {
100             return true;
101         }
102         if (delegateClass == null) return true;
103         try {
104             Method loadXWalkLibrary = delegateClass.getDeclaredMethod(
105                     "loadXWalkLibrary", Context.class);
106             loadXWalkLibrary.invoke(null, (Context)null);
107         } catch (NoSuchMethodException e) {
108             return true;
109         } catch (IllegalArgumentException e) {
110             return true;
111         } catch (IllegalAccessException e) {
112             return true;
113         } catch (InvocationTargetException e) {
114             return true;
115         } catch (UnsatisfiedLinkError e) {
116             return true;
117         }
118         return false;
119     }
120     Wrapper Only */
121
122     public static Context getBridgeContext() {
123         return sBridgeContext;
124     }
125
126     /* Wrapper Only
127     public static void allowCrossPackage() {
128         sAllowCrossPackage = true;
129     }
130     Wrapper Only */
131
132     public static void init() {
133         assert isWrapper();
134         /* Wrapper Only
135         if (shouldUseLibrary()) {
136             if (!sAllowCrossPackage) {
137                 handleException("Use SharedXWalkView if you want to support shared mode");
138             }
139             XWalkApplication app = XWalkApplication.getApplication();
140             if (app == null) {
141                 // TODO(wang16): Handle this well.
142                 handleException("Shared mode requires XWalkApplication");
143                 return;
144             }
145             try {
146                 sBridgeContext = app.createPackageContext(
147                         LIBRARY_APK_PACKAGE,
148                         Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
149                 sAlreadyUsingLibrary = true;
150             } catch (PackageManager.NameNotFoundException e) {
151                 handleException(e);
152             }
153             if (sBridgeContext != null) {
154                 app.addResource(sBridgeContext.getResources());
155                 initClassLoader(sBridgeContext.getClassLoader(), sBridgeContext);
156             }
157         } else {
158             initClassLoader(ReflectionHelper.class.getClassLoader(), null);
159         }
160         Wrapper Only */
161     }
162
163     public static void initClassLoader(ClassLoader loader, Context bridgeContext) {
164         sBridgeOrWrapperLoader = loader;
165         sBridgeContext = bridgeContext;
166         sBridgeWrapperMap.clear();
167         sConstructorMap.clear();
168         try {
169             for (String name : sConstructorHelperMap.keySet()) {
170                 ConstructorHelper helper = sConstructorHelperMap.get(name);
171                 if (helper != null) sConstructorMap.put(name, helper.loadConstructor());
172             }
173             if (sIsWrapper) {
174                 // Load the helper in bridge side and invoke the initClassLoader method of it
175                 // with wrapper's classloader via reflection.
176                 Class<?> helperInBridge =
177                         sBridgeOrWrapperLoader.loadClass(INTERNAL_PACKAGE + "." + "ReflectionHelper");
178                 Method initInBridge = helperInBridge.getMethod(
179                         "initClassLoader", ClassLoader.class, Context.class);
180                 initInBridge.invoke(null, ReflectionHelper.class.getClassLoader(), sBridgeContext);
181             } else {
182                 // JavascriptInterface is an annotation class bridge will use but declared in
183                 // wrapper.
184                 Class<?> javascriptInterface =
185                         sBridgeOrWrapperLoader.loadClass("org.xwalk.core.JavascriptInterface");
186                 Class<?> xwalkContentInInternal =
187                         ReflectionHelper.class.getClassLoader().loadClass(
188                                 INTERNAL_PACKAGE + "." + "XWalkContent");
189                 Method setJavascriptInterface = xwalkContentInInternal.getDeclaredMethod(
190                         "setJavascriptInterfaceClass", javascriptInterface.getClass());
191                 setJavascriptInterface.invoke(null, javascriptInterface);
192             }
193         } catch (Exception e) {
194             handleException(e);
195         }
196     }
197
198     public static void registerConstructor(String name, String clazz, Object... params) {
199         sConstructorHelperMap.put(name, new ConstructorHelper(clazz, params));
200     }
201
202     public static Class<?> loadClass(String clazz) {
203         // Any embedder using Embedding API should only use the exposed APIs which are
204         // in wrapper, so the initialization process is always starting from wrapper.
205         if (sBridgeOrWrapperLoader == null) init();
206         if (sBridgeOrWrapperLoader == null) return null;
207         try {
208             return sBridgeOrWrapperLoader.loadClass(clazz);
209         } catch (ClassNotFoundException e) {
210             handleException(e);
211             return null;
212         }
213     }
214
215     public static Method loadMethod(Class<?> clazz, String name, Object... paramTypes) {
216         if (sBridgeOrWrapperLoader == null) return null;
217         Class<?>[] params = new Class<?>[paramTypes.length];
218         for (int i = 0; i < paramTypes.length; i++) {
219             Object type = paramTypes[i];
220             if (type instanceof Class<?>) {
221                 params[i] = (Class<?>) type;
222             } else if (type instanceof String) {
223                 params[i] = loadClass((String) type);
224             }
225         }
226         try {
227             return clazz.getMethod(name, params);
228         } catch (NoSuchMethodException e) {
229             handleException(e);
230             return null;
231         }
232     }
233
234     public static void handleException(Exception e) {
235         e.printStackTrace();
236         /* Wrapper Only
237         if (isWrapper() && sExceptionHandler != null) {
238             if (sExceptionHandler.handleException(e)) return;
239         }
240         Wrapper Only */
241         throw new RuntimeException(e);
242     }
243
244     public static void handleException(String e) {
245         handleException(new RuntimeException(e));
246     }
247
248     public static Object createInstance(String name, Object... parameters) {
249         Object ret = null;
250         Constructor<?> creator = sConstructorMap.get(name);
251         if (creator == null) {
252             ConstructorHelper helper = sConstructorHelperMap.get(name);
253             if (helper != null) {
254                 creator = helper.loadConstructor();
255                 sConstructorMap.put(name, creator);
256             }
257         }
258         if (creator != null) {
259             try {
260                 ret = creator.newInstance(parameters);
261             } catch (IllegalArgumentException e) {
262                 handleException(e);
263             } catch (InstantiationException e) {
264                 handleException(e);
265             } catch (IllegalAccessException e) {
266                 handleException(e);
267             } catch (InvocationTargetException e) {
268                 handleException(e);
269             }
270         }
271         return ret;
272     }
273
274     public static Object invokeMethod(Method m, Object instance, Object... parameters) {
275         if (sBridgeOrWrapperLoader == null) return null;
276         Object ret = null;
277         if (m != null) {
278             try {
279                 ret = m.invoke(instance, parameters);
280             } catch (IllegalArgumentException e) {
281                 handleException(e);
282             } catch (IllegalAccessException e) {
283                 handleException(e);
284             } catch (InvocationTargetException e) {
285                 handleException(e);
286             } catch (NullPointerException e) {
287                 handleException(e);
288             }
289         }
290         return ret;
291     }
292
293     // Convert between wrapper and bridge instance.
294     public static Object getBridgeOrWrapper(Object instance) {
295         if (sBridgeOrWrapperLoader == null) return null;
296         if (instance == null) return null;
297         Class<?> clazz = instance.getClass();
298         Method method = sBridgeWrapperMap.get(clazz);
299         if (method == null) {
300             String methodName = "getBridge";
301             if (sIsWrapper) {
302                 methodName = "getWrapper";
303             }
304             try {
305                 method = clazz.getDeclaredMethod(methodName);
306             } catch (NoSuchMethodException e) {
307                 handleException(e);
308             }
309
310             if (method == null)  {
311                 return invokeMethod(method, instance);
312             } else {
313                 sBridgeWrapperMap.put(clazz, method);
314             }
315         }
316
317         if (method.isAccessible()) return invokeMethod(method, instance);
318
319         // This is to enable the accessibility of getBridge temporarily.
320         // It's not public for documentation generating.
321         method.setAccessible(true);
322         Object ret = invokeMethod(method, instance);
323         method.setAccessible(false);
324         return ret;
325     }
326
327     private static boolean isWrapper() {
328         return !ReflectionHelper.class.getPackage().getName().equals(INTERNAL_PACKAGE);
329     }
330
331     static {
332         sIsWrapper = isWrapper();
333     }
334 }