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 <T> 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 <T> 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 }