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 for
33 * numeric comparisons with an allowed deviation of two values
34 *
35 * @author gommma
36 * @author Last changed by: $Author$
37 * @version $Revision$ $Date$
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 * Lookup a tolerated delta object by tableName and ColumnName.
53 *
54 * @param tableName
55 * @param columnName
56 * @return The object from the map or <code>null</code> if no such object
57 * was found
58 */
59 public ToleratedDelta findToleratedDelta(String tableName,
60 String columnName)
61 {
62 Map toleratedDeltas = getToleratedDeltasNullSafe();
63 String mapKey = ToleratedDeltaMap.buildMapKey(tableName, columnName);
64 ToleratedDelta deltaObj = (ToleratedDelta) toleratedDeltas.get(mapKey);
65 return deltaObj;
66 }
67
68 private final Map getToleratedDeltasNullSafe()
69 {
70 Map res = getToleratedDeltas();
71 if (res == null)
72 {
73 return Collections.emptyMap();
74 }
75 return res;
76 }
77
78 public Map getToleratedDeltas()
79 {
80 return _toleratedDeltas;
81 }
82
83 /**
84 * Adds a new object to the map of tolerated deltas
85 *
86 * @param delta
87 * The object to be added to the map
88 */
89 public void addToleratedDelta(ToleratedDelta delta)
90 {
91 if (delta == null)
92 {
93 throw new NullPointerException(
94 "The parameter 'delta' must not be null");
95 }
96
97 if (this._toleratedDeltas == null)
98 {
99 this._toleratedDeltas = new HashMap();
100 }
101 String key = ToleratedDeltaMap.buildMapKey(delta);
102 // Put the new object into the map
103 ToleratedDelta removed =
104 (ToleratedDelta) _toleratedDeltas.put(key, delta);
105 // Give a hint to the user when an already existing object has been
106 // overwritten/replaced
107 if (removed != null)
108 {
109 logger.debug(
110 "Replaced old tolerated delta object from map with key {}. Old replaced object={}",
111 key, removed);
112 }
113 }
114
115 /**
116 * Utility method to create a map key from the input parameters
117 *
118 * @param tableName
119 * @param columnName
120 * @return The key for the tolerated delta object map, consisting of the
121 * tableName and the columnName
122 */
123 static String buildMapKey(String tableName, String columnName)
124 {
125 return tableName + "." + columnName;
126 }
127
128 /**
129 * Utility method to create a map key from the input parameters
130 *
131 * @param delta
132 * @return The key for the tolerated delta object map, consisting of the
133 * tableName and the columnName
134 */
135 static String buildMapKey(ToleratedDelta delta)
136 {
137 return buildMapKey(delta.getTableName(), delta.getColumnName());
138 }
139
140 /**
141 * Simple bean that holds the tolerance for floating point comparisons for a
142 * specific database column.
143 */
144 public static class ToleratedDelta
145 {
146 private String tableName;
147 private String columnName;
148 private Precision toleratedDelta;
149
150 /**
151 * @param tableName
152 * The name of the table
153 * @param columnName
154 * The name of the column for which the tolerated delta
155 * should be applied
156 * @param toleratedDelta
157 * The tolerated delta. For example 1E-5 means that the
158 * comparison must match the first 5 decimal digits. All
159 * subsequent decimals are ignored.
160 */
161 public ToleratedDelta(String tableName, String columnName,
162 double toleratedDelta)
163 {
164 this(tableName, columnName, new Precision(
165 new BigDecimal(String.valueOf(toleratedDelta))));
166 }
167
168 /**
169 * @param tableName
170 * The name of the table
171 * @param columnName
172 * The name of the column for which the tolerated delta
173 * should be applied
174 * @param toleratedDelta
175 * The tolerated delta. For example 1E-5 means that the
176 * comparison must match the first 5 decimal digits. All
177 * subsequent decimals are ignored.
178 */
179 public ToleratedDelta(String tableName, String columnName,
180 BigDecimal toleratedDelta)
181 {
182 this(tableName, columnName, new Precision(toleratedDelta));
183 }
184
185 /**
186 * @param tableName
187 * The name of the table
188 * @param columnName
189 * The name of the column for which the tolerated delta
190 * should be applied
191 * @param toleratedDelta
192 * The tolerated delta. For example 1E-5 means that the
193 * comparison must match the first 5 decimal digits. All
194 * subsequent decimals are ignored.
195 * @param isPercentage
196 * Whether or not the given toleratedDelta value is a
197 * percentage. See {@link Precision} for more.
198 */
199 public ToleratedDelta(String tableName, String columnName,
200 BigDecimal toleratedDelta, boolean isPercentage)
201 {
202 this(tableName, columnName,
203 new Precision(toleratedDelta, isPercentage));
204 }
205
206 /**
207 * @param tableName
208 * The name of the table
209 * @param columnName
210 * The name of the column for which the tolerated delta
211 * should be applied
212 * @param toleratedDelta
213 * The tolerated delta. For example 1E-5 means that the
214 * comparison must match the first 5 decimal digits. All
215 * subsequent decimals are ignored.
216 */
217 public ToleratedDelta(String tableName, String columnName,
218 Precision toleratedDelta)
219 {
220 super();
221 this.tableName = tableName;
222 this.columnName = columnName;
223 this.toleratedDelta = toleratedDelta;
224 }
225
226 public String getTableName()
227 {
228 return tableName;
229 }
230
231 public String getColumnName()
232 {
233 return columnName;
234 }
235
236 public Precision getToleratedDelta()
237 {
238 return toleratedDelta;
239 }
240
241 /**
242 * Checks whether or not the <code>tableName</code> and the
243 * <code>columnName</code> match the ones of this object.
244 *
245 * @param tableName
246 * @param columnName
247 * @return <code>true</code> if both given values match those of this
248 * object.
249 */
250 public boolean matches(String tableName, String columnName)
251 {
252 if (this.tableName.equals(tableName)
253 && this.columnName.equals(columnName))
254 {
255 return true;
256 } else
257 {
258 return false;
259 }
260 }
261
262 public String toString()
263 {
264 final StringBuilder sb = new StringBuilder();
265 sb.append("tableName=").append(tableName);
266 sb.append(", columnName=").append(columnName);
267 sb.append(", toleratedDelta=").append(toleratedDelta);
268 return sb.toString();
269 }
270 }
271
272 /**
273 * Container for the tolerated delta of two values that are compared to each
274 * other.
275 *
276 * @author gommma (gommma AT users.sourceforge.net)
277 * @author Last changed by: $Author$
278 * @version $Revision$ $Date$
279 * @since 2.4.0
280 */
281 public static class Precision
282 {
283 private static final BigDecimal ZERO = new BigDecimal("0.0");
284
285 private final boolean percentage;
286 private final BigDecimal delta;
287
288 /**
289 * @param delta
290 * The allowed/tolerated difference
291 */
292 public Precision(BigDecimal delta)
293 {
294 this(delta, false);
295 }
296
297 /**
298 * @param delta
299 * The allowed/tolerated difference
300 * @param percentage
301 * Whether or not the given <code>delta</code> should be
302 * interpreted as percentage or not during the comparison
303 */
304 public Precision(BigDecimal delta, boolean percentage)
305 {
306 super();
307
308 if (delta.compareTo(ZERO) < 0)
309 {
310 throw new IllegalArgumentException(
311 "The given delta '" + delta + "' must be >= 0");
312 }
313
314 this.delta = delta;
315 this.percentage = percentage;
316 }
317
318 public boolean isPercentage()
319 {
320 return percentage;
321 }
322
323 public BigDecimal getDelta()
324 {
325 return delta;
326 }
327
328 }
329
330 }