From 8b12907dfbccb2b6d5918c9dc41dd7694d9bba65 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 30 Jan 2013 18:44:13 +0100 Subject: [PATCH] Improve JavaDoc and add the ability to match declaring class. --- .../reflect/AbstractFuzzyMatcher.java | 93 +------- .../protocol/reflect/AbstractFuzzyMember.java | 72 +++++- .../protocol/reflect/ExactClassMatcher.java | 145 ++++++++++++ .../protocol/reflect/FuzzyClassContract.java | 43 +++- .../protocol/reflect/FuzzyFieldContract.java | 42 +++- .../protocol/reflect/FuzzyMethodContract.java | 209 +++++++++++++++--- .../protocol/reflect/FuzzyReflection.java | 6 +- 7 files changed, 453 insertions(+), 157 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ExactClassMatcher.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMatcher.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMatcher.java index c16ed506..c95fa3e1 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMatcher.java @@ -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 implements Comparable> { 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. - *

- * 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. - *

- * 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. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMember.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMember.java index 32c23d9b..3d77a24b 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMember.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/AbstractFuzzyMember.java @@ -15,8 +15,9 @@ public abstract class AbstractFuzzyMember extends AbstractFuzz // Accessibility matchers private int modifiersRequired; private int modifiersBanned; + private Pattern nameRegex; - private ClassMatcher declaringMatcher = ClassMatcher.MATCH_ALL; + private AbstractFuzzyMatcher> declaringMatcher = ExactClassMatcher.MATCH_ALL; /** * Whether or not this contract can be modified. @@ -31,37 +32,94 @@ public abstract class AbstractFuzzyMember extends AbstractFuzz public static abstract class Builder> { 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 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 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 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 nameRegex(Pattern pattern) { member.nameRegex = pattern; return this; } + /** + * Set the exact name of the member we are matching. + * 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 declaringClassExactType(Class declaringClass) { - member.declaringMatcher = new ClassMatcher(declaringClass, false); + member.declaringMatcher = ExactClassMatcher.matchExact(declaringClass); return this; } - public Builder 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 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 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 declaringClassMatching(AbstractFuzzyMatcher> classMatcher) { + member.declaringMatcher = classMatcher; return this; } @@ -107,13 +165,13 @@ public abstract class AbstractFuzzyMember 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 extends AbstractFuzz throw new IllegalStateException("Cannot calculate round number during construction."); // NULL is zero - return -declaringMatcher.getClassNumber(); + return declaringMatcher.getRoundNumber(); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ExactClassMatcher.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ExactClassMatcher.java new file mode 100644 index 00000000..3e7a34e4 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/ExactClassMatcher.java @@ -0,0 +1,145 @@ +package com.comphenix.protocol.reflect; + +/** + * Used to check class equality. + * + * @author Kristian + */ +class ExactClassMatcher extends AbstractFuzzyMatcher> { + /** + * 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. + *

+ * 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. + *

+ * 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; + } +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyClassContract.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyClassContract.java index f6a0c73c..0ae1b1a5 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyClassContract.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyClassContract.java @@ -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> { return new Builder(); } + /** + * Match the parent class of a method, field or constructor. + * @return Parent matcher. + */ + public static AbstractFuzzyMatcher> matchParent() { + return new AbstractFuzzyMatcher>() { + @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> { } @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 boolean processContracts(Collection values, List> matchers) { + private boolean processContracts(Collection values, Class parent, List> 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> { return count == 0; } - private int processValue(T value, boolean accepted[], List> matchers) { + private int processValue(T value, Class parent, boolean accepted[], List> matchers) { // The order matters for (int i = 0; i < matchers.size(); i++) { if (!accepted[i]) { AbstractFuzzyMatcher matcher = matchers.get(i); // Mark this as detected - if (matcher.isMatch(value)) { + if (matcher.isMatch(value, parent)) { return i; } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyFieldContract.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyFieldContract.java index cc80f7d0..040ec89f 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyFieldContract.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyFieldContract.java @@ -11,29 +11,34 @@ import javax.annotation.Nonnull; * @author Kristian */ public class FuzzyFieldContract extends AbstractFuzzyMember { - private ClassMatcher typeMatcher = ClassMatcher.MATCH_ALL; + private AbstractFuzzyMatcher> typeMatcher = ExactClassMatcher.MATCH_ALL; public static class Builder extends AbstractFuzzyMember.Builder { + @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 { 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> classMatcher) { + super.declaringClassMatching(classMatcher); return this; } @@ -56,12 +74,12 @@ public class FuzzyFieldContract extends AbstractFuzzyMember { } 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 { } @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 { @Override protected int calculateRoundNumber() { - int current = -typeMatcher.getClassNumber(); - - return combineRounds(super.calculateRoundNumber(), current); + // Combine the two + return combineRounds(super.calculateRoundNumber(), + typeMatcher.calculateRoundNumber()); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyMethodContract.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyMethodContract.java index 71ac44f2..c477d08b 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyMethodContract.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyMethodContract.java @@ -20,14 +20,14 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { /** * The expected index. */ - private final ClassMatcher typeMatcher; + private final AbstractFuzzyMatcher> 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> typeMatcher) { this(typeMatcher, null); } @@ -36,7 +36,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { * @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> typeMatcher, Integer indexMatch) { if (typeMatcher == null) throw new IllegalArgumentException("Type matcher cannot be NULL."); @@ -47,25 +47,26 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { /** * 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 { } // Match return value - private ClassMatcher returnMatcher = ClassMatcher.MATCH_ALL; + private AbstractFuzzyMatcher> returnMatcher = ExactClassMatcher.MATCH_ALL; // Handle parameters and exceptions private List paramMatchers = Lists.newArrayList(); @@ -90,92 +91,229 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { 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> 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. + *

+ * 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> 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. + *

+ * 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> 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> 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> 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> classMatcher, int index) { + member.exceptionMatchers.add(new ParameterClassMatcher(classMatcher, index)); return this; } @@ -190,7 +328,6 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { member.prepareBuild(); return new FuzzyMethodContract(member); } - } public static Builder newBuilder() { @@ -219,31 +356,31 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { } @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 matchers) { + private boolean matchParameters(Class[] types, MethodInfo parent, List 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 { return count == 0; } - private int processValue(Class value, int index, boolean accepted[], List matchers) { + private int processValue(Class value, MethodInfo parent, int index, boolean accepted[], List 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 { int current = 0; // Consider the return value first - current = -returnMatcher.getClassNumber(); + current = returnMatcher.getRoundNumber(); // Handle parameters for (ParameterClassMatcher matcher : paramMatchers) { diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java index 2ae04ddb..3f22efdf 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/FuzzyReflection.java @@ -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); } }