414a4ab6a5f2a8c11f4cbdbbc84d6e1c86bfc753
[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 /**
14  * This class is used to encapsulate the reflection invoking for bridge and wrapper.
15  *
16  * org.xwalk.core.internal.ReflectionHelper is for bridge to use. A copy will be
17  * made to org.xwalk.core.ReflectionHelper at build time for wrapper to use.
18  */
19 public class ReflectionHelper {
20     /**
21      * This class contains the identical information of a constructor.
22      *
23      * Constructor of bridge and wrapper class will be initialized in the
24      * static block.
25      * It might happen before the RefletionHelper itself being initialized.
26      * For those cases, record the information of the Constructor and load
27      * them after ReflectionHelper initialized.
28      */
29     static class ConstructorHelper {
30         private String fullClassName;
31         private Object[] paramTypes;
32
33         Constructor<?> loadConstructor() {
34             Class<?> clazz = loadClass(fullClassName);
35             Class<?>[] params = new Class<?>[paramTypes.length];
36             for (int i = 0; i < paramTypes.length; i++) {
37                 Object type = paramTypes[i];
38                 // paramTypes can be string or Class<?>, if it's string,
39                 // it means it's not in bridge/wrapper's classLoader,
40                 // we need to load it from wrapper/bridge's loader.
41                 if (type instanceof Class<?>) {
42                     params[i] = (Class<?>) type;
43                 } else if (type instanceof String) {
44                     params[i] = loadClass((String) type);
45                 }
46             }
47             try {
48                 return clazz.getConstructor(params);
49             } catch (NoSuchMethodException e) {
50                 ReflectionHelper.handleException(e);
51                 return null;
52             }
53         }
54
55         ConstructorHelper(String className, Object... paramTypes) {
56             this.fullClassName = className;
57             this.paramTypes = paramTypes;
58         }
59     }
60
61     private static Map<Class<?>, Method> sBridgeWrapperMap = new HashMap<Class<?>, Method>();
62     private static Map<String, Constructor<?>> sConstructorMap = new HashMap<String, Constructor<?>>();
63     private static Map<String, ConstructorHelper> sConstructorHelperMap =
64             new HashMap<String, ConstructorHelper>();
65     private static ClassLoader sBridgeOrWrapperLoader = null;
66     private static boolean sIsWrapper;
67     private final static String INTERNAL_PACKAGE = "org.xwalk.core.internal";
68
69     public static void init(boolean crossPackage) {
70         assert isWrapper();
71         if (!crossPackage) {
72             initClassLoader(ReflectionHelper.class.getClassLoader());
73         } else {
74             // TODO(wang16): Support shared mode and initClassLoader cross package.
75         }
76     }
77
78     public static void initClassLoader(ClassLoader loader) {
79         sBridgeOrWrapperLoader = loader;
80         sBridgeWrapperMap.clear();
81         sConstructorMap.clear();
82         try {
83             for (String name : sConstructorHelperMap.keySet()) {
84                 ConstructorHelper helper = sConstructorHelperMap.get(name);
85                 if (helper != null) sConstructorMap.put(name, helper.loadConstructor());
86             }
87             if (sIsWrapper) {
88                 // Load the helper in bridge side and invoke the initClassLoader method of it
89                 // with wrapper's classloader via reflection.
90                 Class<?> helperInBridge =
91                         sBridgeOrWrapperLoader.loadClass(INTERNAL_PACKAGE + "." + "ReflectionHelper");
92                 Method initInBridge = helperInBridge.getMethod("initClassLoader", ClassLoader.class);
93                 initInBridge.invoke(null, ReflectionHelper.class.getClassLoader());
94             } else {
95                 // JavascriptInterface is an annotation class bridge will use but declared in
96                 // wrapper.
97                 Class<?> javascriptInterface =
98                         sBridgeOrWrapperLoader.loadClass("org.xwalk.core.JavascriptInterface");
99                 Class<?> xwalkContentInInternal =
100                         ReflectionHelper.class.getClassLoader().loadClass(
101                                 INTERNAL_PACKAGE + "." + "XWalkContent");
102                 Method setJavascriptInterface = xwalkContentInInternal.getDeclaredMethod(
103                         "setJavascriptInterfaceClass", javascriptInterface.getClass());
104                 setJavascriptInterface.invoke(null, javascriptInterface);
105             }
106         } catch (Exception e) {
107             handleException(e);
108         }
109     }
110
111     public static void registerConstructor(String name, String clazz, Object... params) {
112         sConstructorHelperMap.put(name, new ConstructorHelper(clazz, params));
113     }
114
115     public static Class<?> loadClass(String clazz) {
116         // Any embedder using Embedding API should only use the exposed APIs which are
117         // in wrapper, so the initialization process is always starting from wrapper.
118         if (sBridgeOrWrapperLoader == null) init(false);
119         try {
120             return sBridgeOrWrapperLoader.loadClass(clazz);
121         } catch (ClassNotFoundException e) {
122             handleException(e);
123             return null;
124         }
125     }
126
127     public static Method loadMethod(Class<?> clazz, String name, Object... paramTypes) {
128         Class<?>[] params = new Class<?>[paramTypes.length];
129         for (int i = 0; i < paramTypes.length; i++) {
130             Object type = paramTypes[i];
131             if (type instanceof Class<?>) {
132                 params[i] = (Class<?>) type;
133             } else if (type instanceof String) {
134                 params[i] = loadClass((String) type);
135             }
136         }
137         try {
138             return clazz.getMethod(name, params);
139         } catch (NoSuchMethodException e) {
140             handleException(e);
141             return null;
142         }
143     }
144
145     public static void handleException(Exception e) {
146         e.printStackTrace();
147         throw new RuntimeException(e);
148     }
149
150     public static void handleException(String e) {
151         handleException(new RuntimeException(e));
152     }
153
154     public static Object createInstance(String name, Object... parameters) {
155         Object ret = null;
156         Constructor<?> creator = sConstructorMap.get(name);
157         if (creator == null) {
158             ConstructorHelper helper = sConstructorHelperMap.get(name);
159             if (helper != null) {
160                 creator = helper.loadConstructor();
161                 sConstructorMap.put(name, creator);
162             }
163         }
164         if (creator != null) {
165             try {
166                 ret = creator.newInstance(parameters);
167             } catch (IllegalArgumentException e) {
168                 handleException(e);
169             } catch (InstantiationException e) {
170                 handleException(e);
171             } catch (IllegalAccessException e) {
172                 handleException(e);
173             } catch (InvocationTargetException e) {
174                 handleException(e);
175             }
176         }
177         return ret;
178     }
179
180     public static Object invokeMethod(Method m, Object instance, Object... parameters) {
181         Object ret = null;
182         if (m != null) {
183             try {
184                 ret = m.invoke(instance, parameters);
185             } catch (IllegalArgumentException e) {
186                 handleException(e);
187             } catch (IllegalAccessException e) {
188                 handleException(e);
189             } catch (InvocationTargetException e) {
190                 handleException(e);
191             } catch (NullPointerException e) {
192                 handleException(e);
193             }
194         }
195         return ret;
196     }
197
198     // Convert between wrapper and bridge instance.
199     public static Object getBridgeOrWrapper(Object instance) {
200         if (instance == null) return null;
201         Class<?> clazz = instance.getClass();
202         Method method = sBridgeWrapperMap.get(clazz);
203         if (method == null) {
204             String methodName = "getBridge";
205             if (sIsWrapper) {
206                 methodName = "getWrapper";
207             }
208             try {
209                 method = clazz.getMethod(methodName);
210             } catch (NoSuchMethodException e) {
211                 handleException(e);
212             }
213             if (method != null) sBridgeWrapperMap.put(clazz, method);
214         }
215         return invokeMethod(method, instance);
216     }
217
218     private static boolean isWrapper() {
219         return !ReflectionHelper.class.getPackage().getName().equals(INTERNAL_PACKAGE);
220     }
221
222     static {
223         sIsWrapper = isWrapper();
224     }
225 }