package net.mcft.copy.backpacks.misc.util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
public final class ReflectUtils {
private ReflectUtils() { }
// Fields
private static Map<Pair<Class<?>, String>, Field> _cachedFields =
new HashMap<Pair<Class<?>, String>, Field>();
/** Returns a field on the specified class with the specified SRG
* or MCP names. The field is cached, speeding up future calls. */
public static Field findField(Class<?> clazz, String mcpName, String srgName) {
return _cachedFields.computeIfAbsent(Pair.of(clazz, mcpName), key -> {
Field field = null;
for (String name : new String[]{ srgName, mcpName }) {
// Attempt to find the field on the class.
try { field = clazz.getDeclaredField(name); }
catch (NoSuchFieldException ex) { }
// If not found, try next name or exit the loop.
if (field == null) continue;
// Set field to be accessible, allowing its value to be get/set.
try { field.setAccessible(true); }
catch (SecurityException ex) { throw new RuntimeException(ex); }
return field;
}
throw new RuntimeException(String.format("Could not find field '%s'/'%s' for class %s",
mcpName, srgName, clazz.getName()));
});
}
/** Gets the value of the field with the specified SRG or MCP names
* on the specified class and instance (may be null if static). */
@SuppressWarnings("unchecked")
public static <T, V> V get(Class<? extends T> clazz, T instance, String mcpName, String srgName) {
try { return (V)findField(clazz, mcpName, srgName).get(instance); }
catch (ReflectiveOperationException ex) { throw new RuntimeException(ex); }
}
/** Gets the value of the static field with the specified SRG or MCP names on the specified class. */
public static <T, V> V get(Class<T> clazz, String mcpName, String srgName) {
return get(clazz, null, mcpName, srgName);
}
/** Gets the value of the field with the specified SRG or MCP names on the specified instance. */
public static <T, V> V get(T instance, String mcpName, String srgName) {
return get(instance.getClass(), instance, mcpName, srgName);
}
/** Sets the value of the field with the specified SRG or MCP names
* on the specified class and instance (may be null if static). */
public static <T, V> void set(Class<? extends T> clazz, T instance, String mcpName, String srgName, V value) {
try { findField(clazz, mcpName, srgName).set(instance, value); }
catch (ReflectiveOperationException ex) { throw new RuntimeException(ex); }
}
/** Sets the value of the static field with the specified SRG or MCP names on the specified class. */
public static <T, V> void set(Class<T> clazz, String mcpName, String srgName, V value) {
set(clazz, null, mcpName, srgName, value);
}
/** Sets the value of the field with the specified SRG or MCP names on the specified instance. */
public static <T, V> void set(T instance, String mcpName, String srgName, V value) {
set(instance.getClass(), instance, mcpName, srgName, value);
}
// Methods
private static Map<Triple<Class<?>, String, Class<?>[]>, Method> _cachedMethods =
new HashMap<Triple<Class<?>, String, Class<?>[]>, Method>();
/** Returns a method on the specified class with the specified SRG
* or MCP names. The method is cached, speeding up future calls. */
public static Method findMethod(Class<?> clazz, String mcpName, String srgName, Class<?>... parameterTypes) {
return _cachedMethods.computeIfAbsent(Triple.of(clazz, mcpName, parameterTypes), key -> {
Method method;
for (String name : new String[]{ srgName, mcpName }) {
// Attempt to find the method on the class.
try { method = clazz.getDeclaredMethod(name, parameterTypes); }
catch (NoSuchMethodException ex) { continue; }
// If not found, try next name or exit the loop.
if (method == null) continue;
// Set method to be accessible, allowing it to be called.
try { method.setAccessible(true); }
catch (SecurityException ex) { throw new RuntimeException(ex); }
return method;
}
throw new RuntimeException(String.format("Could not find method '%s'/'%s' for class %s",
mcpName, srgName, clazz.getName()));
});
}
// TODO: Add invoke helper methods?
}