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