304 lines
8.8 KiB
Java
304 lines
8.8 KiB
Java
package com.comphenix.protocol.reflect.fuzzy;
|
|
|
|
import java.lang.reflect.Field;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
|
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
|
import com.comphenix.protocol.reflect.MethodInfo;
|
|
import com.google.common.collect.ImmutableList;
|
|
|
|
/**
|
|
* Determine if a given class implements a given fuzzy (duck typed) contract.
|
|
*
|
|
* @author Kristian
|
|
*/
|
|
public class FuzzyClassContract implements AbstractFuzzyMatcher<Class<?>> {
|
|
|
|
private final ImmutableList<AbstractFuzzyMatcher<Field>> fieldContracts;
|
|
private final ImmutableList<AbstractFuzzyMatcher<MethodInfo>> methodContracts;
|
|
private final ImmutableList<AbstractFuzzyMatcher<MethodInfo>> constructorContracts;
|
|
|
|
private final ImmutableList<AbstractFuzzyMatcher<Class<?>>> baseclassContracts;
|
|
private final ImmutableList<AbstractFuzzyMatcher<Class<?>>> interfaceContracts;
|
|
|
|
/**
|
|
* Constructs a new fuzzy class contract with the given contracts.
|
|
*
|
|
* @param builder - the builder that is constructing us.
|
|
*/
|
|
private FuzzyClassContract(Builder builder) {
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Construct a new fuzzy class contract builder.
|
|
*
|
|
* @return A new builder.
|
|
*/
|
|
public static Builder newBuilder() {
|
|
return new Builder();
|
|
}
|
|
|
|
/**
|
|
* Retrieve an immutable list of every field contract.
|
|
* <p>
|
|
* This list is ordered in descending order of priority.
|
|
*
|
|
* @return List of every field contract.
|
|
*/
|
|
public ImmutableList<AbstractFuzzyMatcher<Field>> getFieldContracts() {
|
|
return this.fieldContracts;
|
|
}
|
|
|
|
/**
|
|
* Retrieve an immutable list of every method contract.
|
|
* <p>
|
|
* This list is ordered in descending order of priority.
|
|
*
|
|
* @return List of every method contract.
|
|
*/
|
|
public ImmutableList<AbstractFuzzyMatcher<MethodInfo>> getMethodContracts() {
|
|
return this.methodContracts;
|
|
}
|
|
|
|
/**
|
|
* Retrieve an immutable list of every constructor contract.
|
|
* <p>
|
|
* This list is ordered in descending order of priority.
|
|
*
|
|
* @return List of every constructor contract.
|
|
*/
|
|
public ImmutableList<AbstractFuzzyMatcher<MethodInfo>> getConstructorContracts() {
|
|
return this.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 this.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 this.interfaceContracts;
|
|
}
|
|
|
|
@Override
|
|
public boolean isMatch(Class<?> value, Object parent) {
|
|
FuzzyReflection reflection = FuzzyReflection.fromClass(value, true);
|
|
|
|
// Make sure all the contracts are valid
|
|
return this.processValue(value.getSuperclass(), parent, this.baseclassContracts)
|
|
&& this.processContracts(Arrays.asList(value.getInterfaces()), parent, this.interfaceContracts)
|
|
&& this.processContracts(reflection.getFields(), value, this.fieldContracts)
|
|
&& this.processContracts(MethodInfo.fromMethods(reflection.getMethods()), value, this.methodContracts)
|
|
&& this.processContracts(MethodInfo.fromConstructors(value.getDeclaredConstructors()), value,
|
|
this.constructorContracts);
|
|
}
|
|
|
|
private <T> boolean processContracts(Collection<T> values, Object parent, List<AbstractFuzzyMatcher<T>> matchers) {
|
|
// they all match if no values are present
|
|
if (values.isEmpty() || matchers.isEmpty()) {
|
|
return true;
|
|
}
|
|
|
|
// check if all contracts match the given objects
|
|
int acceptingMatchers = 0;
|
|
for (T value : values) {
|
|
if (this.processValue(value, parent, matchers)) {
|
|
acceptingMatchers++;
|
|
// if all matchers found one match we're done
|
|
if (acceptingMatchers == matchers.size()) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private <T> boolean processValue(T value, Object parent, List<AbstractFuzzyMatcher<T>> matchers) {
|
|
// check if all given contracts match the given value
|
|
for (AbstractFuzzyMatcher<T> matcher : matchers) {
|
|
if (!matcher.isMatch(value, parent)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// they all match
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
StringBuilder builder = new StringBuilder("FuzzyClassContract={\n");
|
|
|
|
// append all subcontracts
|
|
if (!this.fieldContracts.isEmpty()) {
|
|
builder.append(" fields=").append(this.fieldContracts).append("\n");
|
|
}
|
|
|
|
if (!this.methodContracts.isEmpty()) {
|
|
builder.append(" methods=").append(this.methodContracts).append("\n");
|
|
}
|
|
|
|
if (!this.constructorContracts.isEmpty()) {
|
|
builder.append(" constructors=").append(this.constructorContracts).append("\n");
|
|
}
|
|
|
|
if (!this.baseclassContracts.isEmpty()) {
|
|
builder.append(" baseClasses=").append(this.baseclassContracts).append("\n");
|
|
}
|
|
|
|
if (!this.interfaceContracts.isEmpty()) {
|
|
builder.append(" interfaceClasses=").append(this.interfaceContracts).append("\n");
|
|
}
|
|
|
|
// finish off
|
|
return builder.append("}").toString();
|
|
}
|
|
|
|
/**
|
|
* Represents a class contract builder.
|
|
*
|
|
* @author Kristian
|
|
*/
|
|
public static final class Builder {
|
|
|
|
private final List<AbstractFuzzyMatcher<Field>> fieldContracts = new ArrayList<>();
|
|
private final List<AbstractFuzzyMatcher<MethodInfo>> methodContracts = new ArrayList<>();
|
|
private final List<AbstractFuzzyMatcher<MethodInfo>> constructorContracts = new ArrayList<>();
|
|
|
|
private final List<AbstractFuzzyMatcher<Class<?>>> baseclassContracts = new ArrayList<>();
|
|
private final List<AbstractFuzzyMatcher<Class<?>>> interfaceContracts = new ArrayList<>();
|
|
|
|
/**
|
|
* Add a new field contract.
|
|
*
|
|
* @param matcher - new field contract.
|
|
* @return This builder, for chaining.
|
|
*/
|
|
public Builder field(AbstractFuzzyMatcher<Field> matcher) {
|
|
this.fieldContracts.add(matcher);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Add a new field contract via a builder.
|
|
*
|
|
* @param builder - builder for the new field contract.
|
|
* @return This builder, for chaining.
|
|
*/
|
|
public Builder field(FuzzyFieldContract.Builder builder) {
|
|
return this.field(builder.build());
|
|
}
|
|
|
|
/**
|
|
* Add a new method contract.
|
|
*
|
|
* @param matcher - new method contract.
|
|
* @return This builder, for chaining.
|
|
*/
|
|
public Builder method(AbstractFuzzyMatcher<MethodInfo> matcher) {
|
|
this.methodContracts.add(matcher);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Add a new method contract via a builder.
|
|
*
|
|
* @param builder - builder for the new method contract.
|
|
* @return This builder, for chaining.
|
|
*/
|
|
public Builder method(FuzzyMethodContract.Builder builder) {
|
|
return this.method(builder.build());
|
|
}
|
|
|
|
/**
|
|
* Add a new constructor contract.
|
|
*
|
|
* @param matcher - new constructor contract.
|
|
* @return This builder, for chaining.
|
|
*/
|
|
public Builder constructor(AbstractFuzzyMatcher<MethodInfo> matcher) {
|
|
this.constructorContracts.add(matcher);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Add a new constructor contract via a builder.
|
|
*
|
|
* @param builder - builder for the new constructor contract.
|
|
* @return This builder, for chaining.
|
|
*/
|
|
public Builder constructor(FuzzyMethodContract.Builder builder) {
|
|
return this.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) {
|
|
this.baseclassContracts.add(matcher);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Add a new base class contract.
|
|
*
|
|
* @param builder - builder for the new base class contract.
|
|
* @return This builder, for chaining.
|
|
*/
|
|
public Builder baseclass(FuzzyClassContract.Builder builder) {
|
|
return this.baseclass(builder.build());
|
|
}
|
|
|
|
/**
|
|
* Add a new interface contract.
|
|
*
|
|
* @param matcher - new interface contract.
|
|
* @return This builder, for chaining.
|
|
*/
|
|
public Builder interfaces(AbstractFuzzyMatcher<Class<?>> matcher) {
|
|
this.interfaceContracts.add(matcher);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Add a new interface contract.
|
|
*
|
|
* @param builder - builder for the new interface contract.
|
|
* @return This builder, for chaining.
|
|
*/
|
|
public Builder interfaces(FuzzyClassContract.Builder builder) {
|
|
return this.interfaces(builder.build());
|
|
}
|
|
|
|
public FuzzyClassContract build() {
|
|
// Construct a new class matcher
|
|
return new FuzzyClassContract(this);
|
|
}
|
|
}
|
|
}
|