/*
* Copyright (c) 2009-2010 Lockheed Martin Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.eurekastreams.commons.test;
import java.lang.reflect.Field;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
/**
* A matcher to use in junit tests to determine if two objects contain the same state.
*
* This is useful for comparing objects for value equality instead of reference equality when the class does not have an
* appropriate implementation of equals(), such as ClickListeners and AsyncCallbacks.
*
* @param <T>
* Type to compare.
*/
public class IsEqualInternally<T> extends BaseMatcher<T>
{
/** Value to compare result against. */
private Object compareAgainst;
/** Logger. */
private static Log log = LogFactory.getLog(IsEqualInternally.class);
/**
* Create matcher with a given value.
*
* @param inCompareAgainst
* Value to compare result against.
*/
public IsEqualInternally(final T inCompareAgainst)
{
compareAgainst = inCompareAgainst;
}
/**
* Compares the values.
*
* @param item
* Item to compare.
* @return If objects match.
*/
public boolean matches(final Object item)
{
return areEqualInternally(item, compareAgainst);
}
/**
* Determines if two objects are equal internally, i.e. based on values of all fields.
*
* @param object1
* First object.
* @param object2
* Second object.
* @return If objects match.
*/
public static boolean areEqualInternally(final Object object1, final Object object2)
{
if (object1 == null)
{
return object2 == null;
}
else if (object2 == null)
{
return false;
}
// both must be the same type (not just compatible types)
Class type = object1.getClass();
if (!object2.getClass().equals(type))
{
if (log.isDebugEnabled())
{
log.debug("equalInternally: types do not match. '" + type.getName() + "' vs. '"
+ object2.getClass().getName() + "'");
}
return false;
}
// compare values of all fields
for (Field field : type.getDeclaredFields())
{
try
{
field.setAccessible(true);
Object value1 = field.get(object1);
Object value2 = field.get(object2);
if (value1 == null ? value2 != null : !value1.equals(value2))
{
if (log.isDebugEnabled())
{
log.debug("equalInternally: field '" + field.getName() + "' doesn't match. '" + value1
+ "' vs. '" + value2 + "'");
}
return false;
}
}
catch (Throwable ex)
{
if (log.isDebugEnabled())
{
log.debug("equalInternally: exception: " + ex);
}
return false;
}
}
return true;
}
/**
* Describes the object. Needed for a matcher.
*
* @param description
* THe description to add to.
*/
public void describeTo(final Description description)
{
description.appendValue(compareAgainst);
}
/**
* Creates an instance of the matcher.
*
* @param <T>
* Type of object to match.
* @param value
* Specific value to check against.
* @return Matcher.
*/
public static <T> Matcher<T> equalInternally(final T value)
{
return new IsEqualInternally<T>(value);
}
}