1 /*
2 *
3 * The DbUnit Database Testing Framework
4 * Copyright (C)2005, 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
22 package org.dbunit.database;
23
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Set;
27
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30 import org.dbunit.dataset.DataSetException;
31 import org.dbunit.dataset.ITable;
32 import org.dbunit.dataset.ITableMetaData;
33 import org.dbunit.dataset.RowOutOfBoundsException;
34
35 /**
36 * This class is a wrapper for another table with the condition that only a subset
37 * of the original table will be available - the subset is defined by the set of
38 * primary keys that are allowed in the new table.
39 *
40 * @author Felipe Leme (dbunit@felipeal.net)
41 * @version $Revision$
42 * @since Sep 9, 2005
43 */
44 public class PrimaryKeyFilteredTableWrapper implements ITable {
45
46 /** reference to the original table being wrapped */
47 private final ITable originalTable;
48 /** mapping of filtered rows, i.e, each entry on this list has the value of
49 the index on the original table corresponding to the desired index.
50 For instance, if the original table is:
51 row PK Value
52 0 pk1 v1
53 1 pk2 v2
54 2 pk3 v3
55 3 pk4 v4
56 And the allowed PKs are pk2 and pk4, the new table should be:
57 row PK Value
58 0 pk2 v2
59 1 pk4 v4
60 Consequently, the mapping will be {1, 3}
61
62 */
63 private final List filteredRowsMapping;
64 /** logger */
65 protected final Logger logger = LoggerFactory.getLogger(getClass());
66
67 /**
68 * Creates a PKFilteredTable given an original table and the allowed primary keys
69 * for that table.
70 * @param table original table
71 * @param allowedPKs primary keys allowed on the new table
72 * @throws DataSetException if something happened while getting the information
73 */
74 public PrimaryKeyFilteredTableWrapper(ITable table, Set allowedPKs) throws DataSetException {
75 if ( table == null || allowedPKs == null ) {
76 throw new IllegalArgumentException( "Constructor cannot receive null arguments" );
77 }
78 this.originalTable = table;
79 // sets the rows for the new table
80 // NOTE: this conversion might be an issue for long tables, as it iterates for
81 // all values of the original table and that might take time and memory leaks.
82 // So, this mapping mechanism is a candidate for improvement: another alternative
83 // would be to calculate the mapping on the fly, as getValue() is called (and in
84 // this case, getRowCount() would be simply the sise of allowedPKs)
85 this.filteredRowsMapping = setRows( allowedPKs );
86 }
87
88 /**
89 * This method is used to calculate the mapping between the rows of the original
90 * and the filtered tables.
91 * @param allowedPKs primary keys allowed in the new table
92 * @return list of rows for the new table
93 * @throws DataSetException
94 */
95 private List setRows(Set allowedPKs) throws DataSetException {
96 if ( this.logger.isDebugEnabled() ) {
97 this.logger.debug( "Setting rows for table " +
98 this.originalTable.getTableMetaData().getTableName() );
99 }
100 int allowedSize = allowedPKs.size();
101 int fullSize = this.originalTable.getRowCount();
102 List mapping = new ArrayList( allowedSize );
103 // TODO: support multi-columns PKs
104 String pkColumn = this.originalTable.getTableMetaData().getPrimaryKeys()[0].getColumnName();
105 for ( int row=0; row<fullSize; row++ ) {
106 Object pk = this.originalTable.getValue( row, pkColumn );
107 if ( allowedPKs.contains(pk) ) {
108 if ( this.logger.isDebugEnabled() ) {
109 this.logger.debug( "Adding row " + row + " (pk=" + pk + ")" );
110 }
111 mapping.add(row);
112 } else {
113 if ( this.logger.isDebugEnabled() ) {
114 this.logger.debug("Discarding row " + row + " (pk=" + pk + ")" );
115 }
116 }
117 }
118 return mapping;
119 }
120
121 // ITable methods
122
123 public ITableMetaData getTableMetaData() {
124 return this.originalTable.getTableMetaData();
125 }
126
127 public int getRowCount() {
128 return this.filteredRowsMapping.size();
129 }
130
131 public Object getValue(int row, String column) throws DataSetException
132 {
133 if(logger.isDebugEnabled())
134 logger.debug("getValue(row={}, columnName={}) - start", Integer.toString(row), column);
135
136 int max = this.filteredRowsMapping.size();
137 if ( row < max ) {
138 int realRow = ((Integer) this.filteredRowsMapping.get( row )).intValue();
139 Object value = this.originalTable.getValue(realRow, column);
140 return value;
141 } else {
142 throw new RowOutOfBoundsException( "tried to access row " + row +
143 " but rowCount is " + max );
144 }
145 }
146
147 }