Improve JavaDoc and add the ability to match declaring class.
Dieser Commit ist enthalten in:
Ursprung
2f8912a8ae
Commit
8b12907dfb
@ -1,7 +1,5 @@
|
||||
package com.comphenix.protocol.reflect;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
/**
|
||||
@ -13,100 +11,13 @@ import com.google.common.primitives.Ints;
|
||||
public abstract class AbstractFuzzyMatcher<T> implements Comparable<AbstractFuzzyMatcher<T>> {
|
||||
private Integer roundNumber;
|
||||
|
||||
/**
|
||||
* Used to check class equality.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
protected static class ClassMatcher {
|
||||
/**
|
||||
* Match any class.
|
||||
*/
|
||||
public static final ClassMatcher MATCH_ALL = new ClassMatcher(null, true);
|
||||
|
||||
private final Class<?> matcher;
|
||||
private final boolean useAssignable;
|
||||
|
||||
/**
|
||||
* Constructs a new class matcher.
|
||||
* @param matcher - the matching class, or NULL to represent anything.
|
||||
* @param useAssignable - whether or not its acceptible for the input type to be a superclass.
|
||||
*/
|
||||
public ClassMatcher(Class<?> matcher, boolean useAssignable) {
|
||||
this.matcher = matcher;
|
||||
this.useAssignable = useAssignable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given class is equivalent.
|
||||
* <p>
|
||||
* If the matcher is NULL, the result will only be TRUE if use assignable is TRUE.
|
||||
* @param input - the input class defined in the source file.
|
||||
* @return TRUE if input is a matcher or a superclass of matcher, FALSE otherwise.
|
||||
*/
|
||||
public final boolean isClassEqual(@Nonnull Class<?> input) {
|
||||
if (input == null)
|
||||
throw new IllegalArgumentException("Input class cannot be NULL.");
|
||||
|
||||
// Do our checking
|
||||
if (matcher == null)
|
||||
return useAssignable;
|
||||
else if (useAssignable)
|
||||
return input.isAssignableFrom(matcher); // matcher instanceof input
|
||||
else
|
||||
return input.equals(matcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of superclasses of the specific class.
|
||||
* <p>
|
||||
* Object is represented as one. All interfaces are one, unless they're derived.
|
||||
* @param clazz - the class to test.
|
||||
* @return The number of superclasses.
|
||||
*/
|
||||
public final int getClassNumber() {
|
||||
Class<?> clazz = matcher;
|
||||
int count = 0;
|
||||
|
||||
// Move up the hierachy
|
||||
while (clazz != null) {
|
||||
count++;
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the class we're comparing against.
|
||||
* @return Class to compare against.
|
||||
*/
|
||||
public Class<?> getMatcher() {
|
||||
return matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not its acceptible for the input type to be a superclass.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean isUseAssignable() {
|
||||
return useAssignable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (useAssignable)
|
||||
return "Any " + matcher;
|
||||
else
|
||||
return "Exact " + matcher;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given value is a match.
|
||||
* @param value - the value to match.
|
||||
* @param parent - the parent container, or NULL if this value is the root.
|
||||
* @return TRUE if it is a match, FALSE otherwise.
|
||||
*/
|
||||
public abstract boolean isMatch(T value);
|
||||
public abstract boolean isMatch(T value, Object parent);
|
||||
|
||||
/**
|
||||
* Calculate the round number indicating when this matcher should be applied.
|
||||
|
@ -15,8 +15,9 @@ public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzz
|
||||
// Accessibility matchers
|
||||
private int modifiersRequired;
|
||||
private int modifiersBanned;
|
||||
|
||||
private Pattern nameRegex;
|
||||
private ClassMatcher declaringMatcher = ClassMatcher.MATCH_ALL;
|
||||
private AbstractFuzzyMatcher<Class<?>> declaringMatcher = ExactClassMatcher.MATCH_ALL;
|
||||
|
||||
/**
|
||||
* Whether or not this contract can be modified.
|
||||
@ -31,37 +32,94 @@ public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzz
|
||||
public static abstract class Builder<T extends AbstractFuzzyMember<?>> {
|
||||
protected T member = initialMember();
|
||||
|
||||
/**
|
||||
* Add a given bit-field of required modifiers for every matching member.
|
||||
* @param modifier - bit-field of modifiers that are required.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder<T> requireModifier(int modifier) {
|
||||
member.modifiersRequired |= modifier;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a given bit-field of modifers that will skip or ignore members.
|
||||
* @param modifier - bit-field of modifiers to skip or ignore.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder<T> banModifier(int modifier) {
|
||||
member.modifiersBanned |= modifier;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the regular expresson that matches a members name.
|
||||
* @param regex - new regular expression of valid names.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder<T> nameRegex(String regex) {
|
||||
member.nameRegex = Pattern.compile(regex);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the regular expression pattern that matches a members name.
|
||||
* @param pattern - regular expression pattern for a valid name.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder<T> nameRegex(Pattern pattern) {
|
||||
member.nameRegex = pattern;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the exact name of the member we are matching.
|
||||
* <p<
|
||||
* This will overwrite the regular expression rule.
|
||||
* @param name - exact name.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder<T> nameExact(String name) {
|
||||
return nameRegex(Pattern.quote(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Require that a member is defined by this exact class.
|
||||
* @param declaringClass - the declaring class of any matching member.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder<T> declaringClassExactType(Class<?> declaringClass) {
|
||||
member.declaringMatcher = new ClassMatcher(declaringClass, false);
|
||||
member.declaringMatcher = ExactClassMatcher.matchExact(declaringClass);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> declaringClassCanHold(Class<?> declaringClass) {
|
||||
member.declaringMatcher = new ClassMatcher(declaringClass, true);
|
||||
/**
|
||||
* Require that a member is defined by this exact class, or any super class.
|
||||
* @param declaringClass - the declaring class.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder<T> declaringClassSuperOf(Class<?> declaringClass) {
|
||||
member.declaringMatcher = ExactClassMatcher.matchSuper(declaringClass);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require that a member is defined by this exact class, or any super class.
|
||||
* @param declaringClass - the declaring class.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder<T> declaringClassDerivedOf(Class<?> declaringClass) {
|
||||
member.declaringMatcher = ExactClassMatcher.matchDerived(declaringClass);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require that a member is defined by a class that matches the given matcher.
|
||||
* @param classMatcher - class matcher.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder<T> declaringClassMatching(AbstractFuzzyMatcher<Class<?>> classMatcher) {
|
||||
member.declaringMatcher = classMatcher;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -107,13 +165,13 @@ public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzz
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatch(T value) {
|
||||
public boolean isMatch(T value, Object parent) {
|
||||
int mods = value.getModifiers();
|
||||
|
||||
// Match accessibility and name
|
||||
return (mods & modifiersRequired) != 0 &&
|
||||
(mods & modifiersBanned) == 0 &&
|
||||
declaringMatcher.isClassEqual(value.getDeclaringClass()) &&
|
||||
declaringMatcher.isMatch(value.getDeclaringClass(), value) &&
|
||||
isNameMatch(value.getName());
|
||||
}
|
||||
|
||||
@ -131,6 +189,6 @@ public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzz
|
||||
throw new IllegalStateException("Cannot calculate round number during construction.");
|
||||
|
||||
// NULL is zero
|
||||
return -declaringMatcher.getClassNumber();
|
||||
return declaringMatcher.getRoundNumber();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,145 @@
|
||||
package com.comphenix.protocol.reflect;
|
||||
|
||||
/**
|
||||
* Used to check class equality.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class ExactClassMatcher extends AbstractFuzzyMatcher<Class<?>> {
|
||||
/**
|
||||
* Different matching rules.
|
||||
*/
|
||||
enum Options {
|
||||
/**
|
||||
* Match classes exactly.
|
||||
*/
|
||||
MATCH_EXACT,
|
||||
|
||||
/**
|
||||
* A match if the input class is a superclass of the matcher class, or the same class.
|
||||
*/
|
||||
MATCH_SUPER,
|
||||
|
||||
/**
|
||||
* A match if the input class is a derived class of the matcher class, or the same class.
|
||||
*/
|
||||
MATCH_DERIVED
|
||||
}
|
||||
|
||||
/**
|
||||
* Match any class.
|
||||
*/
|
||||
public static final ExactClassMatcher MATCH_ALL = new ExactClassMatcher(null, Options.MATCH_SUPER);
|
||||
|
||||
private final Class<?> matcher;
|
||||
private final Options option;
|
||||
|
||||
/**
|
||||
* Construct a class matcher that matches types exactly.
|
||||
* @param matcher - the matching class.
|
||||
*/
|
||||
public static ExactClassMatcher matchExact(Class<?> matcher) {
|
||||
return new ExactClassMatcher(matcher, Options.MATCH_EXACT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a class matcher that matches super types of the given class.
|
||||
* @param matcher - the matching type must be a super class of this type.
|
||||
* @return A new class mathcher.
|
||||
*/
|
||||
public static ExactClassMatcher matchSuper(Class<?> matcher) {
|
||||
return new ExactClassMatcher(matcher, Options.MATCH_SUPER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a class matcher that matches derived types of the given class.
|
||||
* @param matcher - the matching type must be a derived class of this type.
|
||||
* @return A new class mathcher.
|
||||
*/
|
||||
public static ExactClassMatcher matchDerived(Class<?> matcher) {
|
||||
return new ExactClassMatcher(matcher, Options.MATCH_DERIVED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new class matcher.
|
||||
* @param matcher - the matching class, or NULL to represent anything.
|
||||
* @param option - options specifying the matching rules.
|
||||
*/
|
||||
private ExactClassMatcher(Class<?> matcher, Options option) {
|
||||
this.matcher = matcher;
|
||||
this.option = option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given class is equivalent.
|
||||
* <p>
|
||||
* If the matcher is NULL, the result will only be TRUE if we're not matching exactly.
|
||||
* @param input - the input class defined in the source file.
|
||||
* @param parent - the container that holds a reference to this class.
|
||||
* @return TRUE if input matches according to the rules in {@link #getOptions()}, FALSE otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean isMatch(Class<?> input, Object parent) {
|
||||
if (input == null)
|
||||
throw new IllegalArgumentException("Input class cannot be NULL.");
|
||||
|
||||
// Do our checking
|
||||
if (matcher == null)
|
||||
return option != Options.MATCH_EXACT;
|
||||
else if (option == Options.MATCH_SUPER)
|
||||
return input.isAssignableFrom(matcher); // matcher instanceof input
|
||||
else if (option == Options.MATCH_DERIVED)
|
||||
return matcher.isAssignableFrom(input); // input instanceof matcher
|
||||
else
|
||||
return input.equals(matcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateRoundNumber() {
|
||||
return -getClassNumber(matcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of superclasses of the specific class.
|
||||
* <p>
|
||||
* Object is represented as one. All interfaces are one, unless they're derived.
|
||||
* @param clazz - the class to test.
|
||||
* @return The number of superclasses.
|
||||
*/
|
||||
public static int getClassNumber(Class<?> clazz) {
|
||||
int count = 0;
|
||||
|
||||
// Move up the hierachy
|
||||
while (clazz != null) {
|
||||
count++;
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the class we're comparing against.
|
||||
* @return Class to compare against.
|
||||
*/
|
||||
public Class<?> getMatcher() {
|
||||
return matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* The matching rules for this class matcher.
|
||||
* @return The current matching option.
|
||||
*/
|
||||
public Options getOptions() {
|
||||
return option;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (option == Options.MATCH_SUPER)
|
||||
return matcher + " instanceof input";
|
||||
else if (option == Options.MATCH_DERIVED)
|
||||
return "input instanceof " + matcher;
|
||||
else
|
||||
return "Exact " + matcher;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package com.comphenix.protocol.reflect;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -106,6 +107,32 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the parent class of a method, field or constructor.
|
||||
* @return Parent matcher.
|
||||
*/
|
||||
public static AbstractFuzzyMatcher<Class<?>> matchParent() {
|
||||
return new AbstractFuzzyMatcher<Class<?>>() {
|
||||
@Override
|
||||
public boolean isMatch(Class<?> value, Object parent) {
|
||||
if (parent instanceof Member) {
|
||||
return ((Member) parent).getDeclaringClass().equals(value);
|
||||
} else if (parent instanceof Class) {
|
||||
return parent.equals(value);
|
||||
} else {
|
||||
// Can't be a match
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateRoundNumber() {
|
||||
// We match a very specific type
|
||||
return -100;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new fuzzy class contract with the given contracts.
|
||||
* @param fieldContracts - field contracts.
|
||||
@ -169,22 +196,22 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatch(Class<?> value) {
|
||||
public boolean isMatch(Class<?> value, Object parent) {
|
||||
FuzzyReflection reflection = FuzzyReflection.fromClass(value);
|
||||
|
||||
// Make sure all the contracts are valid
|
||||
return processContracts(reflection.getFields(), fieldContracts) &&
|
||||
processContracts(MethodInfo.fromMethods(reflection.getMethods()), methodContracts) &&
|
||||
processContracts(MethodInfo.fromConstructors(value.getDeclaredConstructors()), constructorContracts);
|
||||
return processContracts(reflection.getFields(), value, fieldContracts) &&
|
||||
processContracts(MethodInfo.fromMethods(reflection.getMethods()), value, methodContracts) &&
|
||||
processContracts(MethodInfo.fromConstructors(value.getDeclaredConstructors()), value, constructorContracts);
|
||||
}
|
||||
|
||||
private <T> boolean processContracts(Collection<T> values, List<AbstractFuzzyMatcher<T>> matchers) {
|
||||
private <T> boolean processContracts(Collection<T> values, Class<?> parent, List<AbstractFuzzyMatcher<T>> matchers) {
|
||||
boolean[] accepted = new boolean[matchers.size()];
|
||||
int count = accepted.length;
|
||||
|
||||
// Process every value in turn
|
||||
for (T value : values) {
|
||||
int index = processValue(value, accepted, matchers);
|
||||
int index = processValue(value, parent, accepted, matchers);
|
||||
|
||||
// See if this worked
|
||||
if (index >= 0) {
|
||||
@ -199,14 +226,14 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
private <T> int processValue(T value, boolean accepted[], List<AbstractFuzzyMatcher<T>> matchers) {
|
||||
private <T> int processValue(T value, Class<?> parent, boolean accepted[], List<AbstractFuzzyMatcher<T>> matchers) {
|
||||
// The order matters
|
||||
for (int i = 0; i < matchers.size(); i++) {
|
||||
if (!accepted[i]) {
|
||||
AbstractFuzzyMatcher<T> matcher = matchers.get(i);
|
||||
|
||||
// Mark this as detected
|
||||
if (matcher.isMatch(value)) {
|
||||
if (matcher.isMatch(value, parent)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
@ -11,29 +11,34 @@ import javax.annotation.Nonnull;
|
||||
* @author Kristian
|
||||
*/
|
||||
public class FuzzyFieldContract extends AbstractFuzzyMember<Field> {
|
||||
private ClassMatcher typeMatcher = ClassMatcher.MATCH_ALL;
|
||||
private AbstractFuzzyMatcher<Class<?>> typeMatcher = ExactClassMatcher.MATCH_ALL;
|
||||
|
||||
public static class Builder extends AbstractFuzzyMember.Builder<FuzzyFieldContract> {
|
||||
@Override
|
||||
public Builder requireModifier(int modifier) {
|
||||
super.requireModifier(modifier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder banModifier(int modifier) {
|
||||
super.banModifier(modifier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder nameRegex(String regex) {
|
||||
super.nameRegex(regex);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder nameRegex(Pattern pattern) {
|
||||
super.nameRegex(pattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder nameExact(String name) {
|
||||
super.nameExact(name);
|
||||
return this;
|
||||
@ -44,8 +49,21 @@ public class FuzzyFieldContract extends AbstractFuzzyMember<Field> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder declaringClassCanHold(Class<?> declaringClass) {
|
||||
super.declaringClassCanHold(declaringClass);
|
||||
@Override
|
||||
public Builder declaringClassSuperOf(Class<?> declaringClass) {
|
||||
super.declaringClassSuperOf(declaringClass);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder declaringClassDerivedOf(Class<?> declaringClass) {
|
||||
super.declaringClassDerivedOf(declaringClass);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder declaringClassMatching(AbstractFuzzyMatcher<Class<?>> classMatcher) {
|
||||
super.declaringClassMatching(classMatcher);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -56,12 +74,12 @@ public class FuzzyFieldContract extends AbstractFuzzyMember<Field> {
|
||||
}
|
||||
|
||||
public Builder typeExact(Class<?> type) {
|
||||
member.typeMatcher = new ClassMatcher(type, false);
|
||||
member.typeMatcher = ExactClassMatcher.matchExact(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder typeCanHold(Class<?> type) {
|
||||
member.typeMatcher = new ClassMatcher(type, true);
|
||||
public Builder typeSuperOf(Class<?> type) {
|
||||
member.typeMatcher = ExactClassMatcher.matchSuper(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -91,9 +109,9 @@ public class FuzzyFieldContract extends AbstractFuzzyMember<Field> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatch(Field value) {
|
||||
if (super.isMatch(value)) {
|
||||
return typeMatcher.isClassEqual(value.getType());
|
||||
public boolean isMatch(Field value, Object parent) {
|
||||
if (super.isMatch(value, parent)) {
|
||||
return typeMatcher.isMatch(value.getType(), value);
|
||||
}
|
||||
// No match
|
||||
return false;
|
||||
@ -101,8 +119,8 @@ public class FuzzyFieldContract extends AbstractFuzzyMember<Field> {
|
||||
|
||||
@Override
|
||||
protected int calculateRoundNumber() {
|
||||
int current = -typeMatcher.getClassNumber();
|
||||
|
||||
return combineRounds(super.calculateRoundNumber(), current);
|
||||
// Combine the two
|
||||
return combineRounds(super.calculateRoundNumber(),
|
||||
typeMatcher.calculateRoundNumber());
|
||||
}
|
||||
}
|
||||
|
@ -20,14 +20,14 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
|
||||
/**
|
||||
* The expected index.
|
||||
*/
|
||||
private final ClassMatcher typeMatcher;
|
||||
private final AbstractFuzzyMatcher<Class<?>> typeMatcher;
|
||||
private final Integer indexMatch;
|
||||
|
||||
/**
|
||||
* Construct a new parameter class matcher.
|
||||
* @param typeMatcher - class type matcher.
|
||||
*/
|
||||
public ParameterClassMatcher(@Nonnull ClassMatcher typeMatcher) {
|
||||
public ParameterClassMatcher(@Nonnull AbstractFuzzyMatcher<Class<?>> typeMatcher) {
|
||||
this(typeMatcher, null);
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
|
||||
* @param typeMatcher - class type matcher.
|
||||
* @param indexMatch - parameter index to match, or NULL for anything.
|
||||
*/
|
||||
public ParameterClassMatcher(@Nonnull ClassMatcher typeMatcher, Integer indexMatch) {
|
||||
public ParameterClassMatcher(@Nonnull AbstractFuzzyMatcher<Class<?>> typeMatcher, Integer indexMatch) {
|
||||
if (typeMatcher == null)
|
||||
throw new IllegalArgumentException("Type matcher cannot be NULL.");
|
||||
|
||||
@ -47,25 +47,26 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
|
||||
/**
|
||||
* See if there's a match for this matcher.
|
||||
* @param used - parameters that have been matched before.
|
||||
* @param parent - the container (member) that holds a reference to this parameter.
|
||||
* @param params - the type of each parameter.
|
||||
* @return TRUE if this matcher matches any of the given parameters, FALSE otherwise.
|
||||
*/
|
||||
public boolean isParameterMatch(Class<?> param, int index) {
|
||||
public boolean isParameterMatch(Class<?> param, MethodInfo parent, int index) {
|
||||
// Make sure the index is valid (or NULL)
|
||||
if (indexMatch == null || indexMatch == index)
|
||||
return typeMatcher.isClassEqual(param);
|
||||
return typeMatcher.isMatch(param, parent);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatch(Class<?>[] value) {
|
||||
public boolean isMatch(Class<?>[] value, Object parent) {
|
||||
throw new NotImplementedException("Use the parameter match instead.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateRoundNumber() {
|
||||
return -typeMatcher.getClassNumber();
|
||||
return typeMatcher.getRoundNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -75,7 +76,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
|
||||
}
|
||||
|
||||
// Match return value
|
||||
private ClassMatcher returnMatcher = ClassMatcher.MATCH_ALL;
|
||||
private AbstractFuzzyMatcher<Class<?>> returnMatcher = ExactClassMatcher.MATCH_ALL;
|
||||
|
||||
// Handle parameters and exceptions
|
||||
private List<ParameterClassMatcher> paramMatchers = Lists.newArrayList();
|
||||
@ -90,92 +91,229 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder banModifier(int modifier) {
|
||||
super.banModifier(modifier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder nameRegex(String regex) {
|
||||
super.nameRegex(regex);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder nameRegex(Pattern pattern) {
|
||||
super.nameRegex(pattern);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder nameExact(String name) {
|
||||
super.nameExact(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder declaringClassExactType(Class<?> declaringClass) {
|
||||
super.declaringClassExactType(declaringClass);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder declaringClassCanHold(Class<?> declaringClass) {
|
||||
super.declaringClassCanHold(declaringClass);
|
||||
@Override
|
||||
public Builder declaringClassSuperOf(Class<?> declaringClass) {
|
||||
super.declaringClassSuperOf(declaringClass);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder declaringClassDerivedOf(Class<?> declaringClass) {
|
||||
super.declaringClassDerivedOf(declaringClass);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder declaringClassMatching(AbstractFuzzyMatcher<Class<?>> classMatcher) {
|
||||
super.declaringClassMatching(classMatcher);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new required parameter by type for any matching method.
|
||||
* @param type - the exact type this parameter must match.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder parameterExactType(Class<?> type) {
|
||||
member.paramMatchers.add(new ParameterClassMatcher(new ClassMatcher(type, false)));
|
||||
member.paramMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchExact(type)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder parameterCanHold(Class<?> type) {
|
||||
member.paramMatchers.add(new ParameterClassMatcher(new ClassMatcher(type, true)));
|
||||
/**
|
||||
* Add a new required parameter whose type must be a superlcass of the given type.
|
||||
* <p>
|
||||
* If a parameter is of type Number, any derived class (Integer, Long, etc.) will match it.
|
||||
* @param type - a type or derived type of the matching parameter.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder parameterSuperOf(Class<?> type) {
|
||||
member.paramMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchSuper(type)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new required parameter whose type must match the given class matcher.
|
||||
* @param classMatcher - the class matcher.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder parameterMatches(AbstractFuzzyMatcher<Class<?>> classMatcher) {
|
||||
member.paramMatchers.add(new ParameterClassMatcher(classMatcher));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new required parameter by type and position for any matching method.
|
||||
* @param type - the exact type this parameter must match.
|
||||
* @param index - the expected position in the parameter list.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder parameterExactType(Class<?> type, int index) {
|
||||
member.paramMatchers.add(new ParameterClassMatcher(new ClassMatcher(type, false), index));
|
||||
member.paramMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchExact(type), index));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder parameterCanHold(Class<?> type, int index) {
|
||||
member.paramMatchers.add(new ParameterClassMatcher(new ClassMatcher(type, true), index));
|
||||
/**
|
||||
* Add a new required parameter whose type must be a superclass of the given type.
|
||||
* <p>
|
||||
* If a parameter is of type Number, any derived class (Integer, Long, etc.) will match it.
|
||||
* @param type - a type or derived type of the matching parameter.
|
||||
* @param index - the expected position in the parameter list.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder parameterSuperOf(Class<?> type, int index) {
|
||||
member.paramMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchSuper(type), index));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new required parameter whose type must match the given class matcher and index.
|
||||
* @param classMatcher - the class matcher.
|
||||
* @param index - the expected position in the parameter list.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder parameterMatches(AbstractFuzzyMatcher<Class<?>> classMatcher, int index) {
|
||||
member.paramMatchers.add(new ParameterClassMatcher(classMatcher, index));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the expected number of parameters in the matching method.
|
||||
* @param expectedCount - the number of parameters to expect.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder parameterCount(int expectedCount) {
|
||||
member.paramCount = expectedCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Require a void method.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder returnTypeVoid() {
|
||||
return returnTypeExact(Void.TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the return type of a matching method exactly.
|
||||
* @param type - the exact return type.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder returnTypeExact(Class<?> type) {
|
||||
member.returnMatcher = new ClassMatcher(type, false);
|
||||
member.returnMatcher = ExactClassMatcher.matchExact(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder returnCanHold(Class<?> type) {
|
||||
member.returnMatcher = new ClassMatcher(type, true);
|
||||
/**
|
||||
* Set the expected super class of the return type for every matching method.
|
||||
* @param type - the return type, or a super class of it.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder returnDerivedOf(Class<?> type) {
|
||||
member.returnMatcher = ExactClassMatcher.matchDerived(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a matcher that must match the return type of a matching method.
|
||||
* @param classMatcher - the exact return type.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder returnTypeMatches(AbstractFuzzyMatcher<Class<?>> classMatcher) {
|
||||
member.returnMatcher = classMatcher;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a throwable exception that must match the given type exactly.
|
||||
* @param type - exception type.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder exceptionExactType(Class<?> type) {
|
||||
member.exceptionMatchers.add(new ParameterClassMatcher(new ClassMatcher(type, false)));
|
||||
member.exceptionMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchExact(type)));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder exceptionCanHold(Class<?> type) {
|
||||
member.exceptionMatchers.add(new ParameterClassMatcher(new ClassMatcher(type, true)));
|
||||
/**
|
||||
* Add a throwable exception that must match the given type or be derived.
|
||||
* @param type - exception type.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder exceptionSuperOf(Class<?> type) {
|
||||
member.exceptionMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchSuper(type)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a throwable exception that must match the given matcher,
|
||||
* @param classMatcher - the class matcher that must match.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder exceptionMatches(AbstractFuzzyMatcher<Class<?>> classMatcher) {
|
||||
member.exceptionMatchers.add(new ParameterClassMatcher(classMatcher));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a throwable exception that must match the given type exactly and index.
|
||||
* @param type - exception type.
|
||||
* @param index - the position in the throwable list.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder exceptionExactType(Class<?> type, int index) {
|
||||
member.exceptionMatchers.add(new ParameterClassMatcher(new ClassMatcher(type, false), index));
|
||||
member.exceptionMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchExact(type), index));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder exceptionCanHold(Class<?> type, int index) {
|
||||
member.exceptionMatchers.add(new ParameterClassMatcher(new ClassMatcher(type, true), index));
|
||||
/**
|
||||
* Add a throwable exception that must match the given type or be derived and index.
|
||||
* @param type - exception type.
|
||||
* @param index - the position in the throwable list.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder exceptionSuperOf(Class<?> type, int index) {
|
||||
member.exceptionMatchers.add(new ParameterClassMatcher(ExactClassMatcher.matchSuper(type), index));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a throwable exception that must match the given matcher and index.
|
||||
* @param classMatcher - the class matcher that must match.
|
||||
* @param index - the position in the throwable list.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
public Builder exceptionMatches(AbstractFuzzyMatcher<Class<?>> classMatcher, int index) {
|
||||
member.exceptionMatchers.add(new ParameterClassMatcher(classMatcher, index));
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -190,7 +328,6 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
|
||||
member.prepareBuild();
|
||||
return new FuzzyMethodContract(member);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Builder newBuilder() {
|
||||
@ -219,31 +356,31 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatch(MethodInfo value) {
|
||||
if (super.isMatch(value)) {
|
||||
public boolean isMatch(MethodInfo value, Object parent) {
|
||||
if (super.isMatch(value, parent)) {
|
||||
Class<?>[] params = value.getParameterTypes();
|
||||
Class<?>[] exceptions = value.getExceptionTypes();
|
||||
|
||||
if (!returnMatcher.isClassEqual(value.getReturnType()))
|
||||
if (!returnMatcher.isMatch(value.getReturnType(), value))
|
||||
return false;
|
||||
if (paramCount != null && paramCount != value.getParameterTypes().length)
|
||||
return false;
|
||||
|
||||
// Finally, check parameters and exceptions
|
||||
return matchParameters(params, paramMatchers) &&
|
||||
matchParameters(exceptions, exceptionMatchers);
|
||||
return matchParameters(params, value, paramMatchers) &&
|
||||
matchParameters(exceptions, value, exceptionMatchers);
|
||||
}
|
||||
// No match
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean matchParameters(Class<?>[] types, List<ParameterClassMatcher> matchers) {
|
||||
private boolean matchParameters(Class<?>[] types, MethodInfo parent, List<ParameterClassMatcher> matchers) {
|
||||
boolean[] accepted = new boolean[matchers.size()];
|
||||
int count = accepted.length;
|
||||
|
||||
// Process every parameter in turn
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
int matcherIndex = processValue(types[i], i, accepted, matchers);
|
||||
int matcherIndex = processValue(types[i], parent, i, accepted, matchers);
|
||||
|
||||
if (matcherIndex >= 0) {
|
||||
accepted[matcherIndex] = true;
|
||||
@ -257,12 +394,12 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
private int processValue(Class<?> value, int index, boolean accepted[], List<ParameterClassMatcher> matchers) {
|
||||
private int processValue(Class<?> value, MethodInfo parent, int index, boolean accepted[], List<ParameterClassMatcher> matchers) {
|
||||
// The order matters
|
||||
for (int i = 0; i < matchers.size(); i++) {
|
||||
if (!accepted[i]) {
|
||||
// See if we got jackpot
|
||||
if (matchers.get(i).isParameterMatch(value, index)) {
|
||||
if (matchers.get(i).isParameterMatch(value, parent, index)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@ -277,7 +414,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
|
||||
int current = 0;
|
||||
|
||||
// Consider the return value first
|
||||
current = -returnMatcher.getClassNumber();
|
||||
current = returnMatcher.getRoundNumber();
|
||||
|
||||
// Handle parameters
|
||||
for (ParameterClassMatcher matcher : paramMatchers) {
|
||||
|
@ -121,7 +121,7 @@ public class FuzzyReflection {
|
||||
|
||||
// Add all matching fields to the list
|
||||
for (Method method : getMethods()) {
|
||||
if (matcher.isMatch(MethodInfo.fromMethod(method))) {
|
||||
if (matcher.isMatch(MethodInfo.fromMethod(method), source)) {
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
@ -338,7 +338,7 @@ public class FuzzyReflection {
|
||||
|
||||
// Add all matching fields to the list
|
||||
for (Field field : getFields()) {
|
||||
if (matcher.isMatch(field)) {
|
||||
if (matcher.isMatch(field, source)) {
|
||||
fields.add(field);
|
||||
}
|
||||
}
|
||||
@ -436,7 +436,7 @@ public class FuzzyReflection {
|
||||
|
||||
// Add all matching fields to the list
|
||||
for (Constructor<?> constructor : getConstructors()) {
|
||||
if (matcher.isMatch(MethodInfo.fromConstructor(constructor))) {
|
||||
if (matcher.isMatch(MethodInfo.fromConstructor(constructor), source)) {
|
||||
constructors.add(constructor);
|
||||
}
|
||||
}
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren