package org.hamcrest.core;

import org.hamcrest.Description;
import org.hamcrest.DiagnosingMatcher;
import org.hamcrest.Matcher;


/**
 
* Tests whether the value is an instance of a class.
 
* Classes of basic types will be converted to the relevant "Object" classes
 
*/

public class IsInstanceOf extends DiagnosingMatcher<Object> {
    
private final Class<?> expectedClass;
    
private final Class<?> matchableClass;

    
/**
     
* Creates a new instance of IsInstanceOf
     
*
     
* @param expectedClass The predicate evaluates to true for instances of this class
     
*
                 
or one of its subclasses.
     
*/

    
public IsInstanceOf(Class<?> expectedClass) {
        
this.expectedClass = expectedClass;
        
this.matchableClass = matchableClass(expectedClass);
    
}

    
private static Class<?> matchableClass(Class<?> expectedClass) {
      
if (boolean.class.equals(expectedClass)) return Boolean.class;
      
if (byte.class.equals(expectedClass)) return Byte.class;
      
if (char.class.equals(expectedClass)) return Character.class;
      
if (double.class.equals(expectedClass)) return Double.class;
      
if (float.class.equals(expectedClass)) return Float.class;
      
if (int.class.equals(expectedClass)) return Integer.class;
      
if (long.class.equals(expectedClass)) return Long.class;
      
if (short.class.equals(expectedClass)) return Short.class;
      
return expectedClass;
    
}

    
@Override
    
protected boolean matches(Object item, Description mismatch) {
      
if (null == item) {
        
mismatch.appendText("null");
        
return false;
      
}
      

      
if (!matchableClass.isInstance(item)) {
        
mismatch.appendValue(item).appendText(" is a " + item.getClass().getName());
        
return false;
      
}
      

      
return true;
    
}

    
@Override
    
public void describeTo(Description description) {
        
description.appendText("an instance of ").appendText(expectedClass.getName());
    
}

    
/**
     
* Creates a matcher that matches when the examined object is an instance of the specified <code>type</code>,
     
* as determined by calling the {@link java.lang.Class#isInstance(Object)} method on that type, passing the
     
* the examined object.
     
*
 

     
* <p>The created matcher assumes no relationship between specified type and the examined object.</p>
     
* For example:
     
* <pre>assertThat(new Canoe(), instanceOf(Paddlable.class));</pre>
     
*
 

     
*/

    
@SuppressWarnings("unchecked")
    
public static <T> Matcher<T> instanceOf(Class<?> type) {
        
return (Matcher<T>) new IsInstanceOf(type);
    
}
    

    
/**
     
* Creates a matcher that matches when the examined object is an instance of the specified <code>type</code>,
     
* as determined by calling the {@link java.lang.Class#isInstance(Object)} method on that type, passing the
     
* the examined object.
     
*
 

     
* <p>The created matcher forces a relationship between specified type and the examined object, and should be
     
* used when it is necessary to make generics conform, for example in the JMock clause
     
* <code>with(any(Thing.class))</code></p>
     
* For example:
     
* <pre>assertThat(new Canoe(), instanceOf(Canoe.class));</pre>
     
*
     
*/

    
@SuppressWarnings("unchecked")
    
public static <T> Matcher<T> any(Class<T> type) {
        
return (Matcher<T>) new IsInstanceOf(type);
    
}

}