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.stream;
22  
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  
29  import org.dbunit.dataset.Column;
30  import org.dbunit.dataset.DataSetException;
31  import org.dbunit.dataset.ITableMetaData;
32  import org.dbunit.dataset.xml.FlatXmlDataSet;
33  
34  /**
35   * Implementation of {@link IDataSetConsumer} which buffers all data
36   * until the {@link #endDataSet()} event occurs.
37   * This provides the possibility to append new {@link Column}s on
38   * the fly which is needed for the column sensing feature in
39   * {@link FlatXmlDataSet}.
40   * 
41   * @author gommma (gommma AT users.sourceforge.net)
42   * @author Last changed by: $Author$
43   * @version $Revision$ $Date$
44   * @since 2.3.0
45   */
46  public class BufferedConsumer implements IDataSetConsumer {
47  
48  	private IDataSetConsumer _wrappedConsumer;
49  	
50  	/**
51  	 * The table which is currently active
52  	 */
53  	private TableBuffer _activeTable;
54  	/**
55  	 * List that stores all {@link TableBuffer}s in a sorted fashion so that the 
56  	 * table that was added first will also be flushed first when the {@link IDataSetConsumer}
57  	 * is finally invoked.
58  	 */
59  	private List _tableBuffers = new ArrayList();
60  	/**
61  	 * Map that stores the table names as key and the {@link TableBuffer} as value
62  	 */
63  	private Map _tableNames = new HashMap();
64  	
65  	
66  	/**
67  	 * @param wrappedConsumer The consumer that is wrapped
68  	 */
69  	public BufferedConsumer(IDataSetConsumer wrappedConsumer) 
70  	{
71  		if (wrappedConsumer == null) {
72  			throw new NullPointerException(
73  					"The parameter '_wrappedConsumer' must not be null");
74  		}
75  		this._wrappedConsumer = wrappedConsumer;
76  	}
77  	
78  	public void startDataSet() throws DataSetException 
79  	{
80  		this._wrappedConsumer.startDataSet();
81  	}
82  
83  	public void endDataSet() throws DataSetException 
84  	{
85  	    // Flush out the whole collected dataset
86  	    
87          // Start the table with the final metadata
88  	    for (Iterator iterator = _tableBuffers.iterator(); iterator.hasNext();) {
89  	        TableBuffer entry = (TableBuffer) iterator.next();
90  	        ITableMetaData metaData = (ITableMetaData) entry.getMetaData();
91              
92  	        this._wrappedConsumer.startTable(metaData);
93  	        
94  	        List dataRows = (List) entry.getDataRows();
95  	        for (Iterator dataIterator = dataRows.iterator(); dataIterator.hasNext();) {
96  	            Object[] rowValues = (Object[]) dataIterator.next();
97  	            this._wrappedConsumer.row(rowValues);
98  	        }
99              // Clear the row data for this table finally
100             dataRows.clear();
101 	        
102 	        this._wrappedConsumer.endTable();
103         }
104 
105 	    // Finally notify consumer of the end of this DataSet
106 		this._wrappedConsumer.endDataSet();
107 	}
108 
109 	public void row(Object[] values) throws DataSetException 
110 	{
111 		// Just collect/buffer the row
112 	    this._activeTable.getDataRows().add(values);
113 	}
114 
115 	public void startTable(ITableMetaData metaData) throws DataSetException 
116 	{
117 		// Do nothing here - we will buffer all data in the "row" method in order to write
118 		// them in the "endTable" method
119 	    if(_tableNames.containsKey(metaData.getTableName()))
120 	    {
121 	        this._activeTable = (TableBuffer) _tableNames.get(metaData.getTableName());
122 	        // overwrite the metadata with the new one which potentially contains new columns
123 	        this._activeTable.setMetaData(metaData);
124 	    }
125 	    else
126 	    {
127 	        _activeTable = new TableBuffer(metaData);
128 
129             _tableBuffers.add(_activeTable);// add to the sorted list
130             _tableNames.put(metaData.getTableName(), _activeTable);// add to the name map
131 	    }
132 	}
133 
134 	public void endTable() throws DataSetException 
135 	{
136 		if(this._activeTable == null) {
137 			throw new IllegalStateException("The field _activeMetaData must not be null at this stage");
138 		}
139 
140 		Column[] columns = this._activeTable.getMetaData().getColumns();
141 		int finalColumnCount = columns.length;
142 
143 		int rowCount = this._activeTable.getDataRows().size();
144 		// Fill up columns that were potentially missing in this row
145 		for (int i=0; i < rowCount; i++) {
146 			// Note that this only works when new columns are always added at the end to the _activeMetaData
147 			Object[] rowValues = (Object[]) this._activeTable.getDataRows().get(i);
148 			// If this row has less columns than final metaData, fill it up with "null"s so that it matches the length
149 			if(rowValues.length < finalColumnCount) {
150 				Object[] newRowValues = new Object[finalColumnCount];
151 				// Put in original values and leave all missing columns on "null"
152 				System.arraycopy(rowValues, 0, newRowValues, 0, rowValues.length);
153 				this._activeTable.getDataRows().set(i, newRowValues);
154 			}
155 		}
156 	}
157 
158 	
159 	private static class TableBuffer
160 	{
161 	    private ITableMetaData metaData;
162 	    private final ArrayList dataRows;
163 	    
164         public TableBuffer(ITableMetaData metaData) {
165             this(metaData, new ArrayList());
166         }
167 
168         public TableBuffer(ITableMetaData metaData, ArrayList dataRows) {
169             super();
170             this.metaData = metaData;
171             this.dataRows = dataRows;
172         }
173 
174         public ITableMetaData getMetaData() {
175             return metaData;
176         }
177 
178         public void setMetaData(ITableMetaData metaData) {
179             this.metaData = metaData;
180         }
181 
182         public ArrayList getDataRows() {
183             return dataRows;
184         }
185 
186 	}
187 }