From eaf7ea9618fa93f447486ef236cf1de450770b1a Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sun, 3 Feb 2013 04:47:02 +0100 Subject: [PATCH] Add an equality and hashCode() method to all the matchers. --- .../reflect/fuzzy/AbstractFuzzyMember.java | 28 ++++++- ...assMatcher.java => ClassExactMatcher.java} | 26 +++++- .../reflect/fuzzy/ClassRegexMatcher.java | 58 ++++++++++++++ .../reflect/fuzzy/ClassSetMatcher.java | 57 +++++++++++++ .../reflect/fuzzy/FuzzyFieldContract.java | 22 ++++- .../protocol/reflect/fuzzy/FuzzyMatchers.java | 80 ++++++++----------- .../reflect/fuzzy/FuzzyMethodContract.java | 26 +++++- 7 files changed, 242 insertions(+), 55 deletions(-) rename ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/{ExactClassMatcher.java => ClassExactMatcher.java} (80%) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ClassRegexMatcher.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ClassSetMatcher.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/AbstractFuzzyMember.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/AbstractFuzzyMember.java index 0aa97a88..6cc0b107 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/AbstractFuzzyMember.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/AbstractFuzzyMember.java @@ -6,6 +6,7 @@ import java.util.regex.Pattern; import javax.annotation.Nonnull; +import com.google.common.base.Objects; import com.google.common.collect.Maps; /** @@ -20,7 +21,7 @@ public abstract class AbstractFuzzyMember extends AbstractFuzz protected int modifiersBanned; protected Pattern nameRegex; - protected AbstractFuzzyMatcher> declaringMatcher = ExactClassMatcher.MATCH_ALL; + protected AbstractFuzzyMatcher> declaringMatcher = ClassExactMatcher.MATCH_ALL; /** * Whether or not this contract can be modified. @@ -256,7 +257,7 @@ public abstract class AbstractFuzzyMember extends AbstractFuzz if (nameRegex != null) { map.put("name", nameRegex.pattern()); } - if (declaringMatcher != ExactClassMatcher.MATCH_ALL) { + if (declaringMatcher != ClassExactMatcher.MATCH_ALL) { map.put("declaring", declaringMatcher); } @@ -271,4 +272,27 @@ public abstract class AbstractFuzzyMember extends AbstractFuzz int snipped = value & ((1 << bits) - 1); return Integer.toBinaryString(snipped); } + + @Override + public boolean equals(Object obj) { + // Immutablity is awesome + if (this == obj) { + return true; + } else if (obj instanceof AbstractFuzzyMember) { + @SuppressWarnings("unchecked") + AbstractFuzzyMember other = (AbstractFuzzyMember) obj; + + return modifiersBanned == other.modifiersBanned && + modifiersRequired == other.modifiersRequired && + FuzzyMatchers.checkPattern(nameRegex, other.nameRegex) && + Objects.equal(declaringMatcher, other.declaringMatcher); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(modifiersBanned, modifiersRequired, + nameRegex != null ? nameRegex.pattern() : null, declaringMatcher); + } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ExactClassMatcher.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ClassExactMatcher.java similarity index 80% rename from ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ExactClassMatcher.java rename to ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ClassExactMatcher.java index 1a0f861a..58213f70 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ExactClassMatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ClassExactMatcher.java @@ -1,11 +1,13 @@ package com.comphenix.protocol.reflect.fuzzy; +import com.google.common.base.Objects; + /** * Used to check class equality. * * @author Kristian */ -class ExactClassMatcher extends AbstractFuzzyMatcher> { +class ClassExactMatcher extends AbstractFuzzyMatcher> { /** * Different matching rules. */ @@ -29,7 +31,7 @@ class ExactClassMatcher extends AbstractFuzzyMatcher> { /** * Match any class. */ - public static final ExactClassMatcher MATCH_ALL = new ExactClassMatcher(null, Options.MATCH_SUPER); + public static final ClassExactMatcher MATCH_ALL = new ClassExactMatcher(null, Options.MATCH_SUPER); private final Class matcher; private final Options option; @@ -39,7 +41,7 @@ class ExactClassMatcher extends AbstractFuzzyMatcher> { * @param matcher - the matching class, or NULL to represent anything. * @param option - options specifying the matching rules. */ - ExactClassMatcher(Class matcher, Options option) { + ClassExactMatcher(Class matcher, Options option) { this.matcher = matcher; this.option = option; } @@ -116,4 +118,22 @@ class ExactClassMatcher extends AbstractFuzzyMatcher> { else return "Exact " + matcher; } + + @Override + public int hashCode() { + return Objects.hashCode(matcher, option); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj instanceof ClassExactMatcher) { + ClassExactMatcher other = (ClassExactMatcher) obj; + + return Objects.equal(matcher, other.matcher) && + Objects.equal(option, other.option); + } + return false; + } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ClassRegexMatcher.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ClassRegexMatcher.java new file mode 100644 index 00000000..f08d5057 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ClassRegexMatcher.java @@ -0,0 +1,58 @@ +package com.comphenix.protocol.reflect.fuzzy; + +import java.util.regex.Pattern; + +import com.google.common.base.Objects; + +/** + * Determine if a class matches based on its name using a regular expression. + * + * @author Kristian + */ +class ClassRegexMatcher extends AbstractFuzzyMatcher> { + private final Pattern regex; + private final int priority; + + public ClassRegexMatcher(Pattern regex, int priority) { + if (regex == null) + throw new IllegalArgumentException("Regular expression pattern cannot be NULL."); + this.regex = regex; + this.priority = priority; + } + + @Override + public boolean isMatch(Class value, Object parent) { + if (value != null) + return regex.matcher(value.getCanonicalName()).matches(); + else + return false; + } + + @Override + protected int calculateRoundNumber() { + return -priority; + } + + @Override + public String toString() { + return "class name of " + regex.toString(); + } + + @Override + public int hashCode() { + return Objects.hashCode(regex, priority); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj instanceof ClassRegexMatcher) { + ClassRegexMatcher other = (ClassRegexMatcher) obj; + + return priority == other.priority && + FuzzyMatchers.checkPattern(regex, other.regex); + } + return false; + } +} \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ClassSetMatcher.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ClassSetMatcher.java new file mode 100644 index 00000000..f67a3634 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/ClassSetMatcher.java @@ -0,0 +1,57 @@ +package com.comphenix.protocol.reflect.fuzzy; + +import java.util.Set; + +import com.google.common.base.Objects; + +/** + * Represents a class matcher that checks for equality using a given set of classes. + * + * @author Kristian + */ +class ClassSetMatcher extends AbstractFuzzyMatcher> { + private final Set> classes; + + public ClassSetMatcher(Set> classes) { + if (classes == null) + throw new IllegalArgumentException("Set of classes cannot be NULL."); + this.classes = classes; + } + + @Override + public boolean isMatch(Class value, Object parent) { + return classes.contains(value); + } + + @Override + protected int calculateRoundNumber() { + int roundNumber = 0; + + // The highest round number (except zero). + for (Class clazz : classes) { + roundNumber = combineRounds(roundNumber, -ClassExactMatcher.getClassNumber(clazz)); + } + return roundNumber; + } + + @Override + public String toString() { + return "match any: " + classes; + } + + @Override + public int hashCode() { + return classes.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj instanceof ClassSetMatcher) { + // See if the sets are equal + return Objects.equal(classes, ((ClassSetMatcher) obj).classes); + } + return true; + } +} \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyFieldContract.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyFieldContract.java index 62e12afc..4f4929d2 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyFieldContract.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyFieldContract.java @@ -6,13 +6,15 @@ import java.util.regex.Pattern; import javax.annotation.Nonnull; +import com.google.common.base.Objects; + /** * Represents a field matcher. * * @author Kristian */ public class FuzzyFieldContract extends AbstractFuzzyMember { - private AbstractFuzzyMatcher> typeMatcher = ExactClassMatcher.MATCH_ALL; + private AbstractFuzzyMatcher> typeMatcher = ClassExactMatcher.MATCH_ALL; /** * Represents a builder for a field matcher. @@ -156,9 +158,25 @@ public class FuzzyFieldContract extends AbstractFuzzyMember { protected Map getKeyValueView() { Map member = super.getKeyValueView(); - if (typeMatcher != ExactClassMatcher.MATCH_ALL) { + if (typeMatcher != ClassExactMatcher.MATCH_ALL) { member.put("type", typeMatcher); } return member; } + + @Override + public int hashCode() { + return Objects.hashCode(typeMatcher, super.hashCode()); + } + + @Override + public boolean equals(Object obj) { + // Use the member equals method + if (this == obj) { + return true; + } else if (obj instanceof FuzzyFieldContract && super.equals(obj)) { + return Objects.equal(typeMatcher, ((FuzzyFieldContract) obj).typeMatcher); + } + return true; + } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyMatchers.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyMatchers.java index bad44d31..8e0f5db6 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyMatchers.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyMatchers.java @@ -4,7 +4,6 @@ import java.lang.reflect.Member; import java.util.Set; import java.util.regex.Pattern; -import com.google.common.base.Joiner; import com.google.common.collect.Sets; /** @@ -23,7 +22,7 @@ public class FuzzyMatchers { * @return A new class mathcher. */ public static AbstractFuzzyMatcher> matchExact(Class matcher) { - return new ExactClassMatcher(matcher, ExactClassMatcher.Options.MATCH_EXACT); + return new ClassExactMatcher(matcher, ClassExactMatcher.Options.MATCH_EXACT); } /** @@ -40,29 +39,8 @@ public class FuzzyMatchers { * @param classes - set of classes to match. * @return A new class mathcher. */ - public static AbstractFuzzyMatcher> matchAnyOf(final Set> classes) { - return new AbstractFuzzyMatcher>() { - @Override - public boolean isMatch(Class value, Object parent) { - return classes.contains(value); - } - - @Override - protected int calculateRoundNumber() { - int roundNumber = 0; - - // The highest round number (except zero). - for (Class clazz : classes) { - roundNumber = combineRounds(roundNumber, -ExactClassMatcher.getClassNumber(clazz)); - } - return roundNumber; - } - - @Override - public String toString() { - return String.format("match any: %s", Joiner.on(",").join(classes)); - } - }; + public static AbstractFuzzyMatcher> matchAnyOf(Set> classes) { + return new ClassSetMatcher(classes); } /** @@ -71,7 +49,7 @@ public class FuzzyMatchers { * @return A new class mathcher. */ public static AbstractFuzzyMatcher> matchSuper(Class matcher) { - return new ExactClassMatcher(matcher, ExactClassMatcher.Options.MATCH_SUPER); + return new ClassExactMatcher(matcher, ClassExactMatcher.Options.MATCH_SUPER); } /** @@ -80,7 +58,7 @@ public class FuzzyMatchers { * @return A new class mathcher. */ public static AbstractFuzzyMatcher> matchDerived(Class matcher) { - return new ExactClassMatcher(matcher, ExactClassMatcher.Options.MATCH_DERIVED); + return new ClassExactMatcher(matcher, ClassExactMatcher.Options.MATCH_DERIVED); } /** @@ -90,25 +68,7 @@ public class FuzzyMatchers { * @return A fuzzy class matcher based on name. */ public static AbstractFuzzyMatcher> matchRegex(final Pattern regex, final int priority) { - return new AbstractFuzzyMatcher>() { - @Override - public boolean isMatch(Class value, Object parent) { - if (value != null) - return regex.matcher(value.getCanonicalName()).matches(); - else - return false; - } - - @Override - protected int calculateRoundNumber() { - return -priority; - } - - @Override - public String toString() { - return "class name of " + regex.toString(); - } - }; + return new ClassRegexMatcher(regex, priority); } /** @@ -149,6 +109,34 @@ public class FuzzyMatchers { public String toString() { return "match parent class"; } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object obj) { + // If they're the same type, then yes + return obj != null && obj.getClass() == this.getClass(); + } }; } + + /** + * Determine if two patterns are the same. + *

+ * Note that two patterns may be functionally the same, but nevertheless be different. + * @param a - the first pattern. + * @param b - the second pattern. + * @return TRUE if they are compiled from the same pattern, FALSE otherwise. + */ + static boolean checkPattern(Pattern a, Pattern b) { + if (a == null) + return b == null; + else if (b == null) + return false; + else + return a.pattern().equals(b.pattern()); + } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyMethodContract.java b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyMethodContract.java index 4e08f226..2fe0da85 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyMethodContract.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/reflect/fuzzy/FuzzyMethodContract.java @@ -10,6 +10,7 @@ import javax.annotation.Nonnull; import org.apache.commons.lang.NotImplementedException; import com.comphenix.protocol.reflect.MethodInfo; +import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; @@ -79,7 +80,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { } // Match return value - private AbstractFuzzyMatcher> returnMatcher = ExactClassMatcher.MATCH_ALL; + private AbstractFuzzyMatcher> returnMatcher = ClassExactMatcher.MATCH_ALL; // Handle parameters and exceptions private List paramMatchers; @@ -500,7 +501,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { Map member = super.getKeyValueView(); // Only add fields that are actual contraints - if (returnMatcher != ExactClassMatcher.MATCH_ALL) { + if (returnMatcher != ClassExactMatcher.MATCH_ALL) { member.put("return", returnMatcher); } if (paramMatchers.size() > 0) { @@ -514,4 +515,25 @@ public class FuzzyMethodContract extends AbstractFuzzyMember { } return member; } + + @Override + public int hashCode() { + return Objects.hashCode(returnMatcher, paramMatchers, exceptionMatchers, paramCount, super.hashCode()); + } + + @Override + public boolean equals(Object obj) { + // Use the member equals method + if (this == obj) { + return true; + } else if (obj instanceof FuzzyMethodContract && super.equals(obj)) { + FuzzyMethodContract other = (FuzzyMethodContract) obj; + + return Objects.equal(paramCount, other.paramCount) && + Objects.equal(returnMatcher, other.returnMatcher) && + Objects.equal(paramMatchers, other.paramMatchers) && + Objects.equal(exceptionMatchers, other.exceptionMatchers); + } + return true; + } }