/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.ui.datasources.table;
import org.pentaho.reporting.engine.classic.core.util.TypedTableModel;
import org.pentaho.reporting.engine.classic.core.util.beans.ConverterRegistry;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import java.util.ArrayList;
public class TableEditorModel extends AbstractTableModel {
private static final int COL_INCREMENT = 50;
private boolean suspendEvents;
private ArrayList<String> columnNames;
private ArrayList<Class> columnTypes;
private ArrayList<ArrayList<Object>> data;
/**
* Constructs a default <code>DefaultTableModel</code> which is a table of zero columns and zero rows.
*/
public TableEditorModel() {
this.columnNames = new ArrayList<String>( COL_INCREMENT );
this.columnTypes = new ArrayList<Class>( COL_INCREMENT );
this.data = new ArrayList<ArrayList<Object>>( 1000 );
}
public void setColumnName( final int index, final String identifier ) {
if ( index == 0 ) {
throw new IllegalStateException();
}
columnNames.set( index - 1, identifier );
fireTableStructureChanged();
}
public void removeColumn( final int column ) {
final int size = data.size();
for ( int rowIndex = 0; rowIndex < size; rowIndex++ ) {
final ArrayList theRow = data.get( rowIndex );
theRow.remove( column );
}
columnNames.remove( column );
columnTypes.remove( column );
fireTableStructureChanged();
}
public void removeRow( final int row ) {
data.remove( row );
}
public void addColumn( final String name, final Class type ) {
columnNames.add( name );
columnTypes.add( type );
final int colCount = getColumnCount();
for ( int theRowIndex = 0; theRowIndex < data.size(); theRowIndex++ ) {
final ArrayList<Object> row = data.get( theRowIndex );
while ( row.size() < colCount ) {
row.add( null );
}
}
fireTableStructureChanged();
}
public void addRow() {
final int colCount = columnNames.size();
final ArrayList<Object> newRow = new ArrayList<Object>( colCount );
for ( int i = 0; i < colCount; i++ ) {
newRow.add( null );
}
data.add( newRow );
fireTableDataChanged();
}
public void addRow( final int idx ) {
final int colCount = columnNames.size();
final ArrayList<Object> newRow = new ArrayList<Object>( colCount );
for ( int i = 0; i < colCount; i++ ) {
newRow.add( null );
}
data.add( idx, newRow );
fireTableDataChanged();
}
/**
* Returns a default name for the column using spreadsheet conventions: A, B, C, ... Z, AA, AB, etc. If
* <code>column</code> cannot be found, returns an empty string.
*
* @param column the column being queried
* @return a string containing the default name of <code>column</code>
*/
public String getColumnName( final int column ) {
if ( column == 0 ) {
return "#";
}
return columnNames.get( column - 1 );
}
/**
* Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
*
* @param columnIndex the column being queried
* @return the Object.class
*/
public Class<?> getColumnClass( final int columnIndex ) {
if ( columnIndex == 0 ) {
return Integer.class;
}
return columnTypes.get( columnIndex - 1 );
}
/**
* Returns the number of rows in the model. A <code>JTable</code> uses this method to determine how many rows it
* should display. This method should be quick, as it is called frequently during rendering.
*
* @return the number of rows in the model
* @see #getColumnCount
*/
public int getRowCount() {
return data.size();
}
/**
* Returns the number of columns in the model. A <code>JTable</code> uses this method to determine how many columns it
* should create and display by default.
*
* @return the number of columns in the model
* @see #getRowCount
*/
public int getColumnCount() {
if ( columnNames.isEmpty() ) {
return 0;
}
return 1 + columnNames.size();
}
/**
* Returns the value for the cell at <code>columnIndex</code> and <code>rowIndex</code>.
*
* @param rowIndex the row whose value is to be queried
* @param columnIndex the column whose value is to be queried
* @return the value Object at the specified cell
*/
public Object getValueAt( final int rowIndex, final int columnIndex ) {
if ( columnIndex == 0 ) {
return Integer.valueOf( rowIndex + 1 );
}
final ArrayList<Object> list = data.get( rowIndex );
return list.get( columnIndex - 1 );
}
/**
* Returns false. This is the default implementation for all cells.
*
* @param rowIndex the row being queried
* @param columnIndex the column being queried
* @return false
*/
public boolean isCellEditable( final int rowIndex, final int columnIndex ) {
return columnIndex != 0;
}
/**
* This empty implementation is provided so users don't have to implement this method if their data model is not
* editable.
*
* @param aValue value to assign to cell
* @param rowIndex row of cell
* @param columnIndex column of cell
*/
public void setValueAt( final Object aValue, final int rowIndex, final int columnIndex ) {
if ( columnIndex == 0 ) {
throw new IllegalStateException();
}
final ArrayList<Object> list = data.get( rowIndex );
list.set( columnIndex - 1, aValue );
fireTableCellUpdated( rowIndex, columnIndex - 1 );
}
public void clear() {
data.clear();
columnNames.clear();
columnTypes.clear();
fireTableStructureChanged();
}
public void setColumnClass( final int index, final Class type ) {
if ( index == 0 ) {
return;
}
if ( type == null ) {
throw new NullPointerException();
}
final int realIndex = index - 1;
columnTypes.set( realIndex, type );
for ( int rowIndex = 0; rowIndex < getRowCount(); rowIndex++ ) {
final Object currentValue = getValueAt( rowIndex, index );
final Object newValue = ConverterRegistry.convert( currentValue, type, null );
setValueAt( newValue, rowIndex, index );
}
fireTableStructureChanged();
}
/**
* Notifies all listeners that all cell values in the table's rows may have changed. The number of rows may also have
* changed and the <code>JTable</code> should redraw the table from scratch. The structure of the table (as in the
* order of the columns) is assumed to be the same.
*
* @see javax.swing.event.TableModelEvent
* @see javax.swing.event.EventListenerList
* @see javax.swing.JTable#tableChanged(javax.swing.event.TableModelEvent)
*/
public void fireTableDataChanged() {
if ( suspendEvents ) {
return;
}
super.fireTableDataChanged();
}
/**
* Notifies all listeners that the table's structure has changed. The number of columns in the table, and the names
* and types of the new columns may be different from the previous state. If the <code>JTable</code> receives this
* event and its <code>autoCreateColumnsFromModel</code> flag is set it discards any table columns that it had and
* reallocates default columns in the order they appear in the model. This is the same as calling
* <code>setModel(TableModel)</code> on the <code>JTable</code>.
*
* @see javax.swing.event.TableModelEvent
* @see javax.swing.event.EventListenerList
*/
public void fireTableStructureChanged() {
if ( suspendEvents ) {
return;
}
super.fireTableStructureChanged();
}
/**
* Notifies all listeners that rows in the range <code>[firstRow, lastRow]</code>, inclusive, have been inserted.
*
* @param firstRow the first row
* @param lastRow the last row
* @see javax.swing.event.TableModelEvent
* @see javax.swing.event.EventListenerList
*/
public void fireTableRowsInserted( final int firstRow, final int lastRow ) {
if ( suspendEvents ) {
return;
}
super.fireTableRowsInserted( firstRow, lastRow );
}
/**
* Notifies all listeners that rows in the range <code>[firstRow, lastRow]</code>, inclusive, have been updated.
*
* @param firstRow the first row
* @param lastRow the last row
* @see javax.swing.event.TableModelEvent
* @see javax.swing.event.EventListenerList
*/
public void fireTableRowsUpdated( final int firstRow, final int lastRow ) {
if ( suspendEvents ) {
return;
}
super.fireTableRowsUpdated( firstRow, lastRow );
}
/**
* Notifies all listeners that rows in the range <code>[firstRow, lastRow]</code>, inclusive, have been deleted.
*
* @param firstRow the first row
* @param lastRow the last row
* @see javax.swing.event.TableModelEvent
* @see javax.swing.event.EventListenerList
*/
public void fireTableRowsDeleted( final int firstRow, final int lastRow ) {
if ( suspendEvents ) {
return;
}
super.fireTableRowsDeleted( firstRow, lastRow );
}
/**
* Notifies all listeners that the value of the cell at <code>[row, column]</code> has been updated.
*
* @param row row of cell which has been updated
* @param column column of cell which has been updated
* @see javax.swing.event.TableModelEvent
* @see javax.swing.event.EventListenerList
*/
public void fireTableCellUpdated( final int row, final int column ) {
if ( suspendEvents ) {
return;
}
super.fireTableCellUpdated( row, column );
}
/**
* Forwards the given notification event to all <code>TableModelListeners</code> that registered themselves as
* listeners for this table model.
*
* @param e the event to be forwarded
* @see javax.swing.event.TableModelEvent
* @see javax.swing.event.EventListenerList
*/
public void fireTableChanged( final TableModelEvent e ) {
if ( suspendEvents ) {
return;
}
super.fireTableChanged( e );
}
public boolean isSuspendEvents() {
return suspendEvents;
}
public void setSuspendEvents( final boolean suspendEvents ) {
this.suspendEvents = suspendEvents;
if ( suspendEvents == false ) {
fireTableStructureChanged();
}
}
public void copyInto( final TableModel model ) {
try {
setSuspendEvents( true );
clear();
if ( model == null ) {
return;
}
final int columnCount = model.getColumnCount();
for ( int col = 0; col < columnCount; col++ ) {
addColumn( model.getColumnName( col ), model.getColumnClass( col ) );
}
final int rowCount = model.getRowCount();
for ( int r = 0; r < rowCount; r++ ) {
addRow();
for ( int col = 0; col < columnCount; col++ ) {
final Object originalValue = model.getValueAt( r, col );
setValueAt( originalValue, r, col + 1 );
}
}
} finally {
setSuspendEvents( false );
}
}
public TableModel createModel() {
final TypedTableModel tableModel = new TypedTableModel();
final int columnCount = getColumnCount();
for ( int col = 1; col < columnCount; col++ ) {
tableModel.addColumn( getColumnName( col ), getColumnClass( col ) );
}
final int rowCount = getRowCount();
for ( int r = 0; r < rowCount; r++ ) {
for ( int col = 1; col < columnCount; col++ ) {
tableModel.setValueAt( getValueAt( r, col ), r, col - 1 );
}
}
return tableModel;
}
}