FeatureMatcher.java

package uk.org.lidalia.test;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import org.hamcrest.internal.ReflectiveTypeFinder;

/**
 * Supporting class for matching a feature of an object. Implement <code>featureValueOf()</code>
 * in a subclass to pull out the feature to be matched against.
 *
 * @param <T> The type of the object to be matched
 * @param <U> The type of the feature to be matched
 */
abstract class FeatureMatcher<T, U> extends TypeSafeDiagnosingMatcher<T> {
    private static final ReflectiveTypeFinder TYPE_FINDER = new ReflectiveTypeFinder("featureValueOf", 1, 0);
    private final Matcher<? extends U> subMatcher;
    private final String featureDescription;
    private final String featureName;

    /**
     * @param subMatcher         The matcher to apply to the feature
     * @param featureDescription Descriptive text to use in describeTo
     * @param featureName        Identifying text for mismatch message
     */
    FeatureMatcher(final Matcher<? extends U> subMatcher, final String featureDescription, final String featureName) {
        super(TYPE_FINDER);
        this.subMatcher = subMatcher;
        this.featureDescription = featureDescription;
        this.featureName = featureName;
    }

    /**
     * Implement this to extract the interesting feature.
     *
     * @param actual the target object
     * @return the feature to be matched
     */
    protected abstract U featureValueOf(T actual);

    @Override
    protected boolean matchesSafely(final T actual, final Description mismatch) {
        final U featureValue = featureValueOf(actual);
        if (!subMatcher.matches(featureValue)) {
            mismatch.appendValue(actual).appendText(featureName).appendText(" ");
            subMatcher.describeMismatch(featureValue, mismatch);
            return false;
        }
        return true;
    }

    @Override
    public final void describeTo(final Description description) {
        description.appendText(featureDescription).appendText(" ")
                .appendDescriptionOf(subMatcher);
    }
}