Add an equality and hashCode() method to all the matchers.

This commit is contained in:
Kristian S. Stangeland 2013-02-03 04:47:02 +01:00
parent 75f05732bb
commit eaf7ea9618
7 changed files with 242 additions and 55 deletions

View File

@ -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<T extends Member> extends AbstractFuzz
protected int modifiersBanned;
protected Pattern nameRegex;
protected AbstractFuzzyMatcher<Class<?>> declaringMatcher = ExactClassMatcher.MATCH_ALL;
protected AbstractFuzzyMatcher<Class<?>> declaringMatcher = ClassExactMatcher.MATCH_ALL;
/**
* Whether or not this contract can be modified.
@ -256,7 +257,7 @@ public abstract class AbstractFuzzyMember<T extends Member> 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<T extends Member> 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<T> other = (AbstractFuzzyMember<T>) 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);
}
}

View File

@ -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<?>> {
class ClassExactMatcher extends AbstractFuzzyMatcher<Class<?>> {
/**
* Different matching rules.
*/
@ -29,7 +31,7 @@ class ExactClassMatcher extends AbstractFuzzyMatcher<Class<?>> {
/**
* 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<Class<?>> {
* @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<Class<?>> {
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;
}
}

View File

@ -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<Class<?>> {
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;
}
}

View File

@ -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<Class<?>> {
private final Set<Class<?>> classes;
public ClassSetMatcher(Set<Class<?>> 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;
}
}

View File

@ -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<Field> {
private AbstractFuzzyMatcher<Class<?>> typeMatcher = ExactClassMatcher.MATCH_ALL;
private AbstractFuzzyMatcher<Class<?>> typeMatcher = ClassExactMatcher.MATCH_ALL;
/**
* Represents a builder for a field matcher.
@ -156,9 +158,25 @@ public class FuzzyFieldContract extends AbstractFuzzyMember<Field> {
protected Map<String, Object> getKeyValueView() {
Map<String, Object> 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;
}
}

View File

@ -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<Class<?>> 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<Class<?>> matchAnyOf(final Set<Class<?>> classes) {
return new AbstractFuzzyMatcher<Class<?>>() {
@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<Class<?>> matchAnyOf(Set<Class<?>> classes) {
return new ClassSetMatcher(classes);
}
/**
@ -71,7 +49,7 @@ public class FuzzyMatchers {
* @return A new class mathcher.
*/
public static AbstractFuzzyMatcher<Class<?>> 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<Class<?>> 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<Class<?>> matchRegex(final Pattern regex, final int priority) {
return new AbstractFuzzyMatcher<Class<?>>() {
@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.
* <p>
* 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());
}
}

View File

@ -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<MethodInfo> {
}
// Match return value
private AbstractFuzzyMatcher<Class<?>> returnMatcher = ExactClassMatcher.MATCH_ALL;
private AbstractFuzzyMatcher<Class<?>> returnMatcher = ClassExactMatcher.MATCH_ALL;
// Handle parameters and exceptions
private List<ParameterClassMatcher> paramMatchers;
@ -500,7 +501,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
Map<String, Object> 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<MethodInfo> {
}
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;
}
}