View Javadoc
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 }