package prefuse.data; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import javax.swing.event.TableModelEvent; import prefuse.data.column.Column; import prefuse.data.column.ColumnFactory; import prefuse.data.column.ColumnMetadata; import prefuse.data.event.ColumnListener; import prefuse.data.event.EventConstants; import prefuse.data.event.TableListener; import prefuse.data.expression.Expression; import prefuse.data.expression.Predicate; import prefuse.data.expression.parser.ExpressionParser; import prefuse.data.tuple.AbstractTupleSet; import prefuse.data.tuple.TableTuple; import prefuse.data.tuple.TupleManager; import prefuse.data.util.FilterIteratorFactory; import prefuse.data.util.Index; import prefuse.data.util.RowManager; import prefuse.data.util.TableIterator; import prefuse.data.util.TreeIndex; import prefuse.util.TypeLib; import prefuse.util.collections.CopyOnWriteArrayList; import prefuse.util.collections.IncompatibleComparatorException; import prefuse.util.collections.IntIterator; /** *
A Table organizes a collection of data into rows and columns, each row * containing a data record, and each column containing data values for a * named data field with a specific data type. Table data can be accessed * directly using the row number and column name, or rows can be treated * in an object-oriented fashion using {@link prefuse.data.Tuple} * instances that represent a single row of data in the table. As such, * tables implement the {@link prefuse.data.tuple.TupleSet} interface.
* *Table rows can be inserted or deleted. In any case, none of the other * existing table rows are effected by an insertion or deletion. A deleted * row simply becomes invalid--any subsequent attempts to access the row * either directly or through a pre-existing Tuple instance will result * in an exception. However, if news rows are later added to the table, * the row number for previously deleted nodes can be reused. In fact, the * lower row number currently unused is assigned to the new row. This results * in an efficient reuse of the table rows, but carries an important side * effect -- rows do not necesarily maintain the order in which they were * added once deletions have occurred on the table. If not deletions * occur, the ordering of table rows will reflect the order in which * rows were added to the table.
* *Collections of table rows can be accessed using both iterators over * the actual row numbers and iterators over the Tuple instances that * encapsulate access to that row. Both types of iteration can also be * filtered by providing a {@link prefuse.data.expression.Predicate}, * allowing tables to be queried for specific values.
* *Columns (alternativele referred to as data fields) can be added to * the Table using {@link #addColumn(String, Class)} and a host of * similar methods. This method will automatically determine the right * kind of backing column instance to use. Furthermore, Table columns * can be specified using a {@link Schema} instance, which describes * the column names, data types, and default values. The Table class * also maintains its own internal Schema, which be accessed (in a * read-only way) using the {@link #getSchema()} method.
* *Tables also support additional structures. The {@link ColumnMetadata} * class returned by the {@link #getMetadata(String)} method supports * calculation of different statistics for a column, including minimum * and maximum values, and the number of unique data values in the column. * {@link prefuse.data.util.Index} instances can be created and retrieved * using the {@link #index(String)} method and retrieved without triggering * creation using {@link #getIndex(String)} method. An index keeps a * sorted collection of all data values in a column, accelerating the creation * of filtered iterators by optimizing query calculations and also providing * faster computation of many of the {@link ColumnMetadata} methods. If * you will be issuing a number of queries (i.e., requesting filtered * iterators) dependent on the values of a given column, indexing that column * may result in a significant performance increase, though at the cost * of storing and maintaining the backing index structure.
* * @author jeffrey heer */ public class Table extends AbstractTupleSet implements ColumnListener { /** Listeners for changes to this table */ protected CopyOnWriteArrayList m_listeners; /** Locally stored data columns */ protected ArrayList m_columns; /** Column names for locally store data columns */ protected ArrayList m_names; /** Mapping between column names and column entries * containing column, metadata, and index references */ protected HashMap m_entries; /** Manager for valid row indices */ protected RowManager m_rows; /** manager for tuples, which are object representations for rows */ protected TupleManager m_tuples; /** Tracks the number of edits of this table */ protected int m_modCount = 0; /** Memoize the index of the last column operated on, * used to expedite handling of column updates. */ protected int m_lastCol = -1; /** A cached schema instance, loaded lazily */ protected Schema m_schema; // ------------------------------------------------------------------------ // Constructors /** * Create a new, empty Table. Rows can be added to the table using * the {@link #addRow()} method. */ public Table() { this(0, 0); } /** * Create a new Table with a given number of rows, and the starting * capacity for a given number of columns. * @param nrows the starting number of table rows * @param ncols the starting capacity for columns */ public Table(int nrows, int ncols) { this(nrows, ncols, TableTuple.class); } /** * Create a new Table. * @param nrows the starting number of table rows * @param ncols the starting capacity for columns * @param tupleType the class of the Tuple instances to use */ protected Table(int nrows, int ncols, Class tupleType) { m_listeners = new CopyOnWriteArrayList(); m_columns = new ArrayList(ncols); m_names = new ArrayList(ncols); m_rows = new RowManager(this); m_entries = new HashMap(ncols+5); m_tuples = new TupleManager(this, null, tupleType); if ( nrows > 0 ) addRows(nrows); } // ------------------------------------------------------------------------ // Table Metadata /** * Get the number of columns / data fields in this table. * @return the number of columns */ public int getColumnCount() { return m_columns.size(); } /** * Get the data type of the column at the given column index. * @param col the column index * @return the data type (as a Java Class) of the column */ public Class getColumnType(int col) { return getColumn(col).getColumnType(); } /** * Get the data type of the column with the given data field name. * @param field the column / data field name * @return the data type (as a Java Class) of the column */ public Class getColumnType(String field) { Column c = getColumn(field); return (c==null ? null : c.getColumnType()); } /** * Get the number of rows in the table. * @return the number of rows */ public int getRowCount() { return m_rows.getRowCount(); } /** * Get the minimum row index currently in use by this Table. * @return the minimum row index */ public int getMinimumRow() { return m_rows.getMinimumRow(); } /** * Get the maximum row index currently in use by this Table. * @return the maximum row index */ public int getMaximumRow() { return m_rows.getMaximumRow(); } /** * Indicates if the value of the given table cell can be changed. * @param row the row number * @param col the column number * @return true if the value can be edited/changed, false otherwise */ public boolean isCellEditable(int row, int col) { if ( !m_rows.isValidRow(row) ) { return false; } else { return getColumn(col).isCellEditable(row); } } /** * Get the number of times this Table has been modified. Adding rows, * deleting rows, and updating table cell values all contribute to * this count. * @return the number of modifications to this table */ public int getModificationCount() { return m_modCount; } /** * Sets the TupleManager used by this Table. Use this method * carefully, as it will cause all existing Tuples retrieved * from this Table to be invalidated. * @param tm the TupleManager to use */ public void setTupleManager(TupleManager tm) { m_tuples.invalidateAll(); m_tuples = tm; } /** * Returns this Table's schema. The returned schema will be * locked, which means that any attempts to edit the returned schema * by adding additional columns will result in a runtime exception. * * If this Table subsequently has columns added or removed, this will not * be reflected in the returned schema. Instead, this method will need to * be called again to get a current schema. Accordingly, it is not * recommended that Schema instances returned by this method be stored * or reused across scopes unless that exact schema snapshot is * desired. * * @return a copy of this Table's schema */ public Schema getSchema() { if ( m_schema == null ) { Schema s = new Schema(); for ( int i=0; iset
method for the given data field can
* accept values of a given target type.
* @param field the data field to check
* @param type a Class instance to check for compatibility with the
* data field values.
* @return true if the data field is compatible with provided type,
* false otherwise. If the value is true, objects of the given type
* can be used as parameters of the {@link #set(int, String, Object)}
* method.
* @see #set(int, String, Object)
*/
public boolean canSet(String field, Class type) {
Column c = getColumn(field);
return ( c==null ? false : c.canSet(type) );
}
/**
* Get the data value at the given row and field as an Object.
* @param row the table row to get
* @param field the data field to retrieve
* @return the data value as an Object. The concrete type of this
* Object is dependent on the underlying data column used.
* @see #canGet(String, Class)
* @see #getColumnType(String)
*/
public Object get(int row, String field) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
return getColumn(col).get(row);
}
/**
* Set the value of a given row and data field.
* @param row the table row to set
* @param field the data field to set
* @param val the value for the field. If the concrete type of this
* Object is not compatible with the underlying data model, an
* Exception will be thrown. Use the {@link #canSet(String, Class)}
* method to check the type-safety ahead of time.
* @see #canSet(String, Class)
* @see #getColumnType(String)
*/
public void set(int row, String field, Object val) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
getColumn(col).set(val, row);
// we don't fire a notification here, as we catch the
// notification from the column itself and then dispatch
}
/**
* Get the data value at the given row and column numbers as an Object.
* @param row the row number
* @param col the column number
* @return the data value as an Object. The concrete type of this
* Object is dependent on the underlying data column used.
* @see #canGet(String, Class)
* @see #getColumnType(int)
*/
public Object getValueAt(int row, int col) {
row = getColumnRow(row, col);
return getColumn(col).get(row);
}
/**
* Set the value of at the given row and column numbers.
* @param row the row number
* @param col the column number
* @param val the value for the field. If the concrete type of this
* Object is not compatible with the underlying data model, an
* Exception will be thrown. Use the {@link #canSet(String, Class)}
* method to check the type-safety ahead of time.
* @see #canSet(String, Class)
* @see #getColumnType(String)
*/
public void setValueAt(int row, int col, Object val) {
row = getColumnRow(row, col);
getColumn(col).set(val, row);
// we don't fire a notification here, as we catch the
// notification from the column itself and then dispatch
}
/**
* Get the default value for the given data field.
* @param field the data field
* @return the default value, as an Object, used to populate rows
* of the data field.
*/
public Object getDefault(String field) {
int col = getColumnNumber(field);
return getColumn(col).getDefaultValue();
}
/**
* Revert this tuple's value for the given field to the default value
* for the field.
* @param field the data field
* @see #getDefault(String)
*/
public void revertToDefault(int row, String field) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
getColumn(col).revertToDefault(row);
}
// ------------------------------------------------------------------------
// Convenience Data Access Methods
/**
* Check if the given data field can return primitive int
* values.
* @param field the data field to check
* @return true if the data field can return primitive int
* values, false otherwise. If true, the {@link #getInt(int, String)}
* method can be used safely.
*/
public final boolean canGetInt(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canGetInt() );
}
/**
* Check if the setInt
method can safely be used for the
* given data field.
* @param field the data field to check
* @return true if the {@link #setInt(int, String, int)} method can safely
* be used for the given field, false otherwise.
*/
public final boolean canSetInt(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canSetInt() );
}
/**
* Get the data value at the given row and field as an
* int
.
* @param row the table row to retrieve
* @param field the data field to retrieve
* @see #canGetInt(String)
*/
public final int getInt(int row, String field) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
return getColumn(col).getInt(row);
}
/**
* Set the data value of the given row and field as an
* int
.
* @param row the table row to set
* @param field the data field to set
* @param val the value to set
* @see #canSetInt(String)
*/
public final void setInt(int row, String field, int val) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
getColumn(col).setInt(val, row);
}
// --------------------------------------------------------------
/**
* Check if the given data field can return primitive long
* values.
* @param field the data field to check
* @return true if the data field can return primitive long
* values, false otherwise. If true, the {@link #getLong(int, String)}
* method can be used safely.
*/
public final boolean canGetLong(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canGetLong() );
}
/**
* Check if the setLong
method can safely be used for the
* given data field.
* @param field the data field to check
* @return true if the {@link #setLong(int, String, long)} method can
* safely be used for the given field, false otherwise.
*/
public final boolean canSetLong(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canSetLong() );
}
/**
* Get the data value at the given row and field as a
* long
.
* @param row the table row to retrieve
* @param field the data field to retrieve
* @see #canGetLong(String)
*/
public final long getLong(int row, String field) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
return getColumn(col).getLong(row);
}
/**
* Set the data value of the given row and field as a
* long
.
* @param row the table row to set
* @param field the data field to set
* @param val the value to set
* @see #canSetLong(String)
*/
public final void setLong(int row, String field, long val) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
getColumn(col).setLong(val, row);
}
// --------------------------------------------------------------
/**
* Check if the given data field can return primitive float
* values.
* @param field the data field to check
* @return true if the data field can return primitive float
* values, false otherwise. If true, the {@link #getFloat(int, String)}
* method can be used safely.
*/
public final boolean canGetFloat(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canGetFloat() );
}
/**
* Check if the setFloat
method can safely be used for the
* given data field.
* @param field the data field to check
* @return true if the {@link #setFloat(int, String, float)} method can
* safely be used for the given field, false otherwise.
*/
public final boolean canSetFloat(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canSetFloat() );
}
/**
* Get the data value at the given row and field as a
* float
.
* @param row the table row to retrieve
* @param field the data field to retrieve
* @see #canGetFloat(String)
*/
public final float getFloat(int row, String field) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
return getColumn(col).getFloat(row);
}
/**
* Set the data value of the given row and field as a
* float
.
* @param row the table row to set
* @param field the data field to set
* @param val the value to set
* @see #canSetFloat(String)
*/
public final void setFloat(int row, String field, float val) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
getColumn(col).setFloat(val, row);
}
// --------------------------------------------------------------
/**
* Check if the given data field can return primitive double
* values.
* @param field the data field to check
* @return true if the data field can return primitive double
* values, false otherwise. If true, the {@link #getDouble(int, String)}
* method can be used safely.
*/
public final boolean canGetDouble(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canGetDouble() );
}
/**
* Check if the setDouble
method can safely be used for the
* given data field.
* @param field the data field to check
* @return true if the {@link #setDouble(int, String, double)} method can
* safely be used for the given field, false otherwise.
*/
public final boolean canSetDouble(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canSetDouble() );
}
/**
* Get the data value at the given row and field as a
* double
.
* @param row the table row to retrieve
* @param field the data field to retrieve
* @see #canGetDouble(String)
*/
public final double getDouble(int row, String field) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
return getColumn(col).getDouble(row);
}
/**
* Set the data value of the given row and field as a
* double
.
* @param row the table row to set
* @param field the data field to set
* @param val the value to set
* @see #canSetDouble(String)
*/
public final void setDouble(int row, String field, double val) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
getColumn(col).setDouble(val, row);
}
// --------------------------------------------------------------
/**
* Check if the given data field can return primitive boolean
* values.
* @param field the data field to check
* @return true if the data field can return primitive boolean
* values, false otherwise. If true, the {@link #getBoolean(int, String)}
* method can be used safely.
*/
public final boolean canGetBoolean(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canGetBoolean() );
}
/**
* Check if the setBoolean
method can safely be used for the
* given data field.
* @param field the data field to check
* @return true if the {@link #setBoolean(int, String, boolean)} method can
* safely be used for the given field, false otherwise.
*/
public final boolean canSetBoolean(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canSetBoolean() );
}
/**
* Get the data value at the given row and field as a
* boolean
.
* @param row the table row to retrieve
* @param field the data field to retrieve
* @see #canGetBoolean(String)
*/
public final boolean getBoolean(int row, String field) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
return getColumn(col).getBoolean(row);
}
/**
* Set the data value of the given row and field as a
* boolean
.
* @param row the table row to set
* @param field the data field to set
* @param val the value to set
* @see #canSetBoolean(String)
*/
public final void setBoolean(int row, String field, boolean val) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
getColumn(col).setBoolean(val, row);
}
// --------------------------------------------------------------
/**
* Check if the given data field can return primitive String
* values.
* @param field the data field to check
* @return true if the data field can return primitive String
* values, false otherwise. If true, the {@link #getString(int, String)}
* method can be used safely.
*/
public final boolean canGetString(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canGetString() );
}
/**
* Check if the setString
method can safely be used for the
* given data field.
* @param field the data field to check
* @return true if the {@link #setString(int, String, String)} method can
* safely be used for the given field, false otherwise.
*/
public final boolean canSetString(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canSetString() );
}
/**
* Get the data value at the given row and field as a
* String
.
* @param row the table row to retrieve
* @param field the data field to retrieve
* @see #canGetString(String)
*/
public final String getString(int row, String field) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
return getColumn(col).getString(row);
}
/**
* Set the data value of the given row and field as a
* String
.
* @param row the table row to set
* @param field the data field to set
* @param val the value to set
* @see #canSetString(String)
*/
public final void setString(int row, String field, String val) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
getColumn(col).setString(val, row);
}
// --------------------------------------------------------------
/**
* Check if the given data field can return primitive Date
* values.
* @param field the data field to check
* @return true if the data field can return primitive Date
* values, false otherwise. If true, the {@link #getDate(int, String)}
* method can be used safely.
*/
public final boolean canGetDate(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canGetDate() );
}
/**
* Check if the setDate
method can safely be used for the
* given data field.
* @param field the data field to check
* @return true if the {@link #setDate(int, String, Date)} method can
* safely be used for the given field, false otherwise.
*/
public final boolean canSetDate(String field) {
Column col = getColumn(field);
return ( col==null ? false : col.canSetDate() );
}
/**
* Get the data value at the given row and field as a
* Date
.
* @param row the table row to retrieve
* @param field the data field to retrieve
* @see #canGetDate(String)
*/
public final Date getDate(int row, String field) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
return getColumn(col).getDate(row);
}
/**
* Set the data value of the given row and field as a
* Date
.
* @param row the table row to set
* @param field the data field to set
* @param val the value to set
* @see #canSetDate(String)
*/
public final void setDate(int row, String field, Date val) {
int col = getColumnNumber(field);
row = getColumnRow(row, col);
getColumn(col).setDate(val, row);
}
// ------------------------------------------------------------------------
// Iterators
/**
* Return a TableIterator over the rows of this table.
* @return a TableIterator over this table
*/
public TableIterator iterator() {
return iterator(rows());
}
/**
* Return a TableIterator over the given rows of this table.
* @param rows an iterator over the table rows to visit
* @return a TableIterator over this table
*/
public TableIterator iterator(IntIterator rows) {
return new TableIterator(this, rows);
}
/**
* Get an iterator over the tuples in this table.
* @return an iterator over the table tuples
* @see prefuse.data.tuple.TupleSet#tuples()
*/
public Iterator tuples() {
return m_tuples.iterator(rows());
}
/**
* Get an iterator over the tuples in this table in reverse order.
* @return an iterator over the table tuples in reverse order
*/
public Iterator tuplesReversed() {
return m_tuples.iterator(rows(true));
}
/**
* Get an iterator over the tuples for the given rows in this table.
* @param rows an iterator over the table rows to visit
* @return an iterator over the selected table tuples
*/
public Iterator tuples(IntIterator rows) {
return m_tuples.iterator(rows);
}
/**
* Get an interator over the row numbers of this table.
* @return an iterator over the rows of this table
*/
public IntIterator rows() {
return m_rows.rows();
}
/**
* Get a filtered iterator over the row numbers of this table, returning
* only the rows whose tuples match the given filter predicate.
* @param filter the filter predicate to apply
* @return a filtered iterator over the rows of this table
*/
public IntIterator rows(Predicate filter) {
return FilterIteratorFactory.rows(this, filter);
}
/**
* Get an interator over the row numbers of this table.
* @param reverse true to iterate in rever order, false for normal order
* @return an iterator over the rows of this table
*/
public IntIterator rows(boolean reverse) {
return m_rows.rows(reverse);
}
/**
* Get an iterator over the rows of this table, sorted by the given data
* field. This method will create an index over the field if one does
* not yet exist.
* @param field the data field to sort by
* @param ascend true if the iteration should proceed in an ascending
* (lowest to highest) sort order, false for a descending order
* @return the sorted iterator over rows of this table
*/
public IntIterator rowsSortedBy(String field, boolean ascend) {
Class type = getColumnType(field);
Index index = getIndex(field, type, true);
int t = ascend ? Index.TYPE_ASCENDING : Index.TYPE_DESCENDING;
return index.allRows(t);
}
/**
* Return an iterator over a range of rwos in this table, determined
* by a bounded range for a given data field. A new index over the
* data field will be created if it doesn't already exist.
* @param field the data field for determining the bounded range
* @param lo the minimum range value
* @param hi the maximum range value
* @param indexType indicate the sort order and inclusivity/exclusivity
* of the range bounds, using the constants of the
* {@link prefuse.data.util.Index} class.
* @return an iterator over a range of table rows, determined by a
* sorted bounded range of a data field
*/
public IntIterator rangeSortedBy(String field, int lo, int hi, int indexType) {
Index index = getIndex(field, int.class, true);
return index.rows(lo, hi, indexType);
}
/**
* Return an iterator over a range of rwos in this table, determined
* by a bounded range for a given data field. A new index over the
* data field will be created if it doesn't already exist.
* @param field the data field for determining the bounded range
* @param lo the minimum range value
* @param hi the maximum range value
* @param indexType indicate the sort order and inclusivity/exclusivity
* of the range bounds, using the constants of the
* {@link prefuse.data.util.Index} class.
* @return an iterator over a range of table rows, determined by a
* sorted bounded range of a data field
*/
public IntIterator rangeSortedBy(String field, long lo, long hi, int indexType) {
Index index = getIndex(field, long.class, true);
return index.rows(lo, hi, indexType);
}
/**
* Return an iterator over a range of rwos in this table, determined
* by a bounded range for a given data field. A new index over the
* data field will be created if it doesn't already exist.
* @param field the data field for determining the bounded range
* @param lo the minimum range value
* @param hi the maximum range value
* @param indexType indicate the sort order and inclusivity/exclusivity
* of the range bounds, using the constants of the
* {@link prefuse.data.util.Index} class.
* @return an iterator over a range of table rows, determined by a
* sorted bounded range of a data field
*/
public IntIterator rangeSortedBy(String field, float lo, float hi, int indexType) {
Index index = getIndex(field, float.class, true);
return index.rows(lo, hi, indexType);
}
/**
* Return an iterator over a range of rwos in this table, determined
* by a bounded range for a given data field. A new index over the
* data field will be created if it doesn't already exist.
* @param field the data field for determining the bounded range
* @param lo the minimum range value
* @param hi the maximum range value
* @param indexType indicate the sort order and inclusivity/exclusivity
* of the range bounds, using the constants of the
* {@link prefuse.data.util.Index} class.
* @return an iterator over a range of table rows, determined by a
* sorted bounded range of a data field
*/
public IntIterator rangeSortedBy(String field, double lo, double hi, int indexType) {
Index index = getIndex(field, double.class, true);
return index.rows(lo, hi, indexType);
}
/**
* Return an iterator over a range of rwos in this table, determined
* by a bounded range for a given data field. A new index over the
* data field will be created if it doesn't already exist.
* @param field the data field for determining the bounded range
* @param lo the minimum range value
* @param hi the maximum range value
* @param indexType indicate the sort order and inclusivity/exclusivity
* of the range bounds, using the constants of the
* {@link prefuse.data.util.Index} class.
* @return an iterator over a range of table rows, determined by a
* sorted bounded range of a data field
*/
public IntIterator rangeSortedBy(String field, Object lo, Object hi, int indexType) {
Class type = TypeLib.getSharedType(lo, hi);
// TODO: check this for correctness
if ( type == null )
throw new IllegalArgumentException("Incompatible arguments");
Index index = getIndex(field, type, true);
return index.rows(lo, hi, indexType);
}
// ------------------------------------------------------------------------
// Listener Methods
// -- ColumnListeners -----------------------------------------------------
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, boolean)
*/
public void columnChanged(Column src, int idx, boolean prev) {
handleColumnChanged(src, idx, idx);
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, double)
*/
public void columnChanged(Column src, int idx, double prev) {
handleColumnChanged(src, idx, idx);
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, float)
*/
public void columnChanged(Column src, int idx, float prev) {
handleColumnChanged(src, idx, idx);
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, int)
*/
public void columnChanged(Column src, int idx, int prev) {
handleColumnChanged(src, idx, idx);
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, long)
*/
public void columnChanged(Column src, int idx, long prev) {
handleColumnChanged(src, idx, idx);
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, java.lang.Object)
*/
public void columnChanged(Column src, int idx, Object prev) {
handleColumnChanged(src, idx, idx);
}
/**
* @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, int, int)
*/
public void columnChanged(Column src, int type, int start, int end) {
handleColumnChanged(src, start, end);
}
/**
* Handle a column change event.
* @param c the modified column
* @param start the starting row of the modified range
* @param end the ending row (inclusive) of the modified range
*/
protected void handleColumnChanged(Column c, int start, int end) {
for ( ; !isValidRow(start) && start <= end; ++start );
if ( start > end ) return; // bail if no valid rows
// determine the index of the updated column
int idx;
if ( m_lastCol != -1 && c == getColumn(m_lastCol) ) {
// constant time
idx = m_lastCol;
} else {
// linear time
idx = getColumnNumber(c);
}
// if we have a valid index, fire a notification
if ( idx >= 0 ) {
fireTableEvent(start, end, idx, TableModelEvent.UPDATE);
}
}
// -- TableListeners ------------------------------------------------------
/**
* Add a table listener to this table.
* @param listnr the listener to add
*/
public void addTableListener(TableListener listnr) {
if ( !m_listeners.contains(listnr) )
m_listeners.add(listnr);
}
/**
* Remove a table listener from this table.
* @param listnr the listener to remove
*/
public void removeTableListener(TableListener listnr) {
m_listeners.remove(listnr);
}
/**
* Fire a table event to notify listeners.
* @param row0 the starting row of the modified range
* @param row1 the ending row (inclusive) of the modified range
* @param col the number of the column modified, or
* {@link prefuse.data.event.EventConstants#ALL_COLUMNS} for operations
* effecting all columns.
* @param type the table modification type, one of
* {@link prefuse.data.event.EventConstants#INSERT},
* {@link prefuse.data.event.EventConstants#DELETE}, or
* {@link prefuse.data.event.EventConstants#UPDATE}.
*/
protected void fireTableEvent(int row0, int row1, int col, int type) {
// increment the modification count
++m_modCount;
if ( type != EventConstants.UPDATE &&
col == EventConstants.ALL_COLUMNS )
{
// fire event to all tuple set listeners
fireTupleEvent(this, row0, row1, type);
}
if ( !m_listeners.isEmpty() ) {
// fire event to all table listeners
Object[] lstnrs = m_listeners.getArray();
for ( int i=0; i