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 any of the class named in {@code className}. This
81      * doesn't work for interfaces.
82      */
isInstanceOf(Object object, String[] classNames)83     public static boolean isInstanceOf(Object object, String[] classNames) {
84         Class superClass = object.getClass();
85         while (superClass != null) {
86             String name = superClass.getName();
87             for (String className : classNames) {
88                 if (name.equals(className)) {
89                     return true;
90                 }
91             }
92             superClass = superClass.getSuperclass();
93         }
94         return false;
95     }
96 
97     @NonNull
getCause(@onNull Throwable throwable)98     public static Throwable getCause(@NonNull Throwable throwable) {
99         Throwable cause = throwable.getCause();
100         return cause == null ? throwable : cause;
101     }
102 
103     /**
104      * Looks through the class hierarchy of {@code object} at runtime and returns the class matching
105      * the name {@code className}.
106      * <p>
107      * This is used when we cannot use Class.forName() since the class we want was loaded from a
108      * different ClassLoader.
109      */
110     @NonNull
getClassInstance(@onNull Object object, @NonNull String className)111     public static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) {
112         Class<?> superClass = object.getClass();
113         while (superClass != null) {
114             if (className.equals(superClass.getName())) {
115                 return superClass;
116             }
117             superClass = superClass.getSuperclass();
118         }
119         throw new RuntimeException("invalid object/classname combination.");
120     }
121 
createProxy(Class<T> interfaze)122     public static <T> T createProxy(Class<T> interfaze) {
123         ClassLoader loader = interfaze.getClassLoader();
124         return (T) Proxy.newProxyInstance(loader, new Class[]{interfaze}, new InvocationHandler() {
125             public Object invoke(Object proxy, Method m, Object[] args) {
126                 final Class<?> returnType = m.getReturnType();
127                 if (returnType == boolean.class) {
128                     return false;
129                 } else if (returnType == int.class) {
130                     return 0;
131                 } else if (returnType == long.class) {
132                     return 0L;
133                 } else if (returnType == short.class) {
134                     return 0;
135                 } else if (returnType == char.class) {
136                     return 0;
137                 } else if (returnType == byte.class) {
138                     return 0;
139                 } else if (returnType == float.class) {
140                     return 0f;
141                 } else if (returnType == double.class) {
142                     return 0.0;
143                 } else {
144                     return null;
145                 }
146             }
147         });
148     }
149 
150     /**
151      * Wraps all reflection related exceptions. Created since ReflectiveOperationException was
152      * introduced in 1.7 and we are still on 1.6
153      */
154     public static class ReflectionException extends Exception {
155         public ReflectionException() {
156             super();
157         }
158 
159         public ReflectionException(String message) {
160             super(message);
161         }
162 
163         public ReflectionException(String message, Throwable cause) {
164             super(message, cause);
165         }
166 
167         public ReflectionException(Throwable cause) {
168             super(cause);
169         }
170     }
171 }
172