001package org.hamcrest;
002
003import org.hamcrest.internal.ReflectiveTypeFinder;
004
005/**
006 * Convenient base class for Matchers that require a non-null value of a specific type.
007 * This simply implements the null check, checks the type and then casts.
008 *
009 * @author Joe Walnes
010 * @author Steve Freeman
011 * @author Nat Pryce
012 */
013public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> {
014    private static final ReflectiveTypeFinder TYPE_FINDER = new ReflectiveTypeFinder("matchesSafely", 1, 0);
015    
016    final private Class<?> expectedType;
017
018    /**
019     * The default constructor for simple sub types
020     */
021    protected TypeSafeMatcher() {
022        this(TYPE_FINDER);
023    }
024   
025    /**
026     * Use this constructor if the subclass that implements <code>matchesSafely</code> 
027     * is <em>not</em> the class that binds &lt;T&gt; to a type. 
028     * @param expectedType The expectedType of the actual value.
029     */
030    protected TypeSafeMatcher(Class<?> expectedType) {
031        this.expectedType = expectedType;
032    }
033    
034    /**
035     * Use this constructor if the subclass that implements <code>matchesSafely</code> 
036     * is <em>not</em> the class that binds &lt;T&gt; to a type. 
037     * @param typeFinder A type finder to extract the type
038     */
039    protected TypeSafeMatcher(ReflectiveTypeFinder typeFinder) {
040      this.expectedType = typeFinder.findExpectedType(getClass()); 
041    }
042 
043    /**
044     * Subclasses should implement this. The item will already have been checked for
045     * the specific type and will never be null.
046     */
047    protected abstract boolean matchesSafely(T item);
048    
049    /**
050     * Subclasses should override this. The item will already have been checked for
051     * the specific type and will never be null.
052     */
053    protected void describeMismatchSafely(T item, Description mismatchDescription) {
054        super.describeMismatch(item, mismatchDescription);
055    }
056    
057    /**
058     * Methods made final to prevent accidental override.
059     * If you need to override this, there's no point on extending TypeSafeMatcher.
060     * Instead, extend the {@link BaseMatcher}.
061     */
062    @Override
063    @SuppressWarnings({"unchecked"})
064    public final boolean matches(Object item) {
065        return item != null
066                && expectedType.isInstance(item)
067                && matchesSafely((T) item);
068    }
069    
070    @SuppressWarnings("unchecked")
071    @Override
072    final public void describeMismatch(Object item, Description description) {
073        if (item == null) {
074            super.describeMismatch(item, description);
075        } else if (! expectedType.isInstance(item)) {
076            description.appendText("was a ")
077                       .appendText(item.getClass().getName())
078                       .appendText(" (")
079                       .appendValue(item)
080                       .appendText(")");
081        } else {
082            describeMismatchSafely((T)item, description);
083        }
084    }
085}