001    package org.hamcrest;
002    
003    import org.hamcrest.internal.ReflectiveTypeFinder;
004    
005    
006    /**
007     * Convenient base class for Matchers that require a non-null value of a specific type
008     * and that will report why the received value has been rejected.
009     * This implements the null check, checks the type and then casts. 
010     * To use, implement <pre>matchesSafely()</pre>. 
011     *
012     * @param <T>
013     * @author Neil Dunn
014     * @author Nat Pryce
015     * @author Steve Freeman
016     */
017    public abstract class TypeSafeDiagnosingMatcher<T> extends BaseMatcher<T> {
018        private static final ReflectiveTypeFinder TYPE_FINDER = new ReflectiveTypeFinder("matchesSafely", 2, 0); 
019        private final Class<?> expectedType;
020    
021        /**
022         * Subclasses should implement this. The item will already have been checked
023         * for the specific type and will never be null.
024         */
025        protected abstract boolean matchesSafely(T item, Description mismatchDescription);
026    
027        /**
028         * Use this constructor if the subclass that implements <code>matchesSafely</code> 
029         * is <em>not</em> the class that binds &lt;T&gt; to a type. 
030         * @param expectedType The expectedType of the actual value.
031         */
032        protected TypeSafeDiagnosingMatcher(Class<?> expectedType) {
033          this.expectedType = expectedType;
034        }
035    
036        /**
037         * Use this constructor if the subclass that implements <code>matchesSafely</code> 
038         * is <em>not</em> the class that binds &lt;T&gt; to a type. 
039         * @param typeFinder A type finder to extract the type
040         */
041        protected TypeSafeDiagnosingMatcher(ReflectiveTypeFinder typeFinder) {
042          this.expectedType = typeFinder.findExpectedType(getClass()); 
043        }
044    
045        /**
046         * The default constructor for simple sub types
047         */
048        protected TypeSafeDiagnosingMatcher() {
049          this(TYPE_FINDER); 
050        }
051    
052        @Override
053        @SuppressWarnings("unchecked")
054        public final boolean matches(Object item) {
055            return item != null
056                && expectedType.isInstance(item)
057                && matchesSafely((T) item, new Description.NullDescription());
058        }
059    
060        @SuppressWarnings("unchecked")
061        @Override
062        public final void describeMismatch(Object item, Description mismatchDescription) {
063          if (item == null || !expectedType.isInstance(item)) {
064            super.describeMismatch(item, mismatchDescription);
065          } else {
066            matchesSafely((T) item, mismatchDescription);
067          }
068        }
069    }