package com.supaham.commons.utils;
import com.google.common.base.Preconditions;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nonnull;
public class ReflectionUtils {
public static List<Method> getUniqueDeclaredMethods(@Nonnull Class<?> clazz) {
return getUniqueDeclaredMethods(clazz, Collections.emptyList());
}
public static List<Method> getUniqueDeclaredMethods(@Nonnull Class<?> clazz,
@Nonnull Collection ignoreClasses) {
Preconditions.checkNotNull(clazz, "class cannot be null.");
Preconditions.checkNotNull(ignoreClasses, "ignoreClasses cannot be null.");
List<Method> result = new ArrayList<>();
getUniqueDeclaredMethods(clazz, ignoreClasses, result);
return result;
}
private static void getUniqueDeclaredMethods(@Nonnull Class<?> clazz,
@Nonnull Collection<Class<?>> ignoreClasses,
@Nonnull List<Method> result) {
Preconditions.checkNotNull(clazz, "class cannot be null.");
Preconditions.checkNotNull(ignoreClasses, "ignoreClasses cannot be null.");
Preconditions.checkNotNull(result, "result cannot be null.");
for (Method method : clazz.getDeclaredMethods()) {
boolean found = false;
for (Method existing : result) {
// find overridden methods by only checking name and parameters
// using equals() checks return type too, we don't care about that.
if (sameMethodSignature(method, existing)) {
found = true;
break;
}
}
if (!found) {
result.add(method);
}
}
if (clazz.getSuperclass() != null) {
if (!ignoreClasses.contains(clazz.getSuperclass())) {
getUniqueDeclaredMethods(clazz.getSuperclass(), ignoreClasses, result);
}
} else if (clazz.isInterface()) {
for (Class<?> interfaceClazz : clazz.getInterfaces()) {
if (!ignoreClasses.contains(interfaceClazz)) {
getUniqueDeclaredMethods(interfaceClazz, ignoreClasses, result);
}
}
}
}
/**
* Returns whether two {@link Method}s have similar signature. Firstly, {@code m1} and {@code m2}
* are checked for null pointers, if true, {@code true} is returned. Otherwise, if either one is
* null {@code false} is returned. Then, both methods are checked for the same name, if false,
* false is returned. Lastly, both methods are checked for same exact parameter types, if true,
* and only then, {@code true} is returned.
*
* @param m1 method 1 to check
* @param m2 method 2 to check
*
* @return whether {@code m1} and {@code m2} are the same
*/
public static boolean sameMethodSignature(Method m1, Method m2) {
if (m1 == null && m2 == null) {
return true;
} else if (m1 == null || m2 == null) {
return false;
} else {
if (m1.getName().equals(m2.getName()) && m1.getParameterCount() == m2.getParameterCount()) {
return Arrays.equals(m1.getParameterTypes(), m2.getParameterTypes());
}
return false;
}
}
public static Field getField(Class<?> clazz, String name) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static Method getMethod(Class<?> clazz, String name,
Class<?>... args) {
for (Method m : clazz.getDeclaredMethods()) {
if (m.getName().equals(name) && (args.length == 0 || ReflectionUtils.ClassListEqual(args,
m.getParameterTypes()))) {
m.setAccessible(true);
return m;
}
}
return null;
}
public static boolean ClassListEqual(Class<?>[] l1, Class<?>[] l2) {
boolean equal = true;
if (l1.length != l2.length) {
return false;
}
for (int i = 0; i < l1.length; i++) {
if (l1[i] != l2[i]) {
equal = false;
break;
}
}
return equal;
}
}