Skip to content

Instantly share code, notes, and snippets.

@MrMaurice211
Created June 13, 2018 04:53
Show Gist options
  • Save MrMaurice211/a26c869a7da241b4d88fc4298fa58ce0 to your computer and use it in GitHub Desktop.
Save MrMaurice211/a26c869a7da241b4d88fc4298fa58ce0 to your computer and use it in GitHub Desktop.
A utility class for using Reflections in Java
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Reflections {
private Reflections() {}
private static String versionString;
private static Set<Clazz> clazzez = Sets.newCopyOnWriteArraySet();
private static Clazz getCache(Class<?> clazz) {
return clazzez.stream().filter(c -> c.clazz() == clazz).findFirst().orElse(null);
}
private static Clazz getCache(String clazzName) {
return clazzez.stream().filter(c -> c.clazz().getName().equals(clazzName)).findFirst().orElse(null);
}
private static String getVersion() {
if (versionString == null) {
String name = Bukkit.getServer().getClass().getPackage().getName();
versionString = name.substring(name.lastIndexOf('.') + 1) + ".";
}
return versionString;
}
public static Clazz getOBCClass(String clazz) {
return getClass("org.bukkit.craftbukkit." + getVersion() + clazz);
}
public static Clazz getNMSClass(String clazz) {
return getClass("net.minecraft.server." + getVersion() + clazz);
}
public static Clazz getClass(String clazzName) {
Clazz cc = Reflections.getCache(clazzName);
if (cc != null)
return cc;
try {
Class<?> clazz = Class.forName(clazzName);
Clazz cache = new Clazz(clazz);
clazzez.add(cache);
return cache;
} catch (Exception e) {
throw new Reflections.ReflectionException("Cannot find the class " + clazzName);
}
}
public static Object getConnection(Player player) {
Object entityPlayer = getMethod(player.getClass(), "getHandle").invoke(player);
return getField(entityPlayer.getClass(), "playerConnection").get(entityPlayer);
}
public static void sendPacket(Object packet, Player player) {
if (packet == null)
return;
Clazz packetClazz = getNMSClass("Packet");
Object conn = getConnection(player);
getMethod(conn.getClass(), "sendPacket", packetClazz.clazz()).invoke(conn, packet);
}
public static ConsInvoker getConstructor(Class<?> clazz, Class<?>... params) {
Predicate<Constructor<?>> filter = c -> Arrays.equals(params, c.getParameterTypes());
Clazz cache = getCache(clazz);
if (cache != null) {
Optional<ConsInvoker> ccs = cache.getCacheOf(ConsInvoker.class).filter(c -> c.isApplicable(filter))
.findFirst();
if (ccs.isPresent())
return ccs.get();
}
Optional<Constructor<?>> mt = Arrays.stream(clazz.getDeclaredConstructors()).filter(filter).findFirst();
if (mt.isPresent())
return new Reflections.ConsInvoker(mt.get());
if (clazz.getSuperclass() != null)
return getConstructor(clazz.getSuperclass(), params);
throw new Reflections.ReflectionException("Cannot find Constructor with parameters " + params);
}
public static MethodInvoker getMethod(String clazz, String methodName, Class<?>... params) {
if (clazz.startsWith("{obc}"))
return getOBCClass(clazz.split("\\.")[1]).getMethod(methodName, params);
if (clazz.startsWith("{nms}"))
return getNMSClass(clazz.split("\\.")[1]).getMethod(methodName, params);
return getMethod(getClass(clazz).clazz(), methodName, params);
}
public static MethodInvoker getMethod(Class<?> clazz, Predicate<Method> filter, Class<?>... params) {
if (params.length > 0)
filter = filter.and(m -> Arrays.equals(params, m.getParameterTypes()));
Predicate<Method> ff = filter;
Clazz cache = getCache(clazz);
if (cache != null) {
Optional<MethodInvoker> mi = cache.getCacheOf(MethodInvoker.class).filter(i -> i.isApplicable(ff))
.findFirst();
if (mi.isPresent())
return mi.get();
}
Optional<Method> mt = Arrays.stream(clazz.getDeclaredMethods()).filter(ff).findFirst();
if (mt.isPresent())
return new Reflections.MethodInvoker(mt.get());
if (clazz.getSuperclass() != null)
return getMethod(clazz.getSuperclass(), ff, params);
throw new Reflections.ReflectionException("Cannot find Method in Class " + clazz.getSimpleName());
}
public static MethodInvoker getMethod(Class<?> clazz, String methodName, Class<?>... params) {
return getMethod(clazz, m -> m.getName().equalsIgnoreCase(methodName), params);
}
public static MethodInvoker getMethod(Class<?> clazz, Class<?> returnType, Class<?>... params) {
if (Number.class.isAssignableFrom(returnType)) {
FieldAccessor f = getField(returnType, "TYPE");
Class<?> c = (Class<?>) f.get(null);
return getMethod(clazz, m -> c.isAssignableFrom(m.getReturnType()), params);
}
return getMethod(clazz, m -> m.getReturnType().isAssignableFrom(returnType), params);
}
public static FieldAccessor getField(String clazz, String fieldName) {
if (clazz.startsWith("{obc}"))
return getOBCClass(clazz.split("\\.")[1]).getField(fieldName);
if (clazz.startsWith("{nms}"))
return getNMSClass(clazz.split("\\.")[1]).getField(fieldName);
return getClass(clazz).getField(fieldName);
}
public static FieldAccessor getField(String clazz, Class<?> fieldType) {
if (clazz.startsWith("{obc}"))
return getOBCClass(clazz.split("\\.")[1]).getField(fieldType);
if (clazz.startsWith("{nms}"))
return getNMSClass(clazz.split("\\.")[1]).getField(fieldType);
return getClass(clazz).getField(fieldType);
}
public static FieldAccessor getField(Class<?> clazz, Predicate<Field> filter) {
Clazz cache = getCache(clazz);
if (cache != null) {
Optional<FieldAccessor> fa = cache.getCacheOf(FieldAccessor.class).filter(i -> i.isApplicable(filter))
.findFirst();
if (fa.isPresent())
return fa.get();
}
Optional<Field> fd = Arrays.stream(clazz.getDeclaredFields()).filter(filter).findFirst();
if (fd.isPresent())
return new Reflections.FieldAccessor(fd.get());
if (clazz.getSuperclass() != null)
return getField(clazz.getSuperclass(), filter);
throw new Reflections.ReflectionException("Cannot find Field in Class " + clazz.getSimpleName());
}
public static FieldAccessor getField(Class<?> clazz, String fieldName) {
return getField(clazz, f -> f.getName().equals(fieldName));
}
public static FieldAccessor getField(Class<?> clazz, Class<?> fieldType) {
return getField(clazz, f -> fieldType.isAssignableFrom(f.getType()));
}
public static <T extends Annotation> T getAnot(Class<?> clazz, Class<T> type) {
return getMethod(clazz, m -> m.getAnnotation(type) != null).getAnnot(type);
}
public static interface CachedObject<T> {
public boolean isApplicable(Predicate<T> filter);
}
public static class FieldAccessor implements CachedObject<Field> {
private Field f;
public FieldAccessor(Field field) {
f = field;
if (!f.isAccessible())
f.setAccessible(true);
}
public Object get(Object obj) {
try {
return f.get(obj);
} catch (Exception e) {
throw new Reflections.ReflectionException("Cannot get the value of field " + f.getName());
}
}
public void set(Object obj, Object value) {
try {
f.set(obj, value);
} catch (Exception e) {
throw new Reflections.ReflectionException("Cannot set the value of field " + f.getName());
}
}
@Override
public boolean isApplicable(Predicate<Field> filter) {
return filter.test(f);
}
}
public static class MethodInvoker implements CachedObject<Method> {
private Method m;
public MethodInvoker(Method method) {
m = method;
if (!m.isAccessible())
m.setAccessible(true);
}
public Object invoke(Object obj, Object... args) {
try {
return m.invoke(obj, args);
} catch (Exception e) {
throw new Reflections.ReflectionException("Cannot invoke the method " + m.getName());
}
}
public <T extends Annotation> T getAnnot(Class<T> anot) {
return m.getAnnotation(anot);
}
@Override
public boolean isApplicable(Predicate<Method> filter) {
return filter.test(m);
}
}
public static class ConsInvoker implements CachedObject<Constructor<?>> {
private Constructor<?> cons;
public ConsInvoker(Constructor<?> constructor) {
cons = constructor;
if (!cons.isAccessible())
cons.setAccessible(true);
}
public <T> T newInstance(Class<T> type, Object... args) {
return type.cast(newInstance(args));
}
public Object newInstance(Object... args) {
try {
return cons.newInstance(args);
} catch (Exception e) {
throw new Reflections.ReflectionException("Cannot start instance of " + cons.getName());
}
}
@Override
public boolean isApplicable(Predicate<Constructor<?>> filter) {
return filter.test(cons);
}
}
public static class Clazz {
private Class<?> parent;
private List<CachedObject<?>> objects;
public Clazz(Class<?> clazz) {
parent = clazz;
objects = Lists.newArrayList();
}
public ConsInvoker getConstructor(Class<?>... params) {
ConsInvoker invoker = Reflections.getConstructor(parent, params);
objects.add(invoker);
return invoker;
}
public MethodInvoker getMethod(String methodName, Class<?>... params) {
MethodInvoker invoker = Reflections.getMethod(parent, methodName, params);
objects.add(invoker);
return invoker;
}
public MethodInvoker getMethod(Class<?> returnType, Class<?>... params) {
MethodInvoker invoker = Reflections.getMethod(parent, returnType, params);
objects.add(invoker);
return invoker;
}
public FieldAccessor getField(String fieldName) {
FieldAccessor accessor = Reflections.getField(parent, fieldName);
objects.add(accessor);
return accessor;
}
public FieldAccessor getField(Class<?> fieldType) {
FieldAccessor accessor = Reflections.getField(parent, fieldType);
objects.add(accessor);
return accessor;
}
public <T extends Annotation> T getAnnotation(Class<T> annot) {
return Reflections.getAnot(parent, annot);
}
public <T extends CachedObject<?>> Stream<T> getCacheOf(Class<T> type) {
return objects.stream().filter(o -> o.getClass().isAssignableFrom(type)).map(type::cast);
}
public Class<?> clazz() {
return parent;
}
}
public static class ReflectionException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ReflectionException(String s) {
super(s);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment