Created
June 13, 2018 04:53
-
-
Save MrMaurice211/a26c869a7da241b4d88fc4298fa58ce0 to your computer and use it in GitHub Desktop.
A utility class for using Reflections in Java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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