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;
22  
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.dbunit.dataset.filter.IRowFilter;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  
30  /**
31   * Filters table rows by using arbitrary column values of the table to check if a row should be filtered or not.
32   * <br>
33   * Implemented as a decorator for {@link ITable}.
34   * 
35   * See dbunit feature request at <a href="https://sourceforge.net/tracker/index.php?func=detail&aid=1959771&group_id=47439&atid=449494">#1959771</a>
36   * 
37   * @author gommma
38   * @author Last changed by: $Author$
39   * @version $Revision$ $Date$
40   * @since 2.3.0
41   */
42  public class RowFilterTable implements ITable, IRowValueProvider {
43  
44  	
45  	/** 
46  	 * reference to the original table being wrapped 
47  	 */
48  	private final ITable originalTable;
49  	/** mapping of filtered rows, i.e, each entry on this list has the value of 
50              the index on the original table corresponding to the desired index. 
51              For instance, if the original table is:
52              row   PK  Value
53              0     pk1  v1
54              1     pk2  v2
55              2     pk3  v3
56              3     pk4  v4
57              And the allowed PKs are pk2 and pk4, the new table should be:
58              row   PK  Value
59              0     pk2  v2
60              1     pk4  v4
61              Consequently, the mapping will be {1, 3}
62  	 */
63  	private final List filteredRowIndexes;
64  	/** 
65  	 * logger 
66  	 */
67  	private final Logger logger = LoggerFactory.getLogger(RowFilterTable.class);
68  	/** 
69  	 * The row that is currently checked for filtering. Used in the implementation of {@link IRowValueProvider}
70  	 */
71  	private int currentRowIdx;
72  
73  	/**
74  	 * Creates a new {@link ITable} where some rows can be filtered out from the original table
75  	 * @param table The table to be wrapped
76  	 * @param rowFilter The row filter that checks for every row whether or not it should be filtered
77  	 * @throws DataSetException
78  	 */
79  	public RowFilterTable(ITable table, IRowFilter rowFilter) throws DataSetException {
80  		if ( table == null || rowFilter == null ) {
81  			throw new IllegalArgumentException( "Constructor cannot receive null arguments" );
82  		}
83  		this.originalTable = table;
84  		// sets the rows for the new table
85  		// NOTE: this conversion might be an issue for long tables, as it iterates for 
86  		// all values of the original table and that might take time and memory leaks.
87  		// So, this mapping mechanism is a candidate for improvement: another alternative
88  		// would be to calculate the mapping on the fly, as getValue() is called (and in
89  		// this case, getRowCount() would be simply the size of allowedPKs)
90  		this.filteredRowIndexes = setRows(rowFilter);
91  	}
92  
93  	private List setRows(IRowFilter rowFilter) throws DataSetException {
94  
95  		ITableMetaData tableMetadata = this.originalTable.getTableMetaData();
96  		this.logger.debug("Setting rows for table {}",  tableMetadata.getTableName() );
97  
98  		int fullSize = this.originalTable.getRowCount();
99  		List filteredRowIndexes = new ArrayList();
100 
101 		for ( int row=0; row<fullSize; row++ ) {
102 			this.currentRowIdx = row;
103 			if(rowFilter.accept(this)) {
104 				this.logger.debug("Adding row {}", new Integer(row));
105 				filteredRowIndexes.add(new Integer(row));
106 			} else {
107 				this.logger.debug("Discarding row {}", new Integer(row));        
108 			}
109 		}
110 		return filteredRowIndexes;   
111 	}
112 
113 
114 	// ITable methods
115 
116 	public ITableMetaData getTableMetaData() {
117 		logger.debug("getTableMetaData() - start");
118 
119 		return this.originalTable.getTableMetaData();
120 	}
121 
122 	public int getRowCount() {
123 		logger.debug("getRowCount() - start");
124 
125 		return this.filteredRowIndexes.size();
126 	}
127 
128 	public Object getValue(int row, String column) throws DataSetException 
129 	{
130 	    if(logger.isDebugEnabled())
131 	        logger.debug("getValue(row={}, columnName={}) - start", Integer.toString(row), column);
132 
133 		int max = this.filteredRowIndexes.size();
134 		if ( row < max ) {
135 			int realRow = ((Integer) this.filteredRowIndexes.get( row )).intValue();
136 			Object value = this.originalTable.getValue(realRow, column);
137 			return value;
138 		} else {
139 			throw new RowOutOfBoundsException( "tried to access row " + row + 
140 					" but rowCount is " + max );
141 		}
142 	}
143 
144 
145 	/**
146 	 * Returns the column value for the column with the given name of the currently processed row
147 	 * @throws DataSetException 
148 	 * @see org.dbunit.dataset.IRowValueProvider#getColumnValue(java.lang.String)
149 	 */
150 	public Object getColumnValue(String columnName) throws DataSetException {
151 		Object valueOfCol = this.originalTable.getValue(this.currentRowIdx, columnName);
152 		return valueOfCol;
153 	}
154 
155 
156 }
157 
158