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.bridge.util;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 
22 import java.lang.reflect.InvocationHandler;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Proxy;
26 
27 /**
28  * Utility to convert checked Reflection exceptions to unchecked exceptions.
29  */
30 public class ReflectionUtils {
31 
32     @NonNull
getMethod(@onNull Class<?> clazz, @NonNull String name, @Nullable Class<?>... params)33     public static Method getMethod(@NonNull Class<?> clazz, @NonNull String name,
34             @Nullable Class<?>... params) throws ReflectionException {
35         try {
36             return clazz.getMethod(name, params);
37         } catch (NoSuchMethodException e) {
38             throw new ReflectionException(e);
39         }
40     }
41 
42     @NonNull
getAccessibleMethod(@onNull Class<?> clazz, @NonNull String name, @Nullable Class<?>... params)43     public static Method getAccessibleMethod(@NonNull Class<?> clazz, @NonNull String name,
44       @Nullable Class<?>... params) throws ReflectionException {
45         Method method = getMethod(clazz, name, params);
46         method.setAccessible(true);
47 
48         return method;
49     }
50 
51     @Nullable
invoke(@onNull Method method, @Nullable Object object, @Nullable Object... args)52     public static Object invoke(@NonNull Method method, @Nullable Object object,
53             @Nullable Object... args) throws ReflectionException {
54         Exception ex;
55         try {
56             return method.invoke(object, args);
57         } catch (IllegalAccessException | InvocationTargetException e) {
58             ex = e;
59         }
60         throw new ReflectionException(ex);
61     }
62 
63     /**
64      * Check if the object is an instance of a class named {@code className}. This doesn't work
65      * for interfaces.
66      */
isInstanceOf(Object object, String className)67     public static boolean isInstanceOf(Object object, String className) {
68         Class superClass = object.getClass();
69         while (superClass != null) {
70             String name = superClass.getName();
71             if (name.equals(className)) {
72                 return true;
73             }
74             superClass = superClass.getSuperclass();
75         }
76         return false;
77     }
78 
79     /**
80      * Check if the object is an instance of a class named {@code className}. This doesn't work
81      * for interfaces.
82      */
isInstanceOf(Object object, String[] classNames)83     public static boolean isInstanceOf(Object object, String[] classNames) {
84         return getParentClass(object, classNames) != null;
85     }
86 
87     /**
88      * Check if the object is an instance of any of the class named in {@code className} and
89      * returns the name of the parent class that matched. This doesn't work for interfaces.
90      */
91     @Nullable
getParentClass(Object object, String[] classNames)92     public static String getParentClass(Object object, String[] classNames) {
93         Class superClass = object.getClass();
94         while (superClass != null) {
95             String name = superClass.getName();
96             for (String className : classNames) {
97                 if (name.equals(className)) {
98                     return className;
99                 }
100             }
101             superClass = superClass.getSuperclass();
102         }
103         return null;
104     }
105 
106     @NonNull
getCause(@onNull Throwable throwable)107     public static Throwable getCause(@NonNull Throwable throwable) {
108         Throwable cause = throwable.getCause();
109         return cause == null ? throwable : cause;
110     }
111 
112     /**
113      * Looks through the class hierarchy of {@code object} at runtime and returns the class matching
114      * the name {@code className}.
115      * <p>
116      * This is used when we cannot use Class.forName() since the class we want was loaded from a
117      * different ClassLoader.
118      */
119     @NonNull
getClassInstance(@onNull Object object, @NonNull String className)120     public static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) {
121         Class<?> superClass = object.getClass();
122         while (superClass != null) {
123             if (className.equals(superClass.getName())) {
124                 return superClass;
125             }
126             superClass = superClass.getSuperclass();
127         }
128         throw new RuntimeException("invalid object/classname combination.");
129     }
130 
createProxy(Class<T> interfaze)131     public static <T> T createProxy(Class<T> interfaze) {
132         ClassLoader loader = interfaze.getClassLoader();
133         return (T) Proxy.newProxyInstance(loader, new Class[]{interfaze}, new InvocationHandler() {
134             public Object invoke(Object proxy, Method m, Object[] args) {
135                 final Class<?> returnType = m.getReturnType();
136                 if (returnType == boolean.class) {
137                     return false;
138                 } else if (returnType == int.class) {
139                     return 0;
140                 } else if (returnType == long.class) {
141                     return 0L;
142                 } else if (returnType == short.class) {
143                     return 0;
144                 } else if (returnType == char.class) {
145                     return 0;
146                 } else if (returnType == byte.class) {
147                     return 0;
148                 } else if (returnType == float.class) {
149                     return 0f;
150                 } else if (returnType == double.class) {
151                     return 0.0;
152                 } else {
153                     return null;
154                 }
155             }
156         });
157     }
158 
159     /**
160      * Wraps all reflection related exceptions. Created since ReflectiveOperationException was
161      * introduced in 1.7 and we are still on 1.6
162      */
163     public static class ReflectionException extends Exception {
164         public ReflectionException() {
165             super();
166         }
167 
168         public ReflectionException(String message) {
169             super(message);
170         }
171 
172         public ReflectionException(String message, Throwable cause) {
173             super(message, cause);
174         }
175 
176         public ReflectionException(Throwable cause) {
177             super(cause);
178         }
179     }
180 }
181