View Javadoc
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( new Integer(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 }