mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-06-26 06:44:42 +02:00
* don't enforce async calls for thread-safe listeners (closes #1551) * cleanups, remove structure compiling * improve cloning a bit * fix small issue in no-op structure modifier * remove last usages of FieldUtils * improve and fix equality check in container test
214 lines
7.1 KiB
Java
214 lines
7.1 KiB
Java
package com.comphenix.protocol.reflect;
|
|
|
|
import com.google.common.base.Joiner;
|
|
import com.google.common.base.Preconditions;
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Modifier;
|
|
import java.util.Arrays;
|
|
|
|
public class ExactReflection {
|
|
|
|
private static final Joiner COMMA_SEPERATED_JOINER = Joiner.on(", ");
|
|
|
|
// The class we're actually representing
|
|
private final Class<?> source;
|
|
private final boolean forceAccess;
|
|
|
|
private ExactReflection(Class<?> source, boolean forceAccess) {
|
|
this.source = Preconditions.checkNotNull(source, "source class cannot be NULL");
|
|
this.forceAccess = forceAccess;
|
|
}
|
|
|
|
/**
|
|
* Retrieves an exact reflection instance from a given class.
|
|
*
|
|
* @param source - the class we'll use.
|
|
* @param forceAccess - whether to also search for members which are out of the allowed scope.
|
|
* @return A fuzzy reflection instance.
|
|
*/
|
|
public static ExactReflection fromClass(Class<?> source, boolean forceAccess) {
|
|
return new ExactReflection(source, forceAccess);
|
|
}
|
|
|
|
/**
|
|
* Retrieves an exact reflection instance from an object.
|
|
*
|
|
* @param reference - the object we'll use.
|
|
* @param forceAccess - whether to also search for members which are out of the allowed scope.
|
|
* @return A fuzzy reflection instance that uses the class of the given object.
|
|
*/
|
|
public static ExactReflection fromObject(Object reference, boolean forceAccess) {
|
|
return new ExactReflection(reference.getClass(), forceAccess);
|
|
}
|
|
|
|
/**
|
|
* Retrieve the first method in the class hierarchy with the given name and parameters.
|
|
* <p>
|
|
* If {@link #isForceAccess()} is TRUE, we will also search for methods which are out of the caller scope.
|
|
*
|
|
* @param methodName - the name of the method to find, NULL to only search by using the given parameters.
|
|
* @param parameters - the parameters of the method to find.
|
|
* @return the first matching method.
|
|
* @throws IllegalArgumentException if there is no method with the given name and parameter types.
|
|
*/
|
|
public Method getMethod(String methodName, Class<?>... parameters) {
|
|
Method method = this.lookupMethod(this.source, methodName, parameters);
|
|
if (method == null) {
|
|
throw new IllegalArgumentException(String.format(
|
|
"Unable to find method %s(%s) in %s",
|
|
methodName,
|
|
COMMA_SEPERATED_JOINER.join(parameters),
|
|
this.source.getName()));
|
|
}
|
|
|
|
return method;
|
|
}
|
|
|
|
/**
|
|
* Finds the first method in the class hierarchy with the given name and parameters.
|
|
* <p>
|
|
* If {@link #isForceAccess()} is TRUE, we will also search for methods which are out of the caller scope.
|
|
*
|
|
* @param methodName - the name of the method to find, NULL to only search by using the given parameters.
|
|
* @param parameters - the parameters of the method to find.
|
|
* @return the first matching method, NULL if no method matches.
|
|
*/
|
|
public Method findMethod(String methodName, Class<?>... parameters) {
|
|
return this.lookupMethod(this.source, methodName, parameters);
|
|
}
|
|
|
|
// For recursion
|
|
private Method lookupMethod(Class<?> instanceClass, String methodName, Class<?>... parameters) {
|
|
for (Method method : instanceClass.getDeclaredMethods()) {
|
|
if ((this.forceAccess || Modifier.isPublic(method.getModifiers()))
|
|
&& (methodName == null || method.getName().equals(methodName))
|
|
&& Arrays.equals(method.getParameterTypes(), parameters)) {
|
|
return method;
|
|
}
|
|
}
|
|
|
|
// Search in every superclass
|
|
if (instanceClass.getSuperclass() != null) {
|
|
return this.lookupMethod(instanceClass.getSuperclass(), methodName, parameters);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Retrieve a field in the class hierarchy by the given name.
|
|
* <p>
|
|
* If {@link #isForceAccess()} is TRUE, we will also search for fields which are out of the caller scope.
|
|
*
|
|
* @param fieldName - the name of the field to find.
|
|
* @return the first matching field.
|
|
* @throws IllegalArgumentException if no field with the given name was found.
|
|
*/
|
|
public Field getField(String fieldName) {
|
|
Field field = this.lookupField(this.source, fieldName);
|
|
if (field == null) {
|
|
throw new IllegalArgumentException(String.format(
|
|
"Unable to find field with name %s in %s.",
|
|
fieldName,
|
|
this.source.getName()));
|
|
}
|
|
|
|
return field;
|
|
}
|
|
|
|
/**
|
|
* Finds a field in the class hierarchy by the given name.
|
|
* <p>
|
|
* If {@link #isForceAccess()} is TRUE, we will also search for fields which are out of the caller scope.
|
|
*
|
|
* @param fieldName - the name of the field to find.
|
|
* @return the first matching field, null if no field matches.
|
|
*/
|
|
public Field findField(String fieldName) {
|
|
return this.lookupField(this.source, fieldName);
|
|
}
|
|
|
|
// For recursion
|
|
private Field lookupField(Class<?> instanceClass, String fieldName) {
|
|
for (Field field : instanceClass.getDeclaredFields()) {
|
|
if ((this.forceAccess || Modifier.isPublic(field.getModifiers())) && field.getName().equals(fieldName)) {
|
|
return field;
|
|
}
|
|
}
|
|
|
|
// Recursively find the correct field
|
|
if (instanceClass.getSuperclass() != null) {
|
|
return this.lookupField(instanceClass.getSuperclass(), fieldName);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the first constructor in the class hierarchy with the given parameters.
|
|
* <p>
|
|
* If {@link #isForceAccess()} is TRUE, we will also search for constructors which are out of the caller scope.
|
|
*
|
|
* @param parameters - the parameters of the constructor to find.
|
|
* @return the first matching constructor.
|
|
* @throws IllegalArgumentException if no constructor with the given parameters was found.
|
|
*/
|
|
public Constructor<?> getConstructor(Class<?>... parameters) {
|
|
Constructor<?> constructor = this.findConstructor(parameters);
|
|
if (constructor == null) {
|
|
throw new IllegalArgumentException(String.format(
|
|
"Unable to find constructor (%s) in %s",
|
|
COMMA_SEPERATED_JOINER.join(parameters),
|
|
this.source.getName()));
|
|
}
|
|
|
|
return constructor;
|
|
}
|
|
|
|
/**
|
|
* Finds the first constructor in the class hierarchy with the given parameters.
|
|
* <p>
|
|
* If {@link #isForceAccess()} is TRUE, we will also search for constructors which are out of the caller scope.
|
|
*
|
|
* @param parameters - the parameters of the constructor to find.
|
|
* @return the first matching constructor, NULL if no constructor matches.
|
|
*/
|
|
public Constructor<?> findConstructor(Class<?>... parameters) {
|
|
try {
|
|
Constructor<?> constructor = this.source.getDeclaredConstructor(parameters);
|
|
return this.forceAccess || Modifier.isPublic(constructor.getModifiers()) ? constructor : null;
|
|
} catch (NoSuchMethodException exception) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve an {@link ExactReflection} object where scope restrictions are ignored.
|
|
*
|
|
* @return A copy of the current object.
|
|
*/
|
|
public ExactReflection forceAccess() {
|
|
return new ExactReflection(this.source, true);
|
|
}
|
|
|
|
/**
|
|
* Determine if we are overriding scope restrictions and will also find private, protected or package members.
|
|
*
|
|
* @return TRUE if we are, FALSE otherwise.
|
|
*/
|
|
public boolean isForceAccess() {
|
|
return this.forceAccess;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the source class we are searching.
|
|
*
|
|
* @return The source.
|
|
*/
|
|
public Class<?> getSource() {
|
|
return this.source;
|
|
}
|
|
}
|