1 /*
2 *
3 * The DbUnit Database Testing Framework
4 * Copyright (C)2002-2004, DbUnit.org
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21 package org.dbunit.dataset.datatype;
22
23 import java.math.BigDecimal;
24
25 import org.dbunit.dataset.datatype.ToleratedDeltaMap.Precision;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 /**
30 * Extended version of the {@link NumberDataType}. Extends the
31 * {@link #compare(Object, Object)} method in order to respect precision tolerance.
32 * This is comparable to the JUnit method
33 * <code>assert(double val1, double val2, double toleratedDelta)</code>.
34 *
35 * @author gommma
36 * @author Last changed by: $Author: gommma $
37 * @version $Revision: 909 $ $Date: 2008-12-04 21:20:00 +0100 (gio, 04 dic 2008) $
38 * @since 2.3.0
39 */
40 public class NumberTolerantDataType extends NumberDataType
41 {
42
43 /**
44 * Logger for this class
45 */
46 private static final Logger logger = LoggerFactory.getLogger(NumberTolerantDataType.class);
47
48 private static final BigDecimal C_100 = new BigDecimal("100");
49
50 /**
51 * The allowed/tolerated difference
52 */
53 private Precision toleratedDelta;
54
55 /**
56 * Creates a new number tolerant datatype
57 * @param name
58 * @param sqlType
59 * @param delta The tolerated delta to be used for the comparison
60 */
61 NumberTolerantDataType(String name, int sqlType, Precision delta)
62 {
63 super(name, sqlType);
64
65 if (delta == null) {
66 throw new NullPointerException(
67 "The parameter 'delta' must not be null");
68 }
69 this.toleratedDelta = delta;
70 }
71
72 public Precision getToleratedDelta()
73 {
74 return toleratedDelta;
75 }
76
77
78 /**
79 * The only method overwritten from the base implementation to compare numbers allowing a tolerance
80 * @see org.dbunit.dataset.datatype.AbstractDataType#compareNonNulls(java.lang.Object, java.lang.Object)
81 */
82 protected int compareNonNulls(Object value1cast, Object value2cast)
83 throws TypeCastException
84 {
85 logger.debug("compareNonNulls(value1={}, value2={}) - start", value1cast, value2cast);
86
87 try
88 {
89 // Start of special handling
90 if(value1cast instanceof BigDecimal && value2cast instanceof BigDecimal){
91 BigDecimal bdValue1 = (BigDecimal)value1cast;
92 BigDecimal bdValue2 = (BigDecimal)value2cast;
93 BigDecimal diff = bdValue1.subtract(bdValue2);
94 // Exact match
95 if(isZero(diff))
96 {
97 return 0;
98 }
99
100 BigDecimal toleratedDeltaValue = this.toleratedDelta.getDelta();
101 if(!this.toleratedDelta.isPercentage())
102 {
103 if(diff.abs().compareTo(toleratedDeltaValue) <= 0)
104 {
105 // within tolerance delta, so accept
106 if(logger.isDebugEnabled())
107 logger.debug("Values val1={}, val2={} differ but are within tolerated delta {}",
108 new Object[] {bdValue1, bdValue2, toleratedDeltaValue } );
109 return 0;
110 }
111 else {
112 // TODO it would be beautiful to report a precise description about difference and tolerated delta values in the assertion
113 // Therefore think about introducing a method "DataType.getCompareInfo()"
114 return diff.signum();
115 }
116 }
117 else {
118 // percentage comparison
119 int scale = toleratedDeltaValue.scale() + 2;
120 BigDecimal toleratedValue = bdValue1.multiply( toleratedDeltaValue.divide(C_100, scale, BigDecimal.ROUND_HALF_UP) );
121 if(diff.abs().compareTo(toleratedValue) <= 0)
122 {
123 // within tolerance delta, so accept
124 if(logger.isDebugEnabled())
125 logger.debug("Values val1={}, val2={} differ but are within tolerated delta {}",
126 new Object[] {bdValue1, bdValue2, toleratedValue } );
127 return 0;
128 }
129 else {
130 // TODO it would be beautiful to report a precise description about difference and tolerated delta values in the assertion
131 // Therefore think about introducing a method "DataType.getCompareInfo()"
132 return diff.signum();
133 }
134 }
135
136 }
137 else {
138 Comparable value1 = (Comparable)value1cast;
139 Comparable value2 = (Comparable)value2cast;
140 return value1.compareTo(value2);
141 }
142 }
143 catch (ClassCastException e)
144 {
145 throw new TypeCastException(e);
146 }
147 }
148
149 /**
150 * Checks if the given value is zero.
151 * @param value
152 * @return <code>true</code> if and only if the given value is zero.
153 */
154 public static final boolean isZero(BigDecimal value)
155 {
156 return value.signum()==0;
157 }
158
159 }