NumberTolerantDataType.java
/*
*
* The DbUnit Database Testing Framework
* Copyright (C)2002-2004, DbUnit.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package org.dbunit.dataset.datatype;
import java.math.BigDecimal;
import org.dbunit.dataset.datatype.ToleratedDeltaMap.Precision;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Extended version of the {@link NumberDataType}. Extends the
* {@link #compare(Object, Object)} method in order to respect precision
* tolerance. This is comparable to the JUnit method
* <code>assert(double val1, double val2, double toleratedDelta)</code>.
*
* @author gommma
* @author Last changed by: $Author$
* @version $Revision$ $Date$
* @since 2.3.0
*/
public class NumberTolerantDataType extends NumberDataType
{
/**
* Logger for this class
*/
private static final Logger logger =
LoggerFactory.getLogger(NumberTolerantDataType.class);
private static final BigDecimal C_100 = new BigDecimal("100");
/**
* The allowed/tolerated difference
*/
private Precision toleratedDelta;
/**
* Creates a new number tolerant datatype
*
* @param name
* @param sqlType
* @param delta
* The tolerated delta to be used for the comparison
*/
NumberTolerantDataType(String name, int sqlType, Precision delta)
{
super(name, sqlType);
if (delta == null)
{
throw new NullPointerException(
"The parameter 'delta' must not be null");
}
this.toleratedDelta = delta;
}
public Precision getToleratedDelta()
{
return toleratedDelta;
}
/**
* The only method overwritten from the base implementation to compare
* numbers allowing a tolerance
*
* @see org.dbunit.dataset.datatype.AbstractDataType#compareNonNulls(java.lang.Object,
* java.lang.Object)
*/
protected int compareNonNulls(Object value1cast, Object value2cast)
throws TypeCastException
{
logger.debug("compareNonNulls(value1={}, value2={}) - start",
value1cast, value2cast);
try
{
// Start of special handling
if (value1cast instanceof BigDecimal
&& value2cast instanceof BigDecimal)
{
BigDecimal bdValue1 = (BigDecimal) value1cast;
BigDecimal bdValue2 = (BigDecimal) value2cast;
BigDecimal diff = bdValue1.subtract(bdValue2);
// Exact match
if (isZero(diff))
{
return 0;
}
BigDecimal toleratedDeltaValue = this.toleratedDelta.getDelta();
if (!this.toleratedDelta.isPercentage())
{
if (diff.abs().compareTo(toleratedDeltaValue) <= 0)
{
// within tolerance delta, so accept
if (logger.isDebugEnabled())
logger.debug(
"Values val1={}, val2={} differ but are within tolerated delta {}",
new Object[] {bdValue1, bdValue2,
toleratedDeltaValue});
return 0;
} else
{
// TODO it would be beautiful to report a precise
// description about difference and tolerated delta
// values in the assertion
// Therefore think about introducing a method
// "DataType.getCompareInfo()"
return diff.signum();
}
} else
{
// percentage comparison
int scale = toleratedDeltaValue.scale() + 2;
BigDecimal toleratedValue =
bdValue1.multiply(toleratedDeltaValue.divide(C_100,
scale, BigDecimal.ROUND_HALF_UP));
if (diff.abs().compareTo(toleratedValue) <= 0)
{
// within tolerance delta, so accept
if (logger.isDebugEnabled())
logger.debug(
"Values val1={}, val2={} differ but are within tolerated delta {}",
new Object[] {bdValue1, bdValue2,
toleratedValue});
return 0;
} else
{
// TODO it would be beautiful to report a precise
// description about difference and tolerated delta
// values in the assertion
// Therefore think about introducing a method
// "DataType.getCompareInfo()"
return diff.signum();
}
}
} else
{
Comparable value1 = (Comparable) value1cast;
Comparable value2 = (Comparable) value2cast;
return value1.compareTo(value2);
}
} catch (ClassCastException e)
{
throw new TypeCastException(e);
}
}
/**
* Checks if the given value is zero.
*
* @param value
* @return <code>true</code> if and only if the given value is zero.
*/
public static final boolean isZero(BigDecimal value)
{
return value.signum() == 0;
}
}