2012-10-16 07:28:54 +02:00
|
|
|
/*
|
|
|
|
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
|
|
|
* Copyright (C) 2012 Kristian S. Stangeland
|
|
|
|
*
|
2015-06-17 20:25:39 +02:00
|
|
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
|
|
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
2012-10-16 07:28:54 +02:00
|
|
|
* the License, or (at your option) any later version.
|
|
|
|
*
|
2015-06-17 20:25:39 +02:00
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
2012-10-16 07:28:54 +02:00
|
|
|
* See the GNU General Public License for more details.
|
|
|
|
*
|
2015-06-17 20:25:39 +02:00
|
|
|
* You should have received a copy of the GNU General Public License along with this program;
|
|
|
|
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
2012-10-16 07:28:54 +02:00
|
|
|
* 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
package com.comphenix.protocol.reflect;
|
|
|
|
|
2017-05-30 04:14:08 +02:00
|
|
|
import java.lang.reflect.*;
|
2012-10-16 07:28:54 +02:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.LinkedHashSet;
|
|
|
|
import java.util.List;
|
2013-03-04 00:44:09 +01:00
|
|
|
import java.util.Map;
|
2012-10-16 07:28:54 +02:00
|
|
|
import java.util.Set;
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
2016-05-16 22:27:05 +02:00
|
|
|
import org.apache.commons.lang.Validate;
|
|
|
|
|
2013-12-09 12:03:30 +01:00
|
|
|
import com.comphenix.protocol.reflect.accessors.Accessors;
|
2013-02-03 02:07:23 +01:00
|
|
|
import com.comphenix.protocol.reflect.fuzzy.AbstractFuzzyMatcher;
|
2013-12-05 03:54:17 +01:00
|
|
|
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
2013-01-30 05:08:38 +01:00
|
|
|
import com.google.common.collect.Lists;
|
2013-03-04 00:44:09 +01:00
|
|
|
import com.google.common.collect.Maps;
|
2013-12-07 19:52:02 +01:00
|
|
|
import com.google.common.collect.Sets;
|
2013-01-30 05:08:38 +01:00
|
|
|
|
2012-10-16 07:28:54 +02:00
|
|
|
/**
|
|
|
|
* Retrieves fields and methods by signature, not just name.
|
|
|
|
*
|
|
|
|
* @author Kristian
|
|
|
|
*/
|
|
|
|
public class FuzzyReflection {
|
|
|
|
// The class we're actually representing
|
|
|
|
private Class<?> source;
|
|
|
|
|
|
|
|
// Whether or not to lookup private members
|
|
|
|
private boolean forceAccess;
|
|
|
|
|
|
|
|
public FuzzyReflection(Class<?> source, boolean forceAccess) {
|
|
|
|
this.source = source;
|
|
|
|
this.forceAccess = forceAccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a fuzzy reflection instance from a given class.
|
|
|
|
* @param source - the class we'll use.
|
|
|
|
* @return A fuzzy reflection instance.
|
|
|
|
*/
|
|
|
|
public static FuzzyReflection fromClass(Class<?> source) {
|
|
|
|
return fromClass(source, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a fuzzy reflection instance from a given class.
|
|
|
|
* @param source - the class we'll use.
|
|
|
|
* @param forceAccess - whether or not to override scope restrictions.
|
|
|
|
* @return A fuzzy reflection instance.
|
|
|
|
*/
|
|
|
|
public static FuzzyReflection fromClass(Class<?> source, boolean forceAccess) {
|
|
|
|
return new FuzzyReflection(source, forceAccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a fuzzy reflection instance from an object.
|
|
|
|
* @param reference - the object we'll use.
|
|
|
|
* @return A fuzzy reflection instance that uses the class of the given object.
|
|
|
|
*/
|
|
|
|
public static FuzzyReflection fromObject(Object reference) {
|
|
|
|
return new FuzzyReflection(reference.getClass(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a fuzzy reflection instance from an object.
|
|
|
|
* @param reference - the object we'll use.
|
|
|
|
* @param forceAccess - whether or not to override scope restrictions.
|
|
|
|
* @return A fuzzy reflection instance that uses the class of the given object.
|
|
|
|
*/
|
|
|
|
public static FuzzyReflection fromObject(Object reference, boolean forceAccess) {
|
|
|
|
return new FuzzyReflection(reference.getClass(), forceAccess);
|
|
|
|
}
|
|
|
|
|
2013-12-04 04:17:02 +01:00
|
|
|
/**
|
|
|
|
* Retrieve the value of the first field of the given type.
|
2015-06-17 20:25:39 +02:00
|
|
|
* @param <T> Type
|
2013-12-04 04:17:02 +01:00
|
|
|
* @param instance - the instance to retrieve from.
|
|
|
|
* @param fieldClass - type of the field to retrieve.
|
|
|
|
* @param forceAccess - whether or not to look for private and protected fields.
|
|
|
|
* @return The value of that field.
|
|
|
|
* @throws IllegalArgumentException If the field cannot be found.
|
|
|
|
*/
|
|
|
|
public static <T> T getFieldValue(Object instance, Class<T> fieldClass, boolean forceAccess) {
|
|
|
|
@SuppressWarnings("unchecked")
|
2013-12-09 12:03:30 +01:00
|
|
|
T result = (T) Accessors.getFieldAccessor(instance.getClass(), fieldClass, forceAccess).get(instance);
|
2013-12-04 04:17:02 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-10-16 07:28:54 +02:00
|
|
|
/**
|
|
|
|
* Retrieves the underlying class.
|
2015-06-17 20:25:39 +02:00
|
|
|
* @return The underlying class.
|
2012-10-16 07:28:54 +02:00
|
|
|
*/
|
|
|
|
public Class<?> getSource() {
|
|
|
|
return source;
|
|
|
|
}
|
2013-12-04 04:17:02 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve the singleton instance of a class, from a method or field.
|
|
|
|
* @return The singleton instance.
|
|
|
|
* @throws IllegalStateException If the class has no singleton.
|
|
|
|
*/
|
2015-06-17 20:25:39 +02:00
|
|
|
public Object getSingleton() {
|
2013-12-04 04:17:02 +01:00
|
|
|
Method method = null;
|
|
|
|
Field field = null;
|
|
|
|
|
|
|
|
try {
|
2013-12-05 03:54:17 +01:00
|
|
|
method = getMethod(
|
|
|
|
FuzzyMethodContract.newBuilder().
|
|
|
|
parameterCount(0).
|
|
|
|
returnDerivedOf(source).
|
|
|
|
requireModifier(Modifier.STATIC).
|
|
|
|
build()
|
|
|
|
);
|
2013-12-04 04:17:02 +01:00
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
// Try getting the field instead
|
|
|
|
// Note that this will throw an exception if not found
|
2014-04-12 23:56:11 +02:00
|
|
|
field = getFieldByType("instance", source);
|
2013-12-04 04:17:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Convert into unchecked exceptions
|
|
|
|
if (method != null) {
|
|
|
|
try {
|
|
|
|
method.setAccessible(true);
|
|
|
|
return method.invoke(null);
|
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RuntimeException("Cannot invoke singleton method " + method, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (field != null) {
|
|
|
|
try {
|
|
|
|
field.setAccessible(true);
|
|
|
|
return field.get(null);
|
|
|
|
} catch (Exception e) {
|
|
|
|
throw new IllegalArgumentException("Cannot get content of singleton field " + field, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// We should never get to this point
|
|
|
|
throw new IllegalStateException("Impossible.");
|
|
|
|
}
|
2012-10-16 07:28:54 +02:00
|
|
|
|
2013-01-30 05:08:38 +01:00
|
|
|
/**
|
|
|
|
* Retrieve the first method that matches.
|
|
|
|
* <p>
|
|
|
|
* ForceAccess must be TRUE in order for this method to access private, protected and package level method.
|
|
|
|
* @param matcher - the matcher to use.
|
|
|
|
* @return The first method that satisfies the given matcher.
|
|
|
|
* @throws IllegalArgumentException If the method cannot be found.
|
|
|
|
*/
|
|
|
|
public Method getMethod(AbstractFuzzyMatcher<MethodInfo> matcher) {
|
|
|
|
List<Method> result = getMethodList(matcher);
|
2016-03-20 18:35:59 +01:00
|
|
|
|
|
|
|
if (result.size() > 0) {
|
2013-01-30 05:08:38 +01:00
|
|
|
return result.get(0);
|
2016-03-20 18:35:59 +01:00
|
|
|
} else {
|
2013-01-30 05:08:38 +01:00
|
|
|
throw new IllegalArgumentException("Unable to find a method that matches " + matcher);
|
2016-03-20 18:35:59 +01:00
|
|
|
}
|
2013-01-30 05:08:38 +01:00
|
|
|
}
|
2016-03-20 18:35:59 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve a method that matches. If there are multiple methods that match, the first one with the preferred
|
|
|
|
* name is selected.
|
|
|
|
* <p>
|
|
|
|
* ForceAccess must be TRUE in order for this method to access private, protected and package level method.
|
|
|
|
* @param matcher - the matcher to use.
|
|
|
|
* @param preferred - the preferred name.
|
|
|
|
* @return The first method that satisfies the given matcher.
|
|
|
|
* @throws IllegalArgumentException If the method cannot be found.
|
|
|
|
*/
|
|
|
|
public Method getMethod(AbstractFuzzyMatcher<MethodInfo> matcher, String preferred) {
|
|
|
|
List<Method> result = getMethodList(matcher);
|
|
|
|
|
|
|
|
if (result.size() > 1) {
|
|
|
|
for (Method method : result) {
|
|
|
|
if (method.getName().equals(preferred)) {
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result.size() > 0) {
|
|
|
|
return result.get(0);
|
|
|
|
} else {
|
|
|
|
throw new IllegalArgumentException("Unable to find a method that matches " + matcher);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-30 05:08:38 +01:00
|
|
|
/**
|
|
|
|
* Retrieve a list of every method that matches the given matcher.
|
|
|
|
* <p>
|
|
|
|
* ForceAccess must be TRUE in order for this method to access private, protected and package level methods.
|
|
|
|
* @param matcher - the matcher to apply.
|
|
|
|
* @return List of found methods.
|
|
|
|
*/
|
|
|
|
public List<Method> getMethodList(AbstractFuzzyMatcher<MethodInfo> matcher) {
|
|
|
|
List<Method> methods = Lists.newArrayList();
|
|
|
|
|
|
|
|
// Add all matching fields to the list
|
|
|
|
for (Method method : getMethods()) {
|
2013-01-30 18:44:13 +01:00
|
|
|
if (matcher.isMatch(MethodInfo.fromMethod(method), source)) {
|
2013-01-30 05:08:38 +01:00
|
|
|
methods.add(method);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return methods;
|
|
|
|
}
|
|
|
|
|
2012-10-16 07:28:54 +02:00
|
|
|
/**
|
|
|
|
* Retrieves a method by looking at its name.
|
|
|
|
* @param nameRegex - regular expression that will match method names.
|
|
|
|
* @return The first method that satisfies the regular expression.
|
|
|
|
* @throws IllegalArgumentException If the method cannot be found.
|
|
|
|
*/
|
|
|
|
public Method getMethodByName(String nameRegex) {
|
|
|
|
Pattern match = Pattern.compile(nameRegex);
|
|
|
|
|
|
|
|
for (Method method : getMethods()) {
|
|
|
|
if (match.matcher(method.getName()).matches()) {
|
2015-06-17 20:25:39 +02:00
|
|
|
// Right - this is probably it.
|
2012-10-16 07:28:54 +02:00
|
|
|
return method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-17 20:25:39 +02:00
|
|
|
throw new IllegalArgumentException("Unable to find a method with the pattern " +
|
2012-10-16 07:28:54 +02:00
|
|
|
nameRegex + " in " + source.getName());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a method by looking at the parameter types only.
|
|
|
|
* @param name - potential name of the method. Only used by the error mechanism.
|
|
|
|
* @param args - parameter types of the method to find.
|
|
|
|
* @return The first method that satisfies the parameter types.
|
|
|
|
* @throws IllegalArgumentException If the method cannot be found.
|
|
|
|
*/
|
|
|
|
public Method getMethodByParameters(String name, Class<?>... args) {
|
|
|
|
// Find the correct method to call
|
|
|
|
for (Method method : getMethods()) {
|
|
|
|
if (Arrays.equals(method.getParameterTypes(), args)) {
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// That sucks
|
|
|
|
throw new IllegalArgumentException("Unable to find " + name + " in " + source.getName());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a method by looking at the parameter types and return type only.
|
|
|
|
* @param name - potential name of the method. Only used by the error mechanism.
|
|
|
|
* @param returnType - return type of the method to find.
|
|
|
|
* @param args - parameter types of the method to find.
|
|
|
|
* @return The first method that satisfies the parameter types.
|
|
|
|
* @throws IllegalArgumentException If the method cannot be found.
|
|
|
|
*/
|
|
|
|
public Method getMethodByParameters(String name, Class<?> returnType, Class<?>[] args) {
|
|
|
|
// Find the correct method to call
|
|
|
|
List<Method> methods = getMethodListByParameters(returnType, args);
|
|
|
|
|
|
|
|
if (methods.size() > 0) {
|
|
|
|
return methods.get(0);
|
|
|
|
} else {
|
|
|
|
// That sucks
|
|
|
|
throw new IllegalArgumentException("Unable to find " + name + " in " + source.getName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a method by looking at the parameter types and return type only.
|
|
|
|
* @param name - potential name of the method. Only used by the error mechanism.
|
|
|
|
* @param returnTypeRegex - regular expression matching the return type of the method to find.
|
|
|
|
* @param argsRegex - regular expressions of the matching parameter types.
|
|
|
|
* @return The first method that satisfies the parameter types.
|
|
|
|
* @throws IllegalArgumentException If the method cannot be found.
|
|
|
|
*/
|
|
|
|
public Method getMethodByParameters(String name, String returnTypeRegex, String[] argsRegex) {
|
|
|
|
Pattern match = Pattern.compile(returnTypeRegex);
|
|
|
|
Pattern[] argMatch = new Pattern[argsRegex.length];
|
|
|
|
|
|
|
|
for (int i = 0; i < argsRegex.length; i++) {
|
|
|
|
argMatch[i] = Pattern.compile(argsRegex[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the correct method to call
|
|
|
|
for (Method method : getMethods()) {
|
|
|
|
if (match.matcher(method.getReturnType().getName()).matches()) {
|
|
|
|
if (matchParameters(argMatch, method.getParameterTypes()))
|
|
|
|
return method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// That sucks
|
|
|
|
throw new IllegalArgumentException("Unable to find " + name + " in " + source.getName());
|
|
|
|
}
|
|
|
|
|
2013-12-14 04:05:12 +01:00
|
|
|
/**
|
|
|
|
* Invoke a method by return type and parameters alone.
|
|
|
|
* <p>
|
|
|
|
* The parameters must be non-null for this to work.
|
|
|
|
* @param target - the instance.
|
|
|
|
* @param name - the name of the method - for debugging.
|
|
|
|
* @param returnType - the expected return type.
|
|
|
|
* @param parameters - the parameters.
|
|
|
|
* @return The return value, or NULL.
|
|
|
|
*/
|
|
|
|
public Object invokeMethod(Object target, String name, Class<?> returnType, Object... parameters) {
|
|
|
|
Class<?>[] types = new Class<?>[parameters.length];
|
|
|
|
|
|
|
|
for (int i = 0; i < types.length; i++) {
|
|
|
|
types[i] = parameters[i].getClass();
|
|
|
|
}
|
|
|
|
return Accessors.getMethodAccessor(getMethodByParameters(name, returnType, types)).
|
|
|
|
invoke(target, parameters);
|
|
|
|
}
|
|
|
|
|
2012-10-16 07:28:54 +02:00
|
|
|
private boolean matchParameters(Pattern[] parameterMatchers, Class<?>[] argTypes) {
|
|
|
|
if (parameterMatchers.length != argTypes.length)
|
|
|
|
throw new IllegalArgumentException("Arrays must have the same cardinality.");
|
|
|
|
|
|
|
|
// Check types against the regular expressions
|
|
|
|
for (int i = 0; i < argTypes.length; i++) {
|
|
|
|
if (!parameterMatchers[i].matcher(argTypes[i].getName()).matches())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves every method that has the given parameter types and return type.
|
|
|
|
* @param returnType - return type of the method to find.
|
|
|
|
* @param args - parameter types of the method to find.
|
|
|
|
* @return Every method that satisfies the given constraints.
|
|
|
|
*/
|
|
|
|
public List<Method> getMethodListByParameters(Class<?> returnType, Class<?>[] args) {
|
|
|
|
List<Method> methods = new ArrayList<Method>();
|
|
|
|
|
|
|
|
// Find the correct method to call
|
|
|
|
for (Method method : getMethods()) {
|
|
|
|
if (method.getReturnType().equals(returnType) && Arrays.equals(method.getParameterTypes(), args)) {
|
|
|
|
methods.add(method);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return methods;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a field by name.
|
|
|
|
* @param nameRegex - regular expression that will match a field name.
|
|
|
|
* @return The first field to match the given expression.
|
|
|
|
* @throws IllegalArgumentException If the field cannot be found.
|
|
|
|
*/
|
|
|
|
public Field getFieldByName(String nameRegex) {
|
|
|
|
Pattern match = Pattern.compile(nameRegex);
|
|
|
|
|
|
|
|
for (Field field : getFields()) {
|
|
|
|
if (match.matcher(field.getName()).matches()) {
|
2015-06-17 20:25:39 +02:00
|
|
|
// Right - this is probably it.
|
2012-10-16 07:28:54 +02:00
|
|
|
return field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Looks like we're outdated. Too bad.
|
2015-06-17 20:25:39 +02:00
|
|
|
throw new IllegalArgumentException("Unable to find a field with the pattern " +
|
2012-10-16 07:28:54 +02:00
|
|
|
nameRegex + " in " + source.getName());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the first field with a type equal to or more specific to the given type.
|
|
|
|
* @param name - name the field probably is given. This will only be used in the error message.
|
|
|
|
* @param type - type of the field to find.
|
|
|
|
* @return The first field with a type that is an instance of the given type.
|
|
|
|
*/
|
|
|
|
public Field getFieldByType(String name, Class<?> type) {
|
|
|
|
List<Field> fields = getFieldListByType(type);
|
|
|
|
|
|
|
|
if (fields.size() > 0) {
|
|
|
|
return fields.get(0);
|
|
|
|
} else {
|
|
|
|
// Looks like we're outdated. Too bad.
|
|
|
|
throw new IllegalArgumentException(String.format("Unable to find a field %s with the type %s in %s",
|
|
|
|
name, type.getName(), source.getName())
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves every field with a type equal to or more specific to the given type.
|
|
|
|
* @param type - type of the fields to find.
|
|
|
|
* @return Every field with a type that is an instance of the given type.
|
|
|
|
*/
|
|
|
|
public List<Field> getFieldListByType(Class<?> type) {
|
|
|
|
List<Field> fields = new ArrayList<Field>();
|
|
|
|
|
|
|
|
// Field with a compatible type
|
|
|
|
for (Field field : getFields()) {
|
|
|
|
// A assignable from B -> B instanceOf A
|
|
|
|
if (type.isAssignableFrom(field.getType())) {
|
|
|
|
fields.add(field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fields;
|
|
|
|
}
|
2017-05-30 04:14:08 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a field with a given type and parameters. This is most useful
|
|
|
|
* when dealing with Collections.
|
|
|
|
*
|
|
|
|
* @param fieldType Type of the field
|
|
|
|
* @param params Variable length array of type parameters
|
|
|
|
* @return The field
|
|
|
|
*
|
|
|
|
* @throws IllegalArgumentException If the field cannot be found
|
|
|
|
*/
|
|
|
|
public Field getParameterizedField(Class<?> fieldType, Class<?>... params) {
|
|
|
|
for (Field field : getFields()) {
|
|
|
|
if (field.getType().equals(fieldType)) {
|
|
|
|
Type type = field.getGenericType();
|
|
|
|
if (type instanceof ParameterizedType) {
|
|
|
|
if (Arrays.equals(((ParameterizedType) type).getActualTypeArguments(), params))
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new IllegalArgumentException("Unable to find a field with type " + fieldType + " and params " + Arrays.toString(params));
|
|
|
|
}
|
2012-10-16 07:28:54 +02:00
|
|
|
|
2013-01-30 05:08:38 +01:00
|
|
|
/**
|
|
|
|
* Retrieve the first field that matches.
|
|
|
|
* <p>
|
|
|
|
* ForceAccess must be TRUE in order for this method to access private, protected and package level fields.
|
|
|
|
* @param matcher - the matcher to use.
|
|
|
|
* @return The first method that satisfies the given matcher.
|
|
|
|
* @throws IllegalArgumentException If the method cannot be found.
|
|
|
|
*/
|
|
|
|
public Field getField(AbstractFuzzyMatcher<Field> matcher) {
|
|
|
|
List<Field> result = getFieldList(matcher);
|
|
|
|
|
|
|
|
if (result.size() > 0)
|
|
|
|
return result.get(0);
|
|
|
|
else
|
|
|
|
throw new IllegalArgumentException("Unable to find a field that matches " + matcher);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve a list of every field that matches the given matcher.
|
|
|
|
* <p>
|
|
|
|
* ForceAccess must be TRUE in order for this method to access private, protected and package level fields.
|
|
|
|
* @param matcher - the matcher to apply.
|
|
|
|
* @return List of found fields.
|
|
|
|
*/
|
|
|
|
public List<Field> getFieldList(AbstractFuzzyMatcher<Field> matcher) {
|
|
|
|
List<Field> fields = Lists.newArrayList();
|
|
|
|
|
|
|
|
// Add all matching fields to the list
|
|
|
|
for (Field field : getFields()) {
|
2013-01-30 18:44:13 +01:00
|
|
|
if (matcher.isMatch(field, source)) {
|
2013-01-30 05:08:38 +01:00
|
|
|
fields.add(field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fields;
|
|
|
|
}
|
|
|
|
|
2012-10-16 07:28:54 +02:00
|
|
|
/**
|
|
|
|
* Retrieves a field by type.
|
|
|
|
* <p>
|
2015-06-17 20:25:39 +02:00
|
|
|
* Note that the type is matched using the full canonical representation, i.e.:
|
2012-10-16 07:28:54 +02:00
|
|
|
* <ul>
|
|
|
|
* <li>java.util.List</li>
|
|
|
|
* <li>net.comphenix.xp.ExperienceMod</li>
|
|
|
|
* </ul>
|
|
|
|
* @param typeRegex - regular expression that will match the field type.
|
|
|
|
* @return The first field with a type that matches the given regular expression.
|
|
|
|
* @throws IllegalArgumentException If the field cannot be found.
|
|
|
|
*/
|
|
|
|
public Field getFieldByType(String typeRegex) {
|
|
|
|
|
|
|
|
Pattern match = Pattern.compile(typeRegex);
|
|
|
|
|
|
|
|
// Like above, only here we test the field type
|
|
|
|
for (Field field : getFields()) {
|
|
|
|
String name = field.getType().getName();
|
|
|
|
|
|
|
|
if (match.matcher(name).matches()) {
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Looks like we're outdated. Too bad.
|
2015-06-17 20:25:39 +02:00
|
|
|
throw new IllegalArgumentException("Unable to find a field with the type " +
|
2012-10-16 07:28:54 +02:00
|
|
|
typeRegex + " in " + source.getName());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a field by type.
|
|
|
|
* <p>
|
2015-06-17 20:25:39 +02:00
|
|
|
* Note that the type is matched using the full canonical representation, i.e.:
|
2012-10-16 07:28:54 +02:00
|
|
|
* <ul>
|
|
|
|
* <li>java.util.List</li>
|
|
|
|
* <li>net.comphenix.xp.ExperienceMod</li>
|
|
|
|
* </ul>
|
|
|
|
* @param typeRegex - regular expression that will match the field type.
|
|
|
|
* @param ignored - types to ignore.
|
|
|
|
* @return The first field with a type that matches the given regular expression.
|
|
|
|
* @throws IllegalArgumentException If the field cannot be found.
|
|
|
|
*/
|
|
|
|
@SuppressWarnings("rawtypes")
|
|
|
|
public Field getFieldByType(String typeRegex, Set<Class> ignored) {
|
|
|
|
|
|
|
|
Pattern match = Pattern.compile(typeRegex);
|
|
|
|
|
|
|
|
// Like above, only here we test the field type
|
|
|
|
for (Field field : getFields()) {
|
|
|
|
Class type = field.getType();
|
|
|
|
|
|
|
|
if (!ignored.contains(type) && match.matcher(type.getName()).matches()) {
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Looks like we're outdated. Too bad.
|
2015-06-17 20:25:39 +02:00
|
|
|
throw new IllegalArgumentException("Unable to find a field with the type " +
|
2012-10-16 07:28:54 +02:00
|
|
|
typeRegex + " in " + source.getName());
|
|
|
|
}
|
|
|
|
|
2013-01-30 05:08:38 +01:00
|
|
|
/**
|
|
|
|
* Retrieve the first constructor that matches.
|
|
|
|
* <p>
|
|
|
|
* ForceAccess must be TRUE in order for this method to access private, protected and package level constructors.
|
|
|
|
* @param matcher - the matcher to use.
|
|
|
|
* @return The first constructor that satisfies the given matcher.
|
|
|
|
* @throws IllegalArgumentException If the constructor cannot be found.
|
|
|
|
*/
|
|
|
|
public Constructor<?> getConstructor(AbstractFuzzyMatcher<MethodInfo> matcher) {
|
|
|
|
List<Constructor<?>> result = getConstructorList(matcher);
|
|
|
|
|
|
|
|
if (result.size() > 0)
|
|
|
|
return result.get(0);
|
|
|
|
else
|
|
|
|
throw new IllegalArgumentException("Unable to find a method that matches " + matcher);
|
|
|
|
}
|
|
|
|
|
2013-03-04 00:44:09 +01:00
|
|
|
/**
|
2015-06-17 20:25:39 +02:00
|
|
|
* Retrieve every method as a map over names.
|
2013-03-04 00:44:09 +01:00
|
|
|
* <p>
|
|
|
|
* Note that overloaded methods will only occur once in the resulting map.
|
|
|
|
* @param methods - every method.
|
|
|
|
* @return A map over every given method.
|
|
|
|
*/
|
|
|
|
public Map<String, Method> getMappedMethods(List<Method> methods) {
|
|
|
|
Map<String, Method> map = Maps.newHashMap();
|
|
|
|
|
|
|
|
for (Method method : methods) {
|
|
|
|
map.put(method.getName(), method);
|
|
|
|
}
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
2013-01-30 05:08:38 +01:00
|
|
|
/**
|
|
|
|
* Retrieve a list of every constructor that matches the given matcher.
|
|
|
|
* <p>
|
|
|
|
* ForceAccess must be TRUE in order for this method to access private, protected and package level constructors.
|
|
|
|
* @param matcher - the matcher to apply.
|
|
|
|
* @return List of found constructors.
|
|
|
|
*/
|
|
|
|
public List<Constructor<?>> getConstructorList(AbstractFuzzyMatcher<MethodInfo> matcher) {
|
|
|
|
List<Constructor<?>> constructors = Lists.newArrayList();
|
|
|
|
|
|
|
|
// Add all matching fields to the list
|
|
|
|
for (Constructor<?> constructor : getConstructors()) {
|
2013-01-30 18:44:13 +01:00
|
|
|
if (matcher.isMatch(MethodInfo.fromConstructor(constructor), source)) {
|
2013-01-30 05:08:38 +01:00
|
|
|
constructors.add(constructor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return constructors;
|
|
|
|
}
|
|
|
|
|
2012-10-16 07:28:54 +02:00
|
|
|
/**
|
|
|
|
* Retrieves all private and public fields in declared order (after JDK 1.5).
|
2013-01-30 05:08:38 +01:00
|
|
|
* <p>
|
|
|
|
* Private, protected and package fields are ignored if forceAccess is FALSE.
|
2012-10-16 07:28:54 +02:00
|
|
|
* @return Every field.
|
|
|
|
*/
|
|
|
|
public Set<Field> getFields() {
|
2016-05-16 22:27:05 +02:00
|
|
|
Validate.notNull(source, "source cannot be null!");
|
|
|
|
|
2012-10-16 07:28:54 +02:00
|
|
|
// We will only consider private fields in the declared class
|
|
|
|
if (forceAccess)
|
|
|
|
return setUnion(source.getDeclaredFields(), source.getFields());
|
|
|
|
else
|
|
|
|
return setUnion(source.getFields());
|
|
|
|
}
|
|
|
|
|
2013-12-04 04:17:02 +01:00
|
|
|
/**
|
|
|
|
* Retrieves all private and public fields, up until a certain superclass.
|
|
|
|
* @param excludeClass - the class (and its superclasses) to exclude from the search.
|
|
|
|
* @return Every such declared field.
|
|
|
|
*/
|
|
|
|
public Set<Field> getDeclaredFields(Class<?> excludeClass) {
|
|
|
|
if (forceAccess) {
|
|
|
|
Class<?> current = source;
|
|
|
|
Set<Field> fields = Sets.newLinkedHashSet();
|
|
|
|
|
|
|
|
while (current != null && current != excludeClass) {
|
|
|
|
fields.addAll(Arrays.asList(current.getDeclaredFields()));
|
|
|
|
current = current.getSuperclass();
|
|
|
|
}
|
|
|
|
return fields;
|
|
|
|
}
|
|
|
|
return getFields();
|
|
|
|
}
|
|
|
|
|
2012-10-16 07:28:54 +02:00
|
|
|
/**
|
|
|
|
* Retrieves all private and public methods in declared order (after JDK 1.5).
|
2013-01-30 05:08:38 +01:00
|
|
|
* <p>
|
|
|
|
* Private, protected and package methods are ignored if forceAccess is FALSE.
|
2012-10-16 07:28:54 +02:00
|
|
|
* @return Every method.
|
|
|
|
*/
|
|
|
|
public Set<Method> getMethods() {
|
|
|
|
// We will only consider private methods in the declared class
|
|
|
|
if (forceAccess)
|
|
|
|
return setUnion(source.getDeclaredMethods(), source.getMethods());
|
|
|
|
else
|
|
|
|
return setUnion(source.getMethods());
|
|
|
|
}
|
|
|
|
|
2013-01-30 05:08:38 +01:00
|
|
|
/**
|
|
|
|
* Retrieves all private and public constructors in declared order (after JDK 1.5).
|
|
|
|
* <p>
|
|
|
|
* Private, protected and package constructors are ignored if forceAccess is FALSE.
|
|
|
|
* @return Every constructor.
|
|
|
|
*/
|
|
|
|
public Set<Constructor<?>> getConstructors() {
|
|
|
|
if (forceAccess)
|
|
|
|
return setUnion(source.getDeclaredConstructors());
|
|
|
|
else
|
|
|
|
return setUnion(source.getConstructors());
|
|
|
|
}
|
|
|
|
|
2012-10-16 07:28:54 +02:00
|
|
|
// Prevent duplicate fields
|
2015-10-08 00:12:50 +02:00
|
|
|
|
2016-02-18 03:03:26 +01:00
|
|
|
@SafeVarargs
|
2012-10-16 07:28:54 +02:00
|
|
|
private static <T> Set<T> setUnion(T[]... array) {
|
|
|
|
Set<T> result = new LinkedHashSet<T>();
|
|
|
|
|
|
|
|
for (T[] elements : array) {
|
|
|
|
for (T element : elements) {
|
|
|
|
result.add(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves whether or not not to override any scope restrictions.
|
|
|
|
* @return TRUE if we override scope, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public boolean isForceAccess() {
|
|
|
|
return forceAccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets whether or not not to override any scope restrictions.
|
|
|
|
* @param forceAccess - TRUE if we override scope, FALSE otherwise.
|
|
|
|
*/
|
|
|
|
public void setForceAccess(boolean forceAccess) {
|
|
|
|
this.forceAccess = forceAccess;
|
|
|
|
}
|
|
|
|
}
|