📄 table.java
字号:
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.Sort;
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;
/**
* <p>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.</p>
*
* <p>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.</p>
*
* <p>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.</p>
*
* <p>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.</p>
*
* <p>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.</p>
*
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
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; i<getColumnCount(); ++i ) {
s.addColumn(getColumnName(i), getColumnType(i),
getColumn(i).getDefaultValue());
}
s.lockSchema();
m_schema = s;
}
return m_schema;
}
/**
* Invalidates this table's cached schema. This method should be called
* whenever columns are added or removed from this table.
*/
protected void invalidateSchema() {
m_schema = null;
}
// ------------------------------------------------------------------------
// Row Operations
/**
* Get the row value for accessing an underlying Column instance,
* corresponding to the given table cell. For basic tables this just
* returns the input row value. However, for tables that inherit
* data columns from a parent table and present a filtered view on
* this data, a mapping between the row numbers of the table and
* the row numbers of the backing data column is needed. In those cases,
* this method returns the result of that mapping. The method
* {@link #getTableRow(int, int)} accesses this map in the reverse
* direction.
* @param row the table row to lookup
* @param col the table column to lookup
* @return the column row number for accessing the desired table cell
*/
public int getColumnRow(int row, int col) {
return m_rows.getColumnRow(row, col);
}
/**
* Get the row number for this table given a row number for a backing
* data column and the column number for the data column. For basic
* tables this just returns the column row value. However, for tables that
* inherit data columns from a parent table and present a filtered view on
* this data, a mapping between the row numbers of the table and
* the row numbers of the backing data column is needed. In those cases,
* this method returns the result of this mapping, in the direction of
* the backing column rows to the table rows of the cascaded table. The
* method {@link #getColumnRow(int, int)} accesses this map in the reverse
* direction.
* @param colrow the row of the backing data column
* @param col the table column to lookup.
* @return the table row number for accessing the desired table cell
*/
public int getTableRow(int colrow, int col) {
return m_rows.getTableRow(colrow, col);
}
/**
* Add a row to this table. All data columns will be notified and will
* take on the appropriate default values for the added row.
* @return the row number of the newly added row
*/
public int addRow() {
int r = m_rows.addRow();
updateRowCount();
fireTableEvent(r, r, TableModelEvent.ALL_COLUMNS,
TableModelEvent.INSERT);
return r;
}
/**
* Add a given number of rows to this table. All data columns will be
* notified and will take on the appropriate default values for the
* added rows.
* @param nrows the number of rows to add.
*/
public void addRows(int nrows) {
for ( int i=0; i<nrows; ++i ) {
addRow();
}
}
/**
* Internal method that updates the row counts for local data columns.
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -