Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.amygdalum.testrecorder.runtime.GenericMatcher Maven / Gradle / Ivy
package net.amygdalum.testrecorder.runtime;
import static java.util.stream.Collectors.toList;
import static net.amygdalum.testrecorder.runtime.GenericComparatorResult.MATCH;
import static net.amygdalum.testrecorder.runtime.GenericComparatorResult.MISMATCH;
import static net.amygdalum.testrecorder.runtime.GenericComparatorResult.NOT_APPLYING;
import static net.amygdalum.testrecorder.util.Reflections.getValue;
import static net.amygdalum.testrecorder.util.Types.allFields;
import static org.hamcrest.CoreMatchers.instanceOf;
import java.lang.reflect.Field;
import java.util.List;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.SelfDescribing;
import org.hamcrest.StringDescription;
import org.hamcrest.TypeSafeMatcher;
import net.amygdalum.testrecorder.util.WorkSet;
public class GenericMatcher extends GenericObject {
public Matcher matching(Class clazz) {
return new InternalsMatcher(clazz);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Matcher matching(Wrapped wrapped) {
return (Matcher) new InternalsMatcher(wrapped.getWrappedClass());
}
public Matcher matching(Class clazz, Class to) {
return new CastingMatcher<>(to, new InternalsMatcher(clazz));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public Matcher matching(Wrapped clazz, Class to) {
return (Matcher) new CastingMatcher(to, new InternalsMatcher(clazz.getWrappedClass()));
}
public List mismatchesWith(String root, Object o) {
WorkSet remainder = new WorkSet<>();
for (Field field : getGenericFields(o.getClass())) {
GenericComparison comparison = getQualifiedField(o.getClass(), field.getName())
.map(qfield -> GenericComparison.from(root, field, this, qfield, o))
.orElseGet(() -> GenericComparison.from(root, field.getName(), this, o));
remainder.add(comparison);
}
GenericComparison.compare(remainder, GenericMatcher::matching);
return remainder.getDone().stream()
.filter(done -> done.isMismatch())
.collect(toList());
}
private static GenericComparatorResult matching(GenericComparison comparison, WorkSet todo) {
Object left = comparison.getLeft();
Object right = comparison.getRight();
if (left instanceof RecursiveMatcher && right != null) {
RecursiveMatcher matcher = (RecursiveMatcher) left;
todo.addAll(matcher.mismatchesWith(comparison.getRoot(), right));
}
if (left instanceof Matcher>) {
Matcher> matcher = (Matcher>) left;
if (matcher.matches(right)) {
return MATCH;
} else {
return MISMATCH;
}
}
return NOT_APPLYING;
}
public static Matcher recursive(Class clazz) {
return instanceOf(clazz);
}
public static Matcher> recursive(Wrapped wrapped) {
return instanceOf(wrapped.getWrappedClass());
}
private class InternalsMatcher extends TypeSafeMatcher implements RecursiveMatcher {
private Class clazz;
InternalsMatcher(Class clazz) {
this.clazz = clazz;
}
@Override
public void describeTo(Description description) {
description.appendText(clazz.getName()).appendText(" {");
for (Field field : allFields(clazz)) {
describeField(description, field, GenericMatcher.this);
}
description.appendText("\n}");
}
@Override
public List mismatchesWith(String root, Object item) {
return GenericMatcher.this.mismatchesWith(root, item);
}
@Override
protected boolean matchesSafely(T item) {
Class> itemClass = item.getClass();
if (isSynthetic(itemClass)) {
if (!clazz.isAssignableFrom(itemClass)) {
return false;
}
} else if (clazz != itemClass) {
return false;
}
List mismatches = mismatchesWith(null, item);
return mismatches.isEmpty();
}
private boolean isSynthetic(Class> itemClass) {
return itemClass.isSynthetic()
|| itemClass.getSimpleName().contains("$");
}
@Override
protected void describeMismatchSafely(T item, Description mismatchDescription) {
List mismatches = mismatchesWith(null, item);
if (!mismatches.isEmpty()) {
mismatchDescription.appendText(item.getClass().getName()).appendText(" {");
for (Field field : allFields(item.getClass())) {
describeField(mismatchDescription, field, item);
}
mismatchDescription.appendText("\n}");
mismatchDescription.appendText("\nfound mismatches at:");
for (GenericComparison mismatch : mismatches) {
if (!(mismatch.getLeft() instanceof RecursiveMatcher)) {
describeMismatch(mismatchDescription, mismatch);
}
}
}
}
private void describeField(Description description, Field field, Object object) {
try {
description.appendText("\n\t")
.appendText(field.getType().getSimpleName()).appendText(" ")
.appendText(field.getName()).appendText(": ");
Object value = getValue(field.getName(), object);
describe(description, value);
description.appendText(";");
} catch (ReflectiveOperationException e) {
description.appendText("\n\t")
.appendValue(field.getType()).appendText(" ")
.appendValue(field.getName()).appendText(":");
}
}
private void describeMismatch(Description description, GenericComparison mismatch) {
description.appendText("\n\t")
.appendText(mismatch.getRoot()).appendText(": ");
if (mismatch.getLeft() instanceof Matcher>) {
((Matcher>) mismatch.getLeft()).describeMismatch(mismatch.getRight(), description);
} else {
describe(description, mismatch.getLeft());
description.appendText(" != ");
describe(description, mismatch.getRight());
}
}
private void describe(Description description, Object value) {
StringDescription subdescription = new StringDescription();
if (value instanceof SelfDescribing) {
subdescription.appendDescriptionOf((SelfDescribing) value);
} else {
subdescription.appendValue(value);
}
description.appendText(subdescription.toString().replace("\n", "\n\t"));
}
}
private class CastingMatcher extends TypeSafeMatcher implements RecursiveMatcher {
private Class clazz;
private Matcher matcher;
CastingMatcher(Class clazz, Matcher matcher) {
this.clazz = clazz;
this.matcher = matcher;
}
@Override
public void describeTo(Description description) {
description.appendDescriptionOf(matcher);
}
@Override
public List mismatchesWith(String root, Object item) {
return GenericMatcher.this.mismatchesWith(root, item);
}
@Override
protected boolean matchesSafely(S item) {
if (!clazz.isInstance(item)) {
return false;
}
if (!matcher.matches(item)) {
return false;
}
return true;
}
}
}