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