Added the ability to match derived classes and interfaces.

This commit is contained in:
Kristian S. Stangeland 2013-04-08 17:57:56 +02:00
parent 867afe29f7
commit 505226f8ad
2 changed files with 127 additions and 21 deletions

View File

@ -63,6 +63,25 @@ public abstract class AbstractFuzzyMatcher<T> implements Comparable<AbstractFuzz
return Math.max(roundA, roundB);
}
/**
* Combine n round numbers by taking the highest non-zero number, or return zero.
* @param rounds - the round numbers.
* @return The combined round number.
*/
protected final int combineRounds(Integer... rounds) {
if (rounds.length < 2)
throw new IllegalArgumentException("Must supply at least two arguments.");
// Get the seed
int reduced = combineRounds(rounds[0], rounds[1]);
// Aggregate it all
for (int i = 2; i < rounds.length; i++) {
reduced = combineRounds(reduced, rounds[i]);
}
return reduced;
}
@Override
public int compareTo(AbstractFuzzyMatcher<T> obj) {
if (obj instanceof AbstractFuzzyMatcher) {

View File

@ -1,6 +1,7 @@
package com.comphenix.protocol.reflect.fuzzy;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@ -23,6 +24,9 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
private final ImmutableList<AbstractFuzzyMatcher<MethodInfo>> methodContracts;
private final ImmutableList<AbstractFuzzyMatcher<MethodInfo>> constructorContracts;
private final ImmutableList<AbstractFuzzyMatcher<Class<?>>> baseclassContracts;
private final ImmutableList<AbstractFuzzyMatcher<Class<?>>> interfaceContracts;
/**
* Represents a class contract builder.
* @author Kristian
@ -33,6 +37,9 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
private List<AbstractFuzzyMatcher<MethodInfo>> methodContracts = Lists.newArrayList();
private List<AbstractFuzzyMatcher<MethodInfo>> constructorContracts = Lists.newArrayList();
private List<AbstractFuzzyMatcher<Class<?>>> baseclassContracts = Lists.newArrayList();
private List<AbstractFuzzyMatcher<Class<?>>> interfaceContracts = Lists.newArrayList();
/**
* Add a new field contract.
* @param matcher - new field contract.
@ -90,17 +97,53 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
return constructor(builder.build());
}
/**
* Add a new base class contract.
* @param matcher - new base class contract.
* @return This builder, for chaining.
*/
public Builder baseclass(AbstractFuzzyMatcher<Class<?>> matcher) {
baseclassContracts.add(matcher);
return this;
}
/**
* Add a new base class contract.
* @param matcher - builder for the new base class contract.
* @return This builder, for chaining.
*/
public Builder baseclass(FuzzyClassContract.Builder builder) {
return baseclass(builder.build());
}
/**
* Add a new interface contract.
* @param matcher - new interface contract.
* @return This builder, for chaining.
*/
public Builder interfaces(AbstractFuzzyMatcher<Class<?>> matcher) {
interfaceContracts.add(matcher);
return this;
}
/**
* Add a new interface contract.
* @param matcher - builder for the new interface contract.
* @return This builder, for chaining.
*/
public Builder interfaces(FuzzyClassContract.Builder builder) {
return interfaces(builder.build());
}
public FuzzyClassContract build() {
Collections.sort(fieldContracts);
Collections.sort(methodContracts);
Collections.sort(constructorContracts);
Collections.sort(baseclassContracts);
Collections.sort(interfaceContracts);
// Construct a new class matcher
return new FuzzyClassContract(
ImmutableList.copyOf(fieldContracts),
ImmutableList.copyOf(methodContracts),
ImmutableList.copyOf(constructorContracts)
);
return new FuzzyClassContract(this);
}
}
@ -114,17 +157,15 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
/**
* Constructs a new fuzzy class contract with the given contracts.
* @param fieldContracts - field contracts.
* @param methodContracts - method contracts.
* @param constructorContracts - constructor contracts.
* @param builder - the builder that is constructing us.
*/
private FuzzyClassContract(ImmutableList<AbstractFuzzyMatcher<Field>> fieldContracts,
ImmutableList<AbstractFuzzyMatcher<MethodInfo>> methodContracts,
ImmutableList<AbstractFuzzyMatcher<MethodInfo>> constructorContracts) {
private FuzzyClassContract(Builder builder) {
super();
this.fieldContracts = fieldContracts;
this.methodContracts = methodContracts;
this.constructorContracts = constructorContracts;
this.fieldContracts = ImmutableList.copyOf(builder.fieldContracts);
this.methodContracts = ImmutableList.copyOf(builder.methodContracts);
this.constructorContracts = ImmutableList.copyOf(builder.constructorContracts);
this.baseclassContracts = ImmutableList.copyOf(builder.baseclassContracts);
this.interfaceContracts = ImmutableList.copyOf(builder.interfaceContracts);
}
/**
@ -157,12 +198,34 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
return constructorContracts;
}
/**
* Retrieve an immutable list of every baseclass contract.
* <p>
* This list is ordered in descending order of priority.
* @return List of every baseclass contract.
*/
public ImmutableList<AbstractFuzzyMatcher<Class<?>>> getBaseclassContracts() {
return baseclassContracts;
}
/**
* Retrieve an immutable list of every interface contract.
* <p>
* This list is ordered in descending order of priority.
* @return List of every interface contract.
*/
public ImmutableList<AbstractFuzzyMatcher<Class<?>>> getInterfaceContracts() {
return interfaceContracts;
}
@Override
protected int calculateRoundNumber() {
// Find the highest round number
return combineRounds(findHighestRound(fieldContracts),
combineRounds(findHighestRound(methodContracts),
findHighestRound(constructorContracts)));
findHighestRound(methodContracts),
findHighestRound(constructorContracts),
findHighestRound(interfaceContracts),
findHighestRound(baseclassContracts));
}
private <T> int findHighestRound(Collection<AbstractFuzzyMatcher<T>> list) {
@ -179,12 +242,19 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
FuzzyReflection reflection = FuzzyReflection.fromClass(value, true);
// Make sure all the contracts are valid
return processContracts(reflection.getFields(), value, fieldContracts) &&
processContracts(MethodInfo.fromMethods(reflection.getMethods()), value, methodContracts) &&
processContracts(MethodInfo.fromConstructors(value.getDeclaredConstructors()), value, constructorContracts);
return (fieldContracts.size() == 0 ||
processContracts(reflection.getFields(), value, fieldContracts)) &&
(methodContracts.size() == 0 ||
processContracts(MethodInfo.fromMethods(reflection.getMethods()), value, methodContracts)) &&
(constructorContracts.size() == 0 ||
processContracts(MethodInfo.fromConstructors(value.getDeclaredConstructors()), value, constructorContracts)) &&
(baseclassContracts.size() == 0 ||
processValue(value.getSuperclass(), parent, baseclassContracts)) &&
(interfaceContracts.size() == 0 ||
processContracts(Arrays.asList(value.getInterfaces()), (Class<?>) parent, interfaceContracts));
}
private <T> boolean processContracts(Collection<T> values, Class<?> parent, List<AbstractFuzzyMatcher<T>> matchers) {
private <T> boolean processContracts(Collection<T> values, Object parent, List<AbstractFuzzyMatcher<T>> matchers) {
boolean[] accepted = new boolean[matchers.size()];
int count = accepted.length;
@ -205,7 +275,18 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
return count == 0;
}
private <T> int processValue(T value, Class<?> parent, boolean accepted[], List<AbstractFuzzyMatcher<T>> matchers) {
private <T> boolean processValue(T value, Object parent, List<AbstractFuzzyMatcher<T>> matchers) {
for (int i = 0; i < matchers.size(); i++) {
if (matchers.get(i).isMatch(value, parent)) {
return true;
}
}
// No match
return false;
}
private <T> int processValue(T value, Object parent, boolean accepted[], List<AbstractFuzzyMatcher<T>> matchers) {
// The order matters
for (int i = 0; i < matchers.size(); i++) {
if (!accepted[i]) {
@ -235,6 +316,12 @@ public class FuzzyClassContract extends AbstractFuzzyMatcher<Class<?>> {
if (constructorContracts.size() > 0) {
params.put("constructors", constructorContracts);
}
if (baseclassContracts.size() > 0) {
params.put("baseclasses", baseclassContracts);
}
if (interfaceContracts.size() > 0) {
params.put("interfaces", interfaceContracts);
}
return "{\n " + Joiner.on(", \n ").join(params.entrySet()) + "\n}";
}
}