1 // Copyright 2015 The Chromium Authors. 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.chromium.incrementalinstall; 6 7 import java.lang.reflect.Array; 8 import java.lang.reflect.Constructor; 9 import java.lang.reflect.Field; 10 import java.lang.reflect.Method; 11 import java.util.Arrays; 12 13 /** 14 * Reflection helper methods. 15 */ 16 final class Reflect { 17 /** 18 * Sets the value of an object's field (even if it's not visible). 19 * 20 * @param instance The object containing the field to set. 21 * @param name The name of the field to set. 22 * @param value The new value for the field. 23 */ 24 static void setField(Object instance, String name, Object value) 25 throws ReflectiveOperationException { 26 Field field = findField(instance, name); 27 field.setAccessible(true); 28 field.set(instance, value); 29 } 30 31 /** 32 * Retrieves the value of an object's field (even if it's not visible). 33 * 34 * @param instance The object containing the field to set. 35 * @param name The name of the field to set. 36 * @return The field's value. Primitive values are returned as their boxed 37 * type. 38 */ 39 static Object getField(Object instance, String name) throws ReflectiveOperationException { 40 Field field = findField(instance, name); 41 field.setAccessible(true); 42 return field.get(instance); 43 } 44 45 /** 46 * Concatenates two arrays into a new array. The arrays must be of the same 47 * type. 48 */ 49 static Object[] concatArrays(Object[] arrType, Object[] left, Object[] right) { 50 Object[] result = (Object[]) Array.newInstance( 51 arrType.getClass().getComponentType(), left.length + right.length); 52 System.arraycopy(left, 0, result, 0, left.length); 53 System.arraycopy(right, 0, result, left.length, right.length); 54 return result; 55 } 56 57 /** 58 * Invokes a method with zero or more parameters. For static methods, use the Class as the 59 * instance. 60 */ 61 static Object invokeMethod(Object instance, String name, Object... params) 62 throws ReflectiveOperationException { 63 boolean isStatic = instance instanceof Class; 64 Class<?> clazz = isStatic ? (Class<?>) instance : instance.getClass(); 65 Method method = findMethod(clazz, name, params); 66 method.setAccessible(true); 67 return method.invoke(instance, params); 68 } 69 70 /** 71 * Calls a constructor with zero or more parameters. 72 */ 73 static Object newInstance(Class<?> clazz, Object... params) 74 throws ReflectiveOperationException { 75 Constructor<?> constructor = findConstructor(clazz, params); 76 constructor.setAccessible(true); 77 return constructor.newInstance(params); 78 } 79 80 private static Field findField(Object instance, String name) throws NoSuchFieldException { 81 boolean isStatic = instance instanceof Class; 82 Class<?> clazz = isStatic ? (Class<?>) instance : instance.getClass(); 83 for (; clazz != null; clazz = clazz.getSuperclass()) { 84 try { 85 return clazz.getDeclaredField(name); 86 } catch (NoSuchFieldException e) { 87 // Need to look in the super class. 88 } 89 } 90 throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass()); 91 } 92 93 private static Method findMethod(Class<?> clazz, String name, Object... params) 94 throws NoSuchMethodException { 95 for (; clazz != null; clazz = clazz.getSuperclass()) { 96 for (Method method : clazz.getDeclaredMethods()) { 97 if (method.getName().equals(name) 98 && areParametersCompatible(method.getParameterTypes(), params)) { 99 return method; 100 } 101 } 102 } 103 throw new NoSuchMethodException("Method " + name + " with parameters " 104 + Arrays.asList(params) + " not found in " + clazz); 105 } 106 107 private static Constructor<?> findConstructor(Class<?> clazz, Object... params) 108 throws NoSuchMethodException { 109 for (Constructor<?> constructor : clazz.getDeclaredConstructors()) { 110 if (areParametersCompatible(constructor.getParameterTypes(), params)) { 111 return constructor; 112 } 113 } 114 throw new NoSuchMethodException("Constructor with parameters " + Arrays.asList(params) 115 + " not found in " + clazz); 116 } 117 118 private static boolean areParametersCompatible(Class<?>[] paramTypes, Object... params) { 119 if (params.length != paramTypes.length) { 120 return false; 121 } 122 for (int i = 0; i < params.length; i++) { 123 if (!isAssignableFrom(paramTypes[i], params[i])) { 124 return false; 125 } 126 } 127 return true; 128 } 129 130 private static boolean isAssignableFrom(Class<?> left, Object right) { 131 if (right == null) { 132 return !left.isPrimitive(); 133 } 134 Class<?> rightClazz = right.getClass(); 135 if (left.isPrimitive()) { 136 // TODO(agrieve): Fill in the rest as needed. 137 return left == boolean.class && rightClazz == Boolean.class 138 || left == int.class && rightClazz == Integer.class; 139 } 140 return left.isAssignableFrom(rightClazz); 141 } 142 } 143