    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    }