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
33   * for 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      /**
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$
228      * @version $Revision$ $Date$
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 }