BufferedConsumer.java

  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. import java.util.ArrayList;
  23. import java.util.HashMap;
  24. import java.util.Iterator;
  25. import java.util.List;
  26. import java.util.Map;

  27. import org.dbunit.dataset.Column;
  28. import org.dbunit.dataset.DataSetException;
  29. import org.dbunit.dataset.ITableMetaData;
  30. import org.dbunit.dataset.xml.FlatXmlDataSet;

  31. /**
  32.  * Implementation of {@link IDataSetConsumer} which buffers all data
  33.  * until the {@link #endDataSet()} event occurs.
  34.  * This provides the possibility to append new {@link Column}s on
  35.  * the fly which is needed for the column sensing feature in
  36.  * {@link FlatXmlDataSet}.
  37.  *
  38.  * @author gommma (gommma AT users.sourceforge.net)
  39.  * @author Last changed by: $Author$
  40.  * @version $Revision$ $Date$
  41.  * @since 2.3.0
  42.  */
  43. public class BufferedConsumer implements IDataSetConsumer {

  44.     private IDataSetConsumer _wrappedConsumer;
  45.    
  46.     /**
  47.      * The table which is currently active
  48.      */
  49.     private TableBuffer _activeTable;
  50.     /**
  51.      * List that stores all {@link TableBuffer}s in a sorted fashion so that the
  52.      * table that was added first will also be flushed first when the {@link IDataSetConsumer}
  53.      * is finally invoked.
  54.      */
  55.     private List _tableBuffers = new ArrayList();
  56.     /**
  57.      * Map that stores the table names as key and the {@link TableBuffer} as value
  58.      */
  59.     private Map _tableNames = new HashMap();
  60.    
  61.    
  62.     /**
  63.      * @param wrappedConsumer The consumer that is wrapped
  64.      */
  65.     public BufferedConsumer(IDataSetConsumer wrappedConsumer)
  66.     {
  67.         if (wrappedConsumer == null) {
  68.             throw new NullPointerException(
  69.                     "The parameter '_wrappedConsumer' must not be null");
  70.         }
  71.         this._wrappedConsumer = wrappedConsumer;
  72.     }
  73.    
  74.     public void startDataSet() throws DataSetException
  75.     {
  76.         this._wrappedConsumer.startDataSet();
  77.     }

  78.     public void endDataSet() throws DataSetException
  79.     {
  80.         // Flush out the whole collected dataset
  81.        
  82.         // Start the table with the final metadata
  83.         for (Iterator iterator = _tableBuffers.iterator(); iterator.hasNext();) {
  84.             TableBuffer entry = (TableBuffer) iterator.next();
  85.             ITableMetaData metaData = (ITableMetaData) entry.getMetaData();
  86.            
  87.             this._wrappedConsumer.startTable(metaData);
  88.            
  89.             List dataRows = (List) entry.getDataRows();
  90.             for (Iterator dataIterator = dataRows.iterator(); dataIterator.hasNext();) {
  91.                 Object[] rowValues = (Object[]) dataIterator.next();
  92.                 this._wrappedConsumer.row(rowValues);
  93.             }
  94.             // Clear the row data for this table finally
  95.             dataRows.clear();
  96.            
  97.             this._wrappedConsumer.endTable();
  98.         }

  99.         // Finally notify consumer of the end of this DataSet
  100.         this._wrappedConsumer.endDataSet();
  101.     }

  102.     public void row(Object[] values) throws DataSetException
  103.     {
  104.         // Just collect/buffer the row
  105.         this._activeTable.getDataRows().add(values);
  106.     }

  107.     public void startTable(ITableMetaData metaData) throws DataSetException
  108.     {
  109.         // Do nothing here - we will buffer all data in the "row" method in order to write
  110.         // them in the "endTable" method
  111.         if(_tableNames.containsKey(metaData.getTableName()))
  112.         {
  113.             this._activeTable = (TableBuffer) _tableNames.get(metaData.getTableName());
  114.             // overwrite the metadata with the new one which potentially contains new columns
  115.             this._activeTable.setMetaData(metaData);
  116.         }
  117.         else
  118.         {
  119.             _activeTable = new TableBuffer(metaData);

  120.             _tableBuffers.add(_activeTable);// add to the sorted list
  121.             _tableNames.put(metaData.getTableName(), _activeTable);// add to the name map
  122.         }
  123.     }

  124.     public void endTable() throws DataSetException
  125.     {
  126.         if(this._activeTable == null) {
  127.             throw new IllegalStateException("The field _activeMetaData must not be null at this stage");
  128.         }

  129.         Column[] columns = this._activeTable.getMetaData().getColumns();
  130.         int finalColumnCount = columns.length;

  131.         int rowCount = this._activeTable.getDataRows().size();
  132.         // Fill up columns that were potentially missing in this row
  133.         for (int i=0; i < rowCount; i++) {
  134.             // Note that this only works when new columns are always added at the end to the _activeMetaData
  135.             Object[] rowValues = (Object[]) this._activeTable.getDataRows().get(i);
  136.             // If this row has less columns than final metaData, fill it up with "null"s so that it matches the length
  137.             if(rowValues.length < finalColumnCount) {
  138.                 Object[] newRowValues = new Object[finalColumnCount];
  139.                 // Put in original values and leave all missing columns on "null"
  140.                 System.arraycopy(rowValues, 0, newRowValues, 0, rowValues.length);
  141.                 this._activeTable.getDataRows().set(i, newRowValues);
  142.             }
  143.         }
  144.     }

  145.    
  146.     private static class TableBuffer
  147.     {
  148.         private ITableMetaData metaData;
  149.         private final ArrayList dataRows;
  150.        
  151.         public TableBuffer(ITableMetaData metaData) {
  152.             this(metaData, new ArrayList());
  153.         }

  154.         public TableBuffer(ITableMetaData metaData, ArrayList dataRows) {
  155.             super();
  156.             this.metaData = metaData;
  157.             this.dataRows = dataRows;
  158.         }

  159.         public ITableMetaData getMetaData() {
  160.             return metaData;
  161.         }

  162.         public void setMetaData(ITableMetaData metaData) {
  163.             this.metaData = metaData;
  164.         }

  165.         public ArrayList getDataRows() {
  166.             return dataRows;
  167.         }

  168.     }
  169. }