View Javadoc
1   package org.dbunit.dataset;
2   
3   import java.sql.ResultSet;
4   import java.sql.SQLException;
5   import java.util.ArrayList;
6   import java.util.List;
7   
8   import org.dbunit.dataset.datatype.DataType;
9   import org.dbunit.dataset.datatype.DataTypeException;
10  import org.dbunit.dataset.datatype.IDataTypeFactory;
11  
12  /**
13   * Wrapper class for column metadata as retrieved by 
14   * {@link java.sql.DatabaseMetaData#getColumns(String, String, String, String)}.
15   * 
16   * This is a utility wrapper so that metadata for any number of columns can be cached  
17   * in a collection and accessed repeatedly and in any order.  Column metadata are
18   * typically returned from the database as a forward-only result set, and cannot be 
19   * "rewound" after scrolling forward to a specific column.  
20   * 
21   * @author Joe Kearns
22   * @version $Revision$
23   * @since June 2017
24   *
25   */
26  public class ColumnMetaData {
27  	private List<MetaDataField> fields = new ArrayList<MetaDataField>();
28  	
29  	private final StringMetaDataField tableCat = new StringMetaDataField("tableCat", 1);
30  	private final StringMetaDataField tableSchema = new StringMetaDataField("tableSchema", 2);
31  	private final StringMetaDataField tableName = new StringMetaDataField("tableName", 3);
32  	private final StringMetaDataField columnName = new StringMetaDataField("columnName", 4);
33  	private final IntegerMetaDataField sqlType = new IntegerMetaDataField("sqlType", 5);
34  	private final StringMetaDataField sqlTypeName = new StringMetaDataField("sqlTypeName", 6);
35  	private final IntegerMetaDataField columnSize = new IntegerMetaDataField("columnSize", 7);
36  	// 8 not used
37  	private final IntegerMetaDataField decimalDigits = new IntegerMetaDataField("decimalDigits", 9);
38  	private final IntegerMetaDataField numPrecRadix = new IntegerMetaDataField("numPrecRadix", 10);
39  	private final IntegerMetaDataField nullable = new IntegerMetaDataField("nullable", 11);
40  	private final StringMetaDataField remarks = new StringMetaDataField("remarks", 12);
41  	private final StringMetaDataField columnDefault = new StringMetaDataField("columnDefault", 13);
42  	// 14 not used
43  	// 15 not used
44  	private final IntegerMetaDataField charOctetLength = new IntegerMetaDataField("charOctetLength", 16);
45  	private final IntegerMetaDataField ordinalPosition = new IntegerMetaDataField("ordinalPosition", 17);
46  	private final StringMetaDataField isoNullable = new StringMetaDataField("isoNullable", 18);
47  	private final StringMetaDataField scopeCatalog = new StringMetaDataField("scopeCatalog", 19);
48  	private final StringMetaDataField scopeSchema = new StringMetaDataField("scopeSchema", 20);
49  	private final StringMetaDataField scopeTable = new StringMetaDataField("scopeTable", 21);
50  	private final ShortMetaDataField sourceDataType = new ShortMetaDataField("sourceDataType", 22);
51  	private final StringMetaDataField autoincrement = new StringMetaDataField("autoincrement", 23);
52  	private final StringMetaDataField generatedcolumn = new StringMetaDataField("generatedcolumn", 24);
53  	
54  	/**
55  	 * Construct a ColumnMetaData instance from a result set retrieved by 
56  	 * {@link java.sql.DatabaseMetaData#getColumns(String, String, String, String)}.
57  	 *
58  	 * @param resultSet column metadata results from DatabaseMetaData.getColumns
59  	 * @throws SQLException thrown on from ResultSet accessors
60  	 */
61  	public ColumnMetaData(ResultSet resultSet) throws SQLException {
62  		setDefaults();
63  		for (MetaDataField field : fields) {
64  			field.setValue(resultSet);
65  		}
66          //If Types.DISTINCT like SQL DOMAIN, then get Source Date Type of SQL-DOMAIN
67          if(sqlType.value == java.sql.Types.DISTINCT)
68          {
69              sqlType.value = resultSet.getInt("SOURCE_DATA_TYPE");
70          }
71  	}
72  
73  	/**
74  	 * Default values for specific fields, consistent with 
75  	 * {@link org.dbunit.util.SQLHelper#createColumn(ResultSet, org.dbunit.dataset.datatype.IDataTypeFactory, boolean)}
76  	 */
77  	private void setDefaults() {
78  		this.autoincrement.value = Column.AutoIncrement.NO.getKey();
79  	}
80  	
81  	public String getTableCat() {
82  		return tableCat.getString();
83  	}
84  	public String getTableSchema() {
85  		return tableSchema.getString();
86  	}
87  	public String getTableName() {
88  		return tableName.getString();
89  	}
90  	public String getColumnName() {
91  		return columnName.getString();
92  	}
93  	public Integer getSQLType() {
94  		return sqlType.getInteger();
95  	}
96  	public String getSqlTypeName() {
97  		return sqlTypeName.getString();
98  	}
99  	public Integer getColumnSize() {
100 		return columnSize.getInteger();
101 	}
102 	public Integer getDecimalDigits() {
103 		return decimalDigits.getInteger();
104 	}
105 	public Integer getNumPrecRadix() {
106 		return numPrecRadix.getInteger();
107 	}
108 	public Integer getNullable() {
109 		return nullable.getInteger();
110 	}
111 	public String getRemarks() {
112 		return remarks.getString();
113 	}
114 	public String getColumnDefault() {
115 		return columnDefault.getString();
116 	}
117 	public Integer getCharOctetLength() {
118 		return charOctetLength.getInteger();
119 	}
120 	public Integer getOrdinalPosition() {
121 		return ordinalPosition.getInteger();
122 	}
123 	public String getIsoNullable() {
124 		return isoNullable.getString();
125 	}
126 	public String getScopeCatalog() {
127 		return scopeCatalog.getString();
128 	}
129 	public String getScopeSchema() {
130 		return scopeSchema.getString();
131 	}
132 	public String getScopeTable() {
133 		return scopeTable.getString();
134 	}
135 	public Short getSourceDataType() {
136 		return sourceDataType.getShort();
137 	}
138 	public String getAutoincrement() {
139 		return autoincrement.getString();
140 	}
141 	public String getGeneratedcolumn() {
142 		return generatedcolumn.getString();
143 	}
144 	
145 	/**
146 	 * Use a DataType factory to create a DataType instance of the type appropriate
147 	 * to the metadata. 
148 	 * 
149 	 * TODO It would be good to have an equivalent factory method defined in IDataTypeFactory
150 	 * but that would impact third-party implementations of the interface (breaking change), so 
151 	 * could only be done as part of a major version release.  
152 	 * 
153 	 * @param dataTypeFactory
154 	 * @return DataType object as created by the factory
155 	 * @throws DataTypeException
156 	 */
157 	public DataType getDataType(IDataTypeFactory dataTypeFactory) throws DataTypeException {
158         return dataTypeFactory.createDataType(
159         		this.getSQLType(), 
160         		this.getSqlTypeName(), 
161         		this.getTableName(), 
162         		this.getColumnName());
163 	}
164 	
165 	@Override
166 	public int hashCode() {
167 		final int prime = 31;
168 		int result = 1;
169 		result = prime * result + ((autoincrement == null) ? 0 : autoincrement.hashCode());
170 		result = prime * result + ((charOctetLength == null) ? 0 : charOctetLength.hashCode());
171 		result = prime * result + ((columnDefault == null) ? 0 : columnDefault.hashCode());
172 		result = prime * result + ((columnName == null) ? 0 : columnName.hashCode());
173 		result = prime * result + ((columnSize == null) ? 0 : columnSize.hashCode());
174 		result = prime * result + ((decimalDigits == null) ? 0 : decimalDigits.hashCode());
175 		result = prime * result + ((generatedcolumn == null) ? 0 : generatedcolumn.hashCode());
176 		result = prime * result + ((isoNullable == null) ? 0 : isoNullable.hashCode());
177 		result = prime * result + ((nullable == null) ? 0 : nullable.hashCode());
178 		result = prime * result + ((numPrecRadix == null) ? 0 : numPrecRadix.hashCode());
179 		result = prime * result + ((ordinalPosition == null) ? 0 : ordinalPosition.hashCode());
180 		result = prime * result + ((remarks == null) ? 0 : remarks.hashCode());
181 		result = prime * result + ((scopeCatalog == null) ? 0 : scopeCatalog.hashCode());
182 		result = prime * result + ((scopeSchema == null) ? 0 : scopeSchema.hashCode());
183 		result = prime * result + ((scopeTable == null) ? 0 : scopeTable.hashCode());
184 		result = prime * result + ((sourceDataType == null) ? 0 : sourceDataType.hashCode());
185 		result = prime * result + ((sqlType == null) ? 0 : sqlType.hashCode());
186 		result = prime * result + ((sqlTypeName == null) ? 0 : sqlTypeName.hashCode());
187 		result = prime * result + ((tableCat == null) ? 0 : tableCat.hashCode());
188 		result = prime * result + ((tableName == null) ? 0 : tableName.hashCode());
189 		result = prime * result + ((tableSchema == null) ? 0 : tableSchema.hashCode());
190 		return result;
191 	}
192 
193 	@Override
194 	public boolean equals(Object obj) {
195 		if (this == obj)
196 			return true;
197 		if (obj == null)
198 			return false;
199 		if (getClass() != obj.getClass())
200 			return false;
201 		ColumnMetaData other = (ColumnMetaData) obj;
202 		if (autoincrement == null) {
203 			if (other.autoincrement != null)
204 				return false;
205 		} else if (!autoincrement.equals(other.autoincrement))
206 			return false;
207 		if (charOctetLength == null) {
208 			if (other.charOctetLength != null)
209 				return false;
210 		} else if (!charOctetLength.equals(other.charOctetLength))
211 			return false;
212 		if (columnDefault == null) {
213 			if (other.columnDefault != null)
214 				return false;
215 		} else if (!columnDefault.equals(other.columnDefault))
216 			return false;
217 		if (columnName == null) {
218 			if (other.columnName != null)
219 				return false;
220 		} else if (!columnName.equals(other.columnName))
221 			return false;
222 		if (columnSize == null) {
223 			if (other.columnSize != null)
224 				return false;
225 		} else if (!columnSize.equals(other.columnSize))
226 			return false;
227 		if (decimalDigits == null) {
228 			if (other.decimalDigits != null)
229 				return false;
230 		} else if (!decimalDigits.equals(other.decimalDigits))
231 			return false;
232 		if (generatedcolumn == null) {
233 			if (other.generatedcolumn != null)
234 				return false;
235 		} else if (!generatedcolumn.equals(other.generatedcolumn))
236 			return false;
237 		if (isoNullable == null) {
238 			if (other.isoNullable != null)
239 				return false;
240 		} else if (!isoNullable.equals(other.isoNullable))
241 			return false;
242 		if (nullable == null) {
243 			if (other.nullable != null)
244 				return false;
245 		} else if (!nullable.equals(other.nullable))
246 			return false;
247 		if (numPrecRadix == null) {
248 			if (other.numPrecRadix != null)
249 				return false;
250 		} else if (!numPrecRadix.equals(other.numPrecRadix))
251 			return false;
252 		if (ordinalPosition == null) {
253 			if (other.ordinalPosition != null)
254 				return false;
255 		} else if (!ordinalPosition.equals(other.ordinalPosition))
256 			return false;
257 		if (remarks == null) {
258 			if (other.remarks != null)
259 				return false;
260 		} else if (!remarks.equals(other.remarks))
261 			return false;
262 		if (scopeCatalog == null) {
263 			if (other.scopeCatalog != null)
264 				return false;
265 		} else if (!scopeCatalog.equals(other.scopeCatalog))
266 			return false;
267 		if (scopeSchema == null) {
268 			if (other.scopeSchema != null)
269 				return false;
270 		} else if (!scopeSchema.equals(other.scopeSchema))
271 			return false;
272 		if (scopeTable == null) {
273 			if (other.scopeTable != null)
274 				return false;
275 		} else if (!scopeTable.equals(other.scopeTable))
276 			return false;
277 		if (sourceDataType == null) {
278 			if (other.sourceDataType != null)
279 				return false;
280 		} else if (!sourceDataType.equals(other.sourceDataType))
281 			return false;
282 		if (sqlType == null) {
283 			if (other.sqlType != null)
284 				return false;
285 		} else if (!sqlType.equals(other.sqlType))
286 			return false;
287 		if (sqlTypeName == null) {
288 			if (other.sqlTypeName != null)
289 				return false;
290 		} else if (!sqlTypeName.equals(other.sqlTypeName))
291 			return false;
292 		if (tableCat == null) {
293 			if (other.tableCat != null)
294 				return false;
295 		} else if (!tableCat.equals(other.tableCat))
296 			return false;
297 		if (tableName == null) {
298 			if (other.tableName != null)
299 				return false;
300 		} else if (!tableName.equals(other.tableName))
301 			return false;
302 		if (tableSchema == null) {
303 			if (other.tableSchema != null)
304 				return false;
305 		} else if (!tableSchema.equals(other.tableSchema))
306 			return false;
307 		return true;
308 	}
309 
310 
311 
312 	private abstract class MetaDataField {
313 		String name;
314 		int index;
315 		
316 		protected MetaDataField(String name, int index) {
317 			super();
318 			this.name = name;
319 			this.index = index;
320 			// Every instance created must be added to the parent object's list of fields
321 			ColumnMetaData.this.fields.add(this);
322 		}
323 
324 		protected void setValue(ResultSet resultSet) throws SQLException {
325 			if (resultSet.getMetaData().getColumnCount() >= this.index) {
326 				Object val = resultSet.getObject(this.index);
327 				// Skip nulls
328 				if (val != null) {
329 					setCheckedValue(resultSet);
330 				}
331 			}
332 		}
333 		
334 		// Having verified that a value for this field is present in the result set, assign it
335 		abstract void setCheckedValue(ResultSet resultSet) throws SQLException;
336 
337 		@Override
338 		public int hashCode() {
339 			final int prime = 31;
340 			int result = 1;
341 			result = prime * result + index;
342 			result = prime * result + ((name == null) ? 0 : name.hashCode());
343 			return result;
344 		}
345 
346 		@Override
347 		public boolean equals(Object obj) {
348 			if (this == obj)
349 				return true;
350 			if (obj == null)
351 				return false;
352 			if (getClass() != obj.getClass())
353 				return false;
354 			MetaDataField other = (MetaDataField) obj;
355 			if (index != other.index)
356 				return false;
357 			if (name == null) {
358 				if (other.name != null)
359 					return false;
360 			} else if (!name.equals(other.name))
361 				return false;
362 			return true;
363 		}
364 		
365 	}
366 	
367 	private class StringMetaDataField extends MetaDataField {
368 		String value;
369 
370 		StringMetaDataField(String name, int index) {
371 			super(name, index);
372 		}
373 		
374 		void setCheckedValue(ResultSet resultSet) throws SQLException{
375 			this.value = resultSet.getString(this.index);
376 		}
377 		
378 		String getString() {
379 			return this.value;
380 		}
381 
382 		@Override
383 		public String toString() {
384 			return "StringMetaDataField [name=" + name + ", index=" + index + ", value=" + value + "]";
385 		}
386 
387 		@Override
388 		public int hashCode() {
389 			final int prime = 31;
390 			int result = super.hashCode();
391 			result = prime * result + ((value == null) ? 0 : value.hashCode());
392 			return result;
393 		}
394 
395 		@Override
396 		public boolean equals(Object obj) {
397 			if (this == obj)
398 				return true;
399 			if (!super.equals(obj))
400 				return false;
401 			if (getClass() != obj.getClass())
402 				return false;
403 			StringMetaDataField other = (StringMetaDataField) obj;
404 			if (value == null) {
405 				if (other.value != null)
406 					return false;
407 			} else if (!value.equals(other.value))
408 				return false;
409 			return true;
410 		}
411 
412 	}
413 	
414 	private class IntegerMetaDataField extends MetaDataField {
415 		Integer value;
416 
417 		IntegerMetaDataField(String name, int index) {
418 			super(name, index);
419 		}
420 		
421 		void setCheckedValue(ResultSet resultSet) throws SQLException{
422 			this.value = resultSet.getInt(this.index);
423 		}
424 		
425 		Integer getInteger() {
426 			return this.value;
427 		}
428 
429 		@Override
430 		public String toString() {
431 			return "IntegerMetaDataField [name=" + name + ", index=" + index + ", value=" + value + "]";
432 		}
433 
434 		@Override
435 		public int hashCode() {
436 			final int prime = 31;
437 			int result = super.hashCode();
438 			result = prime * result + ((value == null) ? 0 : value.hashCode());
439 			return result;
440 		}
441 
442 		@Override
443 		public boolean equals(Object obj) {
444 			if (this == obj)
445 				return true;
446 			if (!super.equals(obj))
447 				return false;
448 			if (getClass() != obj.getClass())
449 				return false;
450 			IntegerMetaDataField other = (IntegerMetaDataField) obj;
451 			if (value == null) {
452 				if (other.value != null)
453 					return false;
454 			} else if (!value.equals(other.value))
455 				return false;
456 			return true;
457 		}
458 		
459 	}
460 	
461 	private class ShortMetaDataField extends MetaDataField {
462 		Short value;
463 
464 		ShortMetaDataField(String name, int index) {
465 			super(name, index);
466 		}
467 		
468 		void setCheckedValue(ResultSet resultSet) throws SQLException{
469 			this.value = resultSet.getShort(this.index);
470 		}
471 		
472 		Short getShort() {
473 			return this.value;
474 		}
475 
476 		@Override
477 		public String toString() {
478 			return "ShortMetaDataField [name=" + name + ", index=" + index + ", value=" + value + "]";
479 		}
480 
481 		@Override
482 		public int hashCode() {
483 			final int prime = 31;
484 			int result = super.hashCode();
485 			result = prime * result + ((value == null) ? 0 : value.hashCode());
486 			return result;
487 		}
488 
489 		@Override
490 		public boolean equals(Object obj) {
491 			if (this == obj)
492 				return true;
493 			if (!super.equals(obj))
494 				return false;
495 			if (getClass() != obj.getClass())
496 				return false;
497 			ShortMetaDataField other = (ShortMetaDataField) obj;
498 			if (value == null) {
499 				if (other.value != null)
500 					return false;
501 			} else if (!value.equals(other.value))
502 				return false;
503 			return true;
504 		}
505 
506 	}
507 
508 }