1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.layoutlib.common.util;
18 
19 import com.android.tools.layoutlib.annotations.NonNull;
20 import com.android.tools.layoutlib.annotations.Nullable;
21 
22 import java.lang.reflect.Field;
23 import java.lang.reflect.InvocationHandler;
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.lang.reflect.Proxy;
27 
28 /**
29  * Utility to convert checked Reflection exceptions to unchecked exceptions.
30  */
31 public class ReflectionUtils {
32 
33     @NonNull
getMethod(@onNull String className, @NonNull String name, @Nullable Class<?>... params)34     public static Method getMethod(@NonNull String className, @NonNull String name,
35             @Nullable Class<?>... params) throws ReflectionException {
36         try {
37             return getMethod(Class.forName(className), name, params);
38         } catch (ClassNotFoundException e) {
39             throw new ReflectionException(e);
40         }
41     }
42 
43     @NonNull
getMethod(@onNull Class<?> clazz, @NonNull String name, @Nullable Class<?>... params)44     public static Method getMethod(@NonNull Class<?> clazz, @NonNull String name,
45             @Nullable Class<?>... params) throws ReflectionException {
46         try {
47             return clazz.getMethod(name, params);
48         } catch (NoSuchMethodException e) {
49             throw new ReflectionException(e);
50         }
51     }
52 
53     @NonNull
getAccessibleMethod(@onNull Class<?> clazz, @NonNull String name, @Nullable Class<?>... params)54     public static Method getAccessibleMethod(@NonNull Class<?> clazz, @NonNull String name,
55       @Nullable Class<?>... params) throws ReflectionException {
56         Method method = getMethod(clazz, name, params);
57         method.setAccessible(true);
58 
59         return method;
60     }
61 
62     @NonNull
getFieldValue(@onNull Class<?> clazz, Object object, @NonNull String name)63     public static Object getFieldValue(@NonNull Class<?> clazz, Object object, @NonNull String name) throws ReflectionException {
64         try {
65             Field field = clazz.getDeclaredField(name);
66             field.setAccessible(true);
67             return field.get(object);
68         } catch (NoSuchFieldException e) {
69             throw new ReflectionException(e);
70         } catch (IllegalAccessException e) {
71             throw new ReflectionException(e);
72         }
73     }
74 
75     @Nullable
invoke(@onNull Method method, @Nullable Object object, @Nullable Object... args)76     public static Object invoke(@NonNull Method method, @Nullable Object object,
77             @Nullable Object... args) throws ReflectionException {
78         Exception ex;
79         try {
80             return method.invoke(object, args);
81         } catch (IllegalAccessException | InvocationTargetException e) {
82             ex = e;
83         }
84         throw new ReflectionException(ex);
85     }
86 
87     @Nullable
invokeStatic(String className, String methodName, @Nullable Object... args)88     public static Object invokeStatic(String className, String methodName, @Nullable Object... args)
89             throws ReflectionException {
90         try {
91             Method m = getMethod(Class.forName(className), methodName);
92             return invoke(m, null, args);
93         } catch (ClassNotFoundException e) {
94             throw new ReflectionException(e);
95         }
96     }
97 
98     /**
99      * Check if the object is an instance of a class named {@code className}. This doesn't work
100      * for interfaces.
101      */
isInstanceOf(Object object, String className)102     public static boolean isInstanceOf(Object object, String className) {
103         Class superClass = object.getClass();
104         while (superClass != null) {
105             String name = superClass.getName();
106             if (name.equals(className)) {
107                 return true;
108             }
109             superClass = superClass.getSuperclass();
110         }
111         return false;
112     }
113 
114     /**
115      * Check if the object is an instance of a class named {@code className}. This doesn't work
116      * for interfaces.
117      */
isInstanceOf(Object object, String[] classNames)118     public static boolean isInstanceOf(Object object, String[] classNames) {
119         return getParentClass(object, classNames) != null;
120     }
121 
122     /**
123      * Check if the object is an instance of any of the class named in {@code className} and
124      * returns the parent class that matched. This doesn't work for interfaces.
125      */
126     @Nullable
getParentClass(Object object, String[] classNames)127     public static Class<?> getParentClass(Object object, String[] classNames) {
128         Class<?> superClass = object.getClass();
129         while (superClass != null) {
130             String name = superClass.getName();
131             for (String className : classNames) {
132                 if (name.equals(className)) {
133                     return superClass;
134                 }
135             }
136             superClass = superClass.getSuperclass();
137         }
138         return null;
139     }
140 
141     /**
142      * Check if the object is an instance of any of the class named in {@code className} and
143      * returns the name of the parent class that matched. This doesn't work for interfaces.
144      */
145     @Nullable
getParentClassName(Object object, String[] classNames)146     public static String getParentClassName(Object object, String[] classNames) {
147         Class<?> superClass = getParentClass(object, classNames);
148         return superClass != null ? superClass.getName() : null;
149     }
150 
151     @NonNull
getCause(@onNull Throwable throwable)152     public static Throwable getCause(@NonNull Throwable throwable) {
153         Throwable cause = throwable.getCause();
154         return cause == null ? throwable : cause;
155     }
156 
157     /**
158      * Looks through the class hierarchy of {@code object} at runtime and returns the class matching
159      * the name {@code className}.
160      * <p>
161      * This is used when we cannot use Class.forName() since the class we want was loaded from a
162      * different ClassLoader.
163      */
164     @NonNull
getClassInstance(@onNull Object object, @NonNull String className)165     public static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) {
166         Class<?> superClass = object.getClass();
167         while (superClass != null) {
168             if (className.equals(superClass.getName())) {
169                 return superClass;
170             }
171             superClass = superClass.getSuperclass();
172         }
173         throw new RuntimeException("invalid object/classname combination.");
174     }
175 
createProxy(Class<T> interfaze)176     public static <T> T createProxy(Class<T> interfaze) {
177         ClassLoader loader = interfaze.getClassLoader();
178         return (T) Proxy.newProxyInstance(loader, new Class[]{interfaze}, new InvocationHandler() {
179             public Object invoke(Object proxy, Method m, Object[] args) {
180                 final Class<?> returnType = m.getReturnType();
181                 if (returnType == boolean.class) {
182                     return false;
183                 } else if (returnType == int.class) {
184                     return 0;
185                 } else if (returnType == long.class) {
186                     return 0L;
187                 } else if (returnType == short.class) {
188                     return 0;
189                 } else if (returnType == char.class) {
190                     return 0;
191                 } else if (returnType == byte.class) {
192                     return 0;
193                 } else if (returnType == float.class) {
194                     return 0f;
195                 } else if (returnType == double.class) {
196                     return 0.0;
197                 } else {
198                     return null;
199                 }
200             }
201         });
202     }
203 
204     /**
205      * Wraps all reflection related exceptions. Created since ReflectiveOperationException was
206      * introduced in 1.7 and we are still on 1.6
207      */
208     public static class ReflectionException extends Exception {
209         public ReflectionException() {
210             super();
211         }
212 
213         public ReflectionException(String message) {
214             super(message);
215         }
216 
217         public ReflectionException(String message, Throwable cause) {
218             super(message, cause);
219         }
220 
221         public ReflectionException(Throwable cause) {
222             super(cause);
223         }
224     }
225 }
226