001/*  Copyright (c) 2000-2006 hamcrest.org
002 */
003package org.hamcrest.core;
004
005import org.hamcrest.Description;
006import org.hamcrest.DiagnosingMatcher;
007import org.hamcrest.Factory;
008import org.hamcrest.Matcher;
009
010
011/**
012 * Tests whether the value is an instance of a class.
013 * Classes of basic types will be converted to the relevant "Object" classes
014 */
015public class IsInstanceOf extends DiagnosingMatcher<Object> {
016    private final Class<?> expectedClass;
017    private final Class<?> matchableClass;
018
019    /**
020     * Creates a new instance of IsInstanceOf
021     *
022     * @param expectedClass The predicate evaluates to true for instances of this class
023     *                 or one of its subclasses.
024     */
025    public IsInstanceOf(Class<?> expectedClass) {
026        this.expectedClass = expectedClass;
027        this.matchableClass = matchableClass(expectedClass);
028    }
029
030    private static Class<?> matchableClass(Class<?> expectedClass) {
031      if (boolean.class.equals(expectedClass)) return Boolean.class; 
032      if (byte.class.equals(expectedClass)) return Byte.class; 
033      if (char.class.equals(expectedClass)) return Character.class; 
034      if (double.class.equals(expectedClass)) return Double.class; 
035      if (float.class.equals(expectedClass)) return Float.class; 
036      if (int.class.equals(expectedClass)) return Integer.class; 
037      if (long.class.equals(expectedClass)) return Long.class; 
038      if (short.class.equals(expectedClass)) return Short.class; 
039      return expectedClass;
040    }
041
042    @Override
043    protected boolean matches(Object item, Description mismatch) {
044      if (null == item) {
045        mismatch.appendText("null");
046        return false;
047      }
048      
049      if (!matchableClass.isInstance(item)) {
050        mismatch.appendValue(item).appendText(" is a " + item.getClass().getName());
051        return false;
052      }
053      
054      return true;
055    }
056
057    @Override
058    public void describeTo(Description description) {
059        description.appendText("an instance of ").appendText(expectedClass.getName());
060    }
061
062    /**
063     * Creates a matcher that matches when the examined object is an instance of the specified <code>type</code>,
064     * as determined by calling the {@link java.lang.Class#isInstance(Object)} method on that type, passing the
065     * the examined object.
066     * 
067     * <p>The created matcher assumes no relationship between specified type and the examined object.</p>
068     * <p/>
069     * For example: 
070     * <pre>assertThat(new Canoe(), instanceOf(Paddlable.class));</pre>
071     * 
072     */
073    @SuppressWarnings("unchecked")
074    @Factory
075    public static <T> Matcher<T> instanceOf(Class<?> type) {
076        return (Matcher<T>) new IsInstanceOf(type);
077    }
078    
079    /**
080     * Creates a matcher that matches when the examined object is an instance of the specified <code>type</code>,
081     * as determined by calling the {@link java.lang.Class#isInstance(Object)} method on that type, passing the
082     * the examined object.
083     * 
084     * <p>The created matcher forces a relationship between specified type and the examined object, and should be
085     * used when it is necessary to make generics conform, for example in the JMock clause
086     * <code>with(any(Thing.class))</code></p>
087     * <p/>
088     * For example: 
089     * <pre>assertThat(new Canoe(), instanceOf(Canoe.class));</pre>
090     *
091     */
092    @SuppressWarnings("unchecked")
093    @Factory
094    public static <T> Matcher<T> any(Class<T> type) {
095        return (Matcher<T>) new IsInstanceOf(type);
096    }
097
098}