001    package org.hamcrest;
002    
003    import org.hamcrest.internal.ReflectiveTypeFinder;
004    
005    /**
006     * Supporting class for matching a feature of an object. Implement <code>featureValueOf()</code>
007     * in a subclass to pull out the feature to be matched against. 
008     *
009     * @param <T> The type of the object to be matched
010     * @param <U> The type of the feature to be matched
011     */
012    public abstract class FeatureMatcher<T, U> extends TypeSafeDiagnosingMatcher<T> {
013      private static final ReflectiveTypeFinder TYPE_FINDER = new ReflectiveTypeFinder("featureValueOf", 1, 0); 
014      private final Matcher<? super U> subMatcher;
015      private final String featureDescription;
016      private final String featureName;
017      
018      /**
019       * Constructor
020       * @param subMatcher The matcher to apply to the feature
021       * @param featureDescription Descriptive text to use in describeTo
022       * @param featureName Identifying text for mismatch message
023       */
024      public FeatureMatcher(Matcher<? super U> subMatcher, String featureDescription, String featureName) {
025        super(TYPE_FINDER);
026        this.subMatcher = subMatcher;
027        this.featureDescription = featureDescription;
028        this.featureName = featureName;
029      }
030      
031      /**
032       * Implement this to extract the interesting feature.
033       * @param actual the target object
034       * @return the feature to be matched
035       */
036      protected abstract U featureValueOf(T actual);
037    
038      @Override
039      protected boolean matchesSafely(T actual, Description mismatch) {
040        final U featureValue = featureValueOf(actual);
041        if (!subMatcher.matches(featureValue)) {
042          mismatch.appendText(featureName).appendText(" ");
043          subMatcher.describeMismatch(featureValue, mismatch);
044          return false;
045        }
046        return true;
047      };
048          
049      @Override
050      public final void describeTo(Description description) {
051        description.appendText(featureDescription).appendText(" ")
052                   .appendDescriptionOf(subMatcher);
053      }
054    }