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 javax.annotation.Nonnull;
import com.google.common.base.Objects;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
/** /**
@ -20,7 +21,7 @@ public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzz
protected int modifiersBanned; protected int modifiersBanned;
protected Pattern nameRegex; 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. * Whether or not this contract can be modified.
@ -256,7 +257,7 @@ public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzz
if (nameRegex != null) { if (nameRegex != null) {
map.put("name", nameRegex.pattern()); map.put("name", nameRegex.pattern());
} }
if (declaringMatcher != ExactClassMatcher.MATCH_ALL) { if (declaringMatcher != ClassExactMatcher.MATCH_ALL) {
map.put("declaring", declaringMatcher); map.put("declaring", declaringMatcher);
} }
@ -271,4 +272,27 @@ public abstract class AbstractFuzzyMember<T extends Member> extends AbstractFuzz
int snipped = value & ((1 << bits) - 1); int snipped = value & ((1 << bits) - 1);
return Integer.toBinaryString(snipped); 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; package com.comphenix.protocol.reflect.fuzzy;
import com.google.common.base.Objects;
/** /**
* Used to check class equality. * Used to check class equality.
* *
* @author Kristian * @author Kristian
*/ */
class ExactClassMatcher extends AbstractFuzzyMatcher<Class<?>> { class ClassExactMatcher extends AbstractFuzzyMatcher<Class<?>> {
/** /**
* Different matching rules. * Different matching rules.
*/ */
@ -29,7 +31,7 @@ class ExactClassMatcher extends AbstractFuzzyMatcher<Class<?>> {
/** /**
* Match any 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 Class<?> matcher;
private final Options option; private final Options option;
@ -39,7 +41,7 @@ class ExactClassMatcher extends AbstractFuzzyMatcher<Class<?>> {
* @param matcher - the matching class, or NULL to represent anything. * @param matcher - the matching class, or NULL to represent anything.
* @param option - options specifying the matching rules. * @param option - options specifying the matching rules.
*/ */
ExactClassMatcher(Class<?> matcher, Options option) { ClassExactMatcher(Class<?> matcher, Options option) {
this.matcher = matcher; this.matcher = matcher;
this.option = option; this.option = option;
} }
@ -116,4 +118,22 @@ class ExactClassMatcher extends AbstractFuzzyMatcher<Class<?>> {
else else
return "Exact " + matcher; 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 javax.annotation.Nonnull;
import com.google.common.base.Objects;
/** /**
* Represents a field matcher. * Represents a field matcher.
* *
* @author Kristian * @author Kristian
*/ */
public class FuzzyFieldContract extends AbstractFuzzyMember<Field> { 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. * Represents a builder for a field matcher.
@ -156,9 +158,25 @@ public class FuzzyFieldContract extends AbstractFuzzyMember<Field> {
protected Map<String, Object> getKeyValueView() { protected Map<String, Object> getKeyValueView() {
Map<String, Object> member = super.getKeyValueView(); Map<String, Object> member = super.getKeyValueView();
if (typeMatcher != ExactClassMatcher.MATCH_ALL) { if (typeMatcher != ClassExactMatcher.MATCH_ALL) {
member.put("type", typeMatcher); member.put("type", typeMatcher);
} }
return member; 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.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import com.google.common.base.Joiner;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
/** /**
@ -23,7 +22,7 @@ public class FuzzyMatchers {
* @return A new class mathcher. * @return A new class mathcher.
*/ */
public static AbstractFuzzyMatcher<Class<?>> matchExact(Class<?> matcher) { 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. * @param classes - set of classes to match.
* @return A new class mathcher. * @return A new class mathcher.
*/ */
public static AbstractFuzzyMatcher<Class<?>> matchAnyOf(final Set<Class<?>> classes) { public static AbstractFuzzyMatcher<Class<?>> matchAnyOf(Set<Class<?>> classes) {
return new AbstractFuzzyMatcher<Class<?>>() { return new ClassSetMatcher(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, -ExactClassMatcher.getClassNumber(clazz));
}
return roundNumber;
}
@Override
public String toString() {
return String.format("match any: %s", Joiner.on(",").join(classes));
}
};
} }
/** /**
@ -71,7 +49,7 @@ public class FuzzyMatchers {
* @return A new class mathcher. * @return A new class mathcher.
*/ */
public static AbstractFuzzyMatcher<Class<?>> matchSuper(Class<?> matcher) { 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. * @return A new class mathcher.
*/ */
public static AbstractFuzzyMatcher<Class<?>> matchDerived(Class<?> matcher) { 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. * @return A fuzzy class matcher based on name.
*/ */
public static AbstractFuzzyMatcher<Class<?>> matchRegex(final Pattern regex, final int priority) { public static AbstractFuzzyMatcher<Class<?>> matchRegex(final Pattern regex, final int priority) {
return new AbstractFuzzyMatcher<Class<?>>() { return new ClassRegexMatcher(regex, 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();
}
};
} }
/** /**
@ -149,6 +109,34 @@ public class FuzzyMatchers {
public String toString() { public String toString() {
return "match parent class"; 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 org.apache.commons.lang.NotImplementedException;
import com.comphenix.protocol.reflect.MethodInfo; import com.comphenix.protocol.reflect.MethodInfo;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@ -79,7 +80,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
} }
// Match return value // Match return value
private AbstractFuzzyMatcher<Class<?>> returnMatcher = ExactClassMatcher.MATCH_ALL; private AbstractFuzzyMatcher<Class<?>> returnMatcher = ClassExactMatcher.MATCH_ALL;
// Handle parameters and exceptions // Handle parameters and exceptions
private List<ParameterClassMatcher> paramMatchers; private List<ParameterClassMatcher> paramMatchers;
@ -500,7 +501,7 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
Map<String, Object> member = super.getKeyValueView(); Map<String, Object> member = super.getKeyValueView();
// Only add fields that are actual contraints // Only add fields that are actual contraints
if (returnMatcher != ExactClassMatcher.MATCH_ALL) { if (returnMatcher != ClassExactMatcher.MATCH_ALL) {
member.put("return", returnMatcher); member.put("return", returnMatcher);
} }
if (paramMatchers.size() > 0) { if (paramMatchers.size() > 0) {
@ -514,4 +515,25 @@ public class FuzzyMethodContract extends AbstractFuzzyMember<MethodInfo> {
} }
return member; 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;
}
} }