/* Copyright (c) 2004-2006 Ricebridge. All Rights Reserved.
 *
 * This file is available under the terms and conditions of the
 * Ricebridge "Open Source API" policy; Ricebridge grants use of this
 * copyrighted work under the terms of a BSD-style license only. See
 * http://www.opensource.org/licenses/bsd-license.php for more
 * information.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *  - Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above
 *  copyright notice, this list of conditions and the following
 *  disclaimer in the documentation and/or other materials provided
 *  with the distribution.
 *
 *  - Neither the name of the Ricebridge nor the names of its
 *  contributors may be used to endorse or promote products derived
 *  from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.  
 */

package com.ricebridge.csvman;


import org.jostraca.util.TextUtil;
import org.jostraca.util.Internal;

import javax.swing.table.TableModel;
import javax.swing.table.AbstractTableModel;

import java.util.*;


/** Implementation of a <a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/table/TableModel.html">TableModel</a> to hold CSV data.
 *    <p>This <code>TableModel</code> can be used directly with a 
 *  <a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JTable.html">JTable</a>.
 *  Data is represented by <code>String</code> objects, and can be {@link #setEditable editable}.
 *  Missing data is represented by an empty <code>String</code> in the table cell. You may subclass this class to provide
 *  more functionality.</p>
 *    <p>The <b><a href="CsvTableModel.java.html">Source Code</a></b> of this Java class 
 *    is available under a <a href="http://www.opensource.org/licenses/bsd-license.php">BSD-style license</a>.</p>
 */
public class CsvTableModel extends AbstractTableModel implements TableModel {

  /** The table may be editable. */
  protected boolean   iEditable     = false;

  /** The first line of the CSV file may contain headers. */
  protected boolean   iHasHeaders   = false;

  /** Flag to indicate that headers have been loaded. */
  protected boolean   iHeadersStored  = false; 

  /** List of headers as <code>String</code>s. */
  protected List      iHeaders        = new ArrayList();

  /** Table data as a <code>List</code> of <code>String[]</code> arrays. */
  protected ArrayList iData           = new ArrayList();

  /** Maximum number of fields encountered. */
  protected int       iMaxNumFields   = 0;



  /** Support <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Class.html#newInstance">Class.newInstance</a> 
   *  object instantiation. */
  public CsvTableModel() {
    init();
  }


  /** Create a new <code>TableModel</code> with specified a <code>List</code> of <code>String[]</code> arrays. */
  public CsvTableModel( List pData ) {
    init();

    List data = Internal.null_list( pData );
    iData.addAll(data);

    if( 0 < iData.size() ) {
      String[] first = (String[])iData.get(0);
      iMaxNumFields = first.length;
    }
  }


  /** Initialise the internal data containers before loading new CSV data.
   *  If CSV data is reloading using the same {@link CsvManager} object and 
   *  hence the same {@link TableModelLineListener}, the same <code>CsvTableModel</code> instance
   *  will be used, so that this method must be called prior to loading new data.
   *  <code>TableModelLineListener</code> calls this method in {@link TableModelLineListener#startProcessImpl}.
   *  If you subclass this class to provide your own <code>TableModel</code>, you should provide your 
   *  own implementation of this method.
   */
  public void init() {
    iHeadersStored  = false; 
    iHeaders        = new ArrayList();
    iData           = new ArrayList();
    iMaxNumFields   = 0;
  }


  /** Add a data line to the internal data containers for this <code>TableModel</code>. 
   *    <p>The parameter <code>pNumFields</code> indicates the number of actual data fields
   *  found on the current line, which may be less that the number expected, thus <code>pNumFields <= pLine.length</code>.
   *  The remaining members of pLine will be empty <code>String</code>s.</p>
   *  @param pLine data fields
   *  @param pNumFields number of actual data fields
   */
  public void addLine( String[] pLine, int pNumFields ) {
    if( pNumFields > iMaxNumFields ) {
      iMaxNumFields = pNumFields;
    }
    if( pLine.length > iMaxNumFields ) {
      iMaxNumFields = pLine.length;
    }

    if( iHasHeaders && !iHeadersStored ) {
      iHeaders = Arrays.asList( pLine );
      iHeadersStored = true;
    }
    else {
      iData.add( pLine );
    }
  }


  /** The data provided via {@link #addLine} includes column headers.
   *    <p>When <code>true</code>, the first call to <code>addLine</code> is assumed to be the list of column headers.</p>
   *    <p>When <code>false</code>, the table has no headers.</p>
   *  @param pHasHeaders data has headers
   *  @see TableModelLineListener#startProcessImpl
   *  @see HeadersListenerSupport#setHasHeaders(boolean)
   *  @see CsvManager#loadTableModel(File,boolean)
   */
  public void setHasHeaders( boolean pHasHeaders ) {
    iHasHeaders = pHasHeaders;
  }


  /** You can use this setting to make your <a href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JTable.html">JTable</a>
   *  editable. 
   *  <p>You will need to access this object first, like so:<pre>
   *     TableModelLineListener tmln 
   *       = csvManager.getCsvManagerStore().getTableModelLineListener();
   *     tmln.setEditable( true );
   *  </pre></p>
   *  @param pEditable editable table
   */
  public void setEditable( boolean pEditable ) {
    iEditable = pEditable;
  }


  // TableModel interface

  /** Get the name of the column.
   *  @param pIndex index of column (from zero)
   */
  public String getColumnName( int pIndex ) {
    if( iHasHeaders && pIndex < iHeaders.size() ) {
      return String.valueOf( iHeaders.get( pIndex ) );
    }

    return "Column "+(pIndex+1);
  }


  /** Get number of rows in table. */
  public int getRowCount() {
    return iData.size();
  }


  /** Get number of columns in table. */
  public int getColumnCount() {
    return iMaxNumFields;
  }


  /** Get the value of a table cell. 
   *  @param pRow    index of row (from zero)
   *  @param pColumn index of column (from zero)
   */
  public Object getValueAt( int pRow, int pColumn ) {
    if( pRow >= iData.size() ) {
      return "";
    }
    else {
      String[] line = (String[]) iData.get( pRow );
      if( pColumn >= line.length ) {
        return "";
      }
      else {
        return line[pColumn];
      }
    }
  }


  /** Indicate that table cell value can be changed. 
   *  @param pRow    index of row (from zero)
   *  @param pColumn index of column (from zero)
   */ 
  public boolean isCellEditable( int pRow, int pColumn ) {
    return iEditable;
  }


  /** Set the value of a table cell.
   *  @param pValue  new value of cell
   *  @param pRow    index of row (from zero)
   *  @param pColumn index of column (from zero)
   */
  public void setValueAt( Object pValue, int pRow, int pColumn ) {
    if( pRow < iData.size() ) {
      String[] line = (String[]) iData.get( pRow );
      String   value = String.valueOf( null==pValue?"":pValue );
      if( pColumn >= line.length ) {
        String[] newline = new String[ pColumn+1 ];
        System.arraycopy( line, 0, newline, 0, line.length );
        for( int fI = line.length; fI < pColumn; fI++ ) {
          newline[fI] = "";
        }
        newline[pColumn] = value;
        iData.set( pRow, newline );
      }
      else {
        line[pColumn] = value;
      }
    }
    fireTableCellUpdated( pRow, pColumn );
  }


  /** <code>String</code> description of object instance. */
  public String toString() {
    return "CsvTableModel[head:"+iHasHeaders+",edit:"+iEditable+", data:"+makeString(iData)+"]";
  }



  // private

  /** Returns <code>List</code> of <code>String[]</code> arrays as <code>String</code>. */
  private String makeString( List pData ) {
    StringBuffer sb = new StringBuffer( pData.size() * 55 );
    sb.append("[\n");
    int numRows = getRowCount();
    int numCols = getColumnCount();
    for( int rowI = 0; rowI < numRows; rowI++ ) {
      for( int colI = 0; colI < numCols; colI++ ) {
        sb.append("|");
        sb.append( getValueAt(rowI,colI) );
      }
      sb.append("|\n");
    }
    sb.append("]");
    return sb.toString();
  }  
}
Syntax Highlighting created using the com.Ostermiller.Syntax package.
Saturday, October 07 2006 at 12:22