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 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.Map;
27
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 /**
32 * Container that manages a map of {@link ToleratedDelta} objects to be used
33 * for numeric comparisons with an allowed deviation of two values
34 *
35 * @author gommma
36 * @author Last changed by: $Author: gommma $
37 * @version $Revision: 824 $ $Date: 2008-10-05 19:20:37 +0200 (dom, 05 ott 2008) $
38 * @since 2.3.0
39 */
40 public class ToleratedDeltaMap
41 {
42 /**
43 * List of {@link ToleratedDelta} objects
44 */
45 private Map _toleratedDeltas;
46 /**
47 * The logger
48 */
49 private Logger logger = LoggerFactory.getLogger(ToleratedDeltaMap.class);
50
51
52 /**
53 * Lookup a tolerated delta object by tableName and ColumnName.
54 * @param tableName
55 * @param columnName
56 * @return The object from the map or <code>null</code> if no such object was found
57 */
58 public ToleratedDelta findToleratedDelta(String tableName, String columnName)
59 {
60 Map toleratedDeltas = getToleratedDeltasNullSafe();
61 String mapKey = ToleratedDeltaMap.buildMapKey(tableName, columnName);
62 ToleratedDelta deltaObj = (ToleratedDelta)toleratedDeltas.get(mapKey);
63 return deltaObj;
64 }
65
66 private final Map getToleratedDeltasNullSafe()
67 {
68 Map res = getToleratedDeltas();
69 if(res==null)
70 {
71 return Collections.EMPTY_MAP;
72 }
73 return res;
74 }
75
76 public Map getToleratedDeltas()
77 {
78 return _toleratedDeltas;
79 }
80
81 /**
82 * Adds a new object to the map of tolerated deltas
83 * @param delta The object to be added to the map
84 */
85 public void addToleratedDelta(ToleratedDelta delta)
86 {
87 if (delta == null)
88 {
89 throw new NullPointerException("The parameter 'delta' must not be null");
90 }
91
92 if(this._toleratedDeltas==null)
93 {
94 this._toleratedDeltas=new HashMap();
95 }
96 String key = ToleratedDeltaMap.buildMapKey(delta);
97 // Put the new object into the map
98 ToleratedDelta removed = (ToleratedDelta)_toleratedDeltas.put(key, delta);
99 //Give a hint to the user when an already existing object has been overwritten/replaced
100 if(removed!=null)
101 {
102 logger.debug("Replaced old tolerated delta object from map with key {}. Old replaced object={}", key, removed);
103 }
104 }
105
106 /**
107 * Utility method to create a map key from the input parameters
108 * @param tableName
109 * @param columnName
110 * @return The key for the tolerated delta object map, consisting of the tableName and the columnName
111 */
112 static String buildMapKey(String tableName, String columnName)
113 {
114 return tableName+ "." + columnName;
115 }
116
117 /**
118 * Utility method to create a map key from the input parameters
119 * @param delta
120 * @return The key for the tolerated delta object map, consisting of the tableName and the columnName
121 */
122 static String buildMapKey(ToleratedDelta delta)
123 {
124 return buildMapKey(delta.getTableName(),delta.getColumnName());
125 }
126
127
128 /**
129 * Simple bean that holds the tolerance for floating point comparisons for a specific
130 * database column.
131 */
132 public static class ToleratedDelta
133 {
134 private String tableName;
135 private String columnName;
136 private Precision toleratedDelta;
137
138 /**
139 * @param tableName The name of the table
140 * @param columnName The name of the column for which the tolerated delta should be applied
141 * @param toleratedDelta The tolerated delta. For example 1E-5 means that the comparison must
142 * match the first 5 decimal digits. All subsequent decimals are ignored.
143 */
144 public ToleratedDelta(String tableName, String columnName, double toleratedDelta)
145 {
146 this(tableName, columnName, new Precision(new BigDecimal(String.valueOf(toleratedDelta)) ));
147 }
148
149 /**
150 * @param tableName The name of the table
151 * @param columnName The name of the column for which the tolerated delta should be applied
152 * @param toleratedDelta The tolerated delta. For example 1E-5 means that the comparison must
153 * match the first 5 decimal digits. All subsequent decimals are ignored.
154 */
155 public ToleratedDelta(String tableName, String columnName, BigDecimal toleratedDelta)
156 {
157 this(tableName, columnName, new Precision(toleratedDelta));
158 }
159
160 /**
161 * @param tableName The name of the table
162 * @param columnName The name of the column for which the tolerated delta should be applied
163 * @param toleratedDelta The tolerated delta. For example 1E-5 means that the comparison must
164 * match the first 5 decimal digits. All subsequent decimals are ignored.
165 * @param isPercentage Whether or not the given toleratedDelta value is a percentage. See {@link Precision} for more.
166 */
167 public ToleratedDelta(String tableName, String columnName, BigDecimal toleratedDelta, boolean isPercentage)
168 {
169 this(tableName, columnName, new Precision(toleratedDelta, isPercentage));
170 }
171
172 /**
173 * @param tableName The name of the table
174 * @param columnName The name of the column for which the tolerated delta should be applied
175 * @param toleratedDelta The tolerated delta. For example 1E-5 means that the comparison must
176 * match the first 5 decimal digits. All subsequent decimals are ignored.
177 */
178 public ToleratedDelta(String tableName, String columnName, Precision toleratedDelta)
179 {
180 super();
181 this.tableName = tableName;
182 this.columnName = columnName;
183 this.toleratedDelta = toleratedDelta;
184 }
185
186 public String getTableName() {
187 return tableName;
188 }
189
190 public String getColumnName() {
191 return columnName;
192 }
193
194 public Precision getToleratedDelta() {
195 return toleratedDelta;
196 }
197
198 /**
199 * Checks whether or not the <code>tableName</code> and the <code>columnName</code>
200 * match the ones of this object.
201 * @param tableName
202 * @param columnName
203 * @return <code>true</code> if both given values match those of this object.
204 */
205 public boolean matches(String tableName, String columnName) {
206 if(this.tableName.equals(tableName) && this.columnName.equals(columnName)) {
207 return true;
208 }else {
209 return false;
210 }
211 }
212
213 public String toString() {
214 StringBuffer sb = new StringBuffer();
215 sb.append("tableName=").append(tableName);
216 sb.append(", columnName=").append(columnName);
217 sb.append(", toleratedDelta=").append(toleratedDelta);
218 return sb.toString();
219 }
220 }
221
222
223 /**
224 * Container for the tolerated delta of two values that are compared to each other.
225 *
226 * @author gommma (gommma AT users.sourceforge.net)
227 * @author Last changed by: $Author: gommma $
228 * @version $Revision: 824 $ $Date: 2008-10-05 19:20:37 +0200 (dom, 05 ott 2008) $
229 * @since 2.4.0
230 */
231 public static class Precision
232 {
233 private static final BigDecimal ZERO = new BigDecimal("0.0");
234
235 private boolean percentage;
236 private BigDecimal delta;
237
238 /**
239 * @param delta The allowed/tolerated difference
240 */
241 public Precision(BigDecimal delta) {
242 this(delta, false);
243 }
244
245 /**
246 * @param delta The allowed/tolerated difference
247 * @param percentage Whether or not the given <code>delta</code> should be
248 * interpreted as percentage or not during the comparison
249 */
250 public Precision(BigDecimal delta, boolean percentage) {
251 super();
252
253 if(delta.compareTo(ZERO) < 0) {
254 throw new IllegalArgumentException("The given delta '"+delta+"' must be >= 0");
255 }
256
257 this.delta = delta;
258 this.percentage = percentage;
259 }
260
261 public boolean isPercentage() {
262 return percentage;
263 }
264 public BigDecimal getDelta() {
265 return delta;
266 }
267
268 }
269
270 }