001package org.hamcrest;
002
003import 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 */
012public 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}