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