swingx-master/swingx-testsupport/src/main/java/org/jdesktop/test/matchers/EquivalentMatcher.java
package org.jdesktop.test.matchers;

import static org.hamcrest.CoreMatchers.equalTo;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;

import org.mockito.ArgumentMatcher;

class EquivalentMatcher<T> extends ArgumentMatcher<T> {
    private final T object;
    
    public EquivalentMatcher(T object) {
        this.object = object;
    }
    
    @Override
    public boolean matches(Object argument) {
        if (equalTo(object).matches(argument)) {
            //short circuit: equal is always equivalent
            return true;
        }
        
        if (argument != null && object.getClass() == argument.getClass()) {
            BeanInfo beanInfo = null;
            
            try {
                beanInfo = Introspector.getBeanInfo(object.getClass());
            } catch (IntrospectionException shouldNeverHappen) {
                throw new Error(shouldNeverHappen);
            }
            
            for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
                if (pd.getReadMethod() == null) {
                    continue;
                }
                
                Object value1 = null;
                
                try {
                    value1 = pd.getReadMethod().invoke(object);
                } catch (RuntimeException e) {
                    throw e;
                } catch (Exception e) {
                    new Error(e);
                }
                
                Object value2 = null;
                
                try {
                    value2 = pd.getReadMethod().invoke(object);
                } catch (RuntimeException e) {
                    //prevent us from wrapping RuntimExceptions unnecessarily
                    throw e;
                } catch (Exception shouldNeverHappen) {
                    new Error(shouldNeverHappen);
                }
                
                if (!equalTo(value1).matches(value2)) {
                    return false;
                }
            }
            
            
            return true;
        }
        
        return false;
    }
}
