1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
| class Reflect {
/** Invocation d'une méthode static */
public static Object invokeStatic(Class<?> type, String staticMethodName,
Object... args) throws Throwable {
return invoke(null, type, staticMethodName, args);
}
/** Invocation d'une méthode d'instance */
public static Object invoke(Object instance, String methodName,
Object... args) throws Throwable {
return invoke(instance, instance.getClass(), methodName, args);
}
/** Invocation d'une méthode */
private static Object invoke(Object instance, Class<?> type,
String methodName, Object[] args)
throws ReflectiveOperationException {
Objects.requireNonNull(type);
Objects.requireNonNull(methodName);
Objects.requireNonNull(args);
final boolean isStaticCall = (instance == null);
for (Method method : type.getMethods()) {
if (matchArgs(method, methodName, isStaticCall, args)) {
return method.invoke(instance, buildArgs(method, args));
}
}
throw new ReflectiveOperationException("no match");
}
/** Vérifie si la méthode correspond à notre instruction */
private static boolean matchArgs(Method method, String methodName,
boolean isStaticCall, Object[] args) {
// La méthode doit avoir le même nom :
if (methodName.equals(method.getName()) == false) {
return false;
}
// On ne mélange pas les méthodes static/non-static
if (isStaticCall != Modifier.isStatic(method.getModifiers())) {
return false;
}
final boolean isVarArgs = method.isVarArgs();
final int paramCount = isVarArgs ? method.getParameterCount() - 1
: method.getParameterCount();
if (isVarArgs) {
// Le nombre de paramètre ne doit pas être inférieur :
if (args.length < paramCount) {
return false;
}
} else {
// Le nombre de paramètre doit correspondre
if (args.length != paramCount) {
return false;
}
}
Class<?>[] types = method.getParameterTypes();
// Tous les paramètres doivent correspondre :
for (int i = 0; i < paramCount; i++) {
if (isAssignable(types[i], args[i]) == false) {
return false;
}
}
if (isVarArgs) {
// Pour les varArgs il faut vérifier
// le type des paramètres supplémentaires de l'ellipse :
Class<?> varArgType = types[paramCount].getComponentType();
for (int i = paramCount; i < args.length; i++) {
if (isAssignable(varArgType, args[i]) == false) {
return false;
}
}
}
return true;
}
/** Construction d'un tableau d'argument pour invoke() */
private static Object[] buildArgs(Method method, Object[] args) {
if (method.isVarArgs()) {
// On récupère le nombre de paramètre "standard" :
final int paramCount = method.getParameterCount() - 1;
// On récupère le type de base de l'ellipse
Class<?> componentType = method.getParameterTypes()[paramCount].getComponentType();
// On crée un tableau permettant de stocker les éléments de l'ellipse :
Object array = Array.newInstance(componentType, args.length - paramCount);
for (int i=paramCount; i<args.length; i++) {
// Et on affecte chaque valeur :
arraySet(componentType, array, i-paramCount, args[i]);
}
// On copie le tableau de paramètre, à la bonne taille :
Object[] varArgs = Arrays.copyOf(args, paramCount + 1);
// Et on stocke l'ellipse dans le dernier emplacement :
varArgs[paramCount] = array;
return varArgs;
}
return args;
}
/** Affecte un élément dans l'index du tableau, selon son type */
private static void arraySet(Class<?> componentType, Object array,
int index, Object value) {
if (componentType.isPrimitive()) {
if (componentType == boolean.class)
Array.setBoolean(array, index, ((Boolean) value).booleanValue());
else if (componentType == byte.class)
Array.setByte(array, index, ((Byte) value).byteValue());
else if (componentType == char.class)
Array.setChar(array, index, ((Character) value).charValue());
else if (componentType == short.class)
Array.setShort(array, index, ((Short) value).shortValue());
else if (componentType == int.class)
Array.setInt(array, index, ((Integer) value).intValue());
else if (componentType == long.class)
Array.setLong(array, index, ((Long) value).longValue());
else if (componentType == float.class)
Array.setFloat(array, index, ((Float) value).floatValue());
else if (componentType == double.class)
Array.setDouble(array, index, ((Double) value).doubleValue());
else
throw new IllegalStateException();
} else {
Array.set(array, index, value);
}
}
/** Retourne le type 'boxed' si primitif */
private static Class<?> boxedType(Class<?> type) {
if (type.isPrimitive()) {
if (type == boolean.class)
return Integer.class;
if (type == byte.class)
return Byte.class;
if (type == char.class)
return Character.class;
if (type == short.class)
return Short.class;
if (type == int.class)
return Integer.class;
if (type == long.class)
return Long.class;
if (type == float.class)
return Float.class;
if (type == double.class)
return Double.class;
throw new IllegalStateException();
}
return type;
}
/** Vérifie si une instance peut être assignable à un type. */
public static boolean isAssignable(Class<?> type, Object instance) {
if (instance == null) {
// Les valeurs null ne s'appliquent pas aux primitives
return type.isPrimitive() == false;
}
return boxedType(type).isInstance(instance);
}
} |
Partager