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: gommma $
39 * @version $Revision: 850 $ $Date: 2008-10-31 19:39:59 +0100 (ven, 31 ott 2008) $
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