0001: package prefuse.data;
0002:
0003: import java.util.ArrayList;
0004: import java.util.Date;
0005: import java.util.HashMap;
0006: import java.util.Iterator;
0007:
0008: import javax.swing.event.TableModelEvent;
0009:
0010: import prefuse.data.column.Column;
0011: import prefuse.data.column.ColumnFactory;
0012: import prefuse.data.column.ColumnMetadata;
0013: import prefuse.data.event.ColumnListener;
0014: import prefuse.data.event.EventConstants;
0015: import prefuse.data.event.TableListener;
0016: import prefuse.data.expression.Expression;
0017: import prefuse.data.expression.Predicate;
0018: import prefuse.data.expression.parser.ExpressionParser;
0019: import prefuse.data.tuple.AbstractTupleSet;
0020: import prefuse.data.tuple.TableTuple;
0021: import prefuse.data.tuple.TupleManager;
0022: import prefuse.data.util.FilterIteratorFactory;
0023: import prefuse.data.util.Index;
0024: import prefuse.data.util.RowManager;
0025: import prefuse.data.util.Sort;
0026: import prefuse.data.util.TableIterator;
0027: import prefuse.data.util.TreeIndex;
0028: import prefuse.util.TypeLib;
0029: import prefuse.util.collections.CopyOnWriteArrayList;
0030: import prefuse.util.collections.IncompatibleComparatorException;
0031: import prefuse.util.collections.IntIterator;
0032:
0033: /**
0034: * <p>A Table organizes a collection of data into rows and columns, each row
0035: * containing a data record, and each column containing data values for a
0036: * named data field with a specific data type. Table data can be accessed
0037: * directly using the row number and column name, or rows can be treated
0038: * in an object-oriented fashion using {@link prefuse.data.Tuple}
0039: * instances that represent a single row of data in the table. As such,
0040: * tables implement the {@link prefuse.data.tuple.TupleSet} interface.</p>
0041: *
0042: * <p>Table rows can be inserted or deleted. In any case, none of the other
0043: * existing table rows are effected by an insertion or deletion. A deleted
0044: * row simply becomes invalid--any subsequent attempts to access the row
0045: * either directly or through a pre-existing Tuple instance will result
0046: * in an exception. However, if news rows are later added to the table,
0047: * the row number for previously deleted nodes can be reused. In fact, the
0048: * lower row number currently unused is assigned to the new row. This results
0049: * in an efficient reuse of the table rows, but carries an important side
0050: * effect -- rows do not necesarily maintain the order in which they were
0051: * added once deletions have occurred on the table. If not deletions
0052: * occur, the ordering of table rows will reflect the order in which
0053: * rows were added to the table.</p>
0054: *
0055: * <p>Collections of table rows can be accessed using both iterators over
0056: * the actual row numbers and iterators over the Tuple instances that
0057: * encapsulate access to that row. Both types of iteration can also be
0058: * filtered by providing a {@link prefuse.data.expression.Predicate},
0059: * allowing tables to be queried for specific values.</p>
0060: *
0061: * <p>Columns (alternativele referred to as data fields) can be added to
0062: * the Table using {@link #addColumn(String, Class)} and a host of
0063: * similar methods. This method will automatically determine the right
0064: * kind of backing column instance to use. Furthermore, Table columns
0065: * can be specified using a {@link Schema} instance, which describes
0066: * the column names, data types, and default values. The Table class
0067: * also maintains its own internal Schema, which be accessed (in a
0068: * read-only way) using the {@link #getSchema()} method.</p>
0069: *
0070: * <p>Tables also support additional structures. The {@link ColumnMetadata}
0071: * class returned by the {@link #getMetadata(String)} method supports
0072: * calculation of different statistics for a column, including minimum
0073: * and maximum values, and the number of unique data values in the column.
0074: * {@link prefuse.data.util.Index} instances can be created and retrieved
0075: * using the {@link #index(String)} method and retrieved without triggering
0076: * creation using {@link #getIndex(String)} method. An index keeps a
0077: * sorted collection of all data values in a column, accelerating the creation
0078: * of filtered iterators by optimizing query calculations and also providing
0079: * faster computation of many of the {@link ColumnMetadata} methods. If
0080: * you will be issuing a number of queries (i.e., requesting filtered
0081: * iterators) dependent on the values of a given column, indexing that column
0082: * may result in a significant performance increase, though at the cost
0083: * of storing and maintaining the backing index structure.</p>
0084: *
0085: * @author <a href="http://jheer.org">jeffrey heer</a>
0086: */
0087: public class Table extends AbstractTupleSet implements ColumnListener {
0088:
0089: /** Listeners for changes to this table */
0090: protected CopyOnWriteArrayList m_listeners;
0091:
0092: /** Locally stored data columns */
0093: protected ArrayList m_columns;
0094: /** Column names for locally store data columns */
0095: protected ArrayList m_names;
0096:
0097: /** Mapping between column names and column entries
0098: * containing column, metadata, and index references */
0099: protected HashMap m_entries;
0100:
0101: /** Manager for valid row indices */
0102: protected RowManager m_rows;
0103:
0104: /** manager for tuples, which are object representations for rows */
0105: protected TupleManager m_tuples;
0106:
0107: /** Tracks the number of edits of this table */
0108: protected int m_modCount = 0;
0109:
0110: /** Memoize the index of the last column operated on,
0111: * used to expedite handling of column updates. */
0112: protected int m_lastCol = -1;
0113:
0114: /** A cached schema instance, loaded lazily */
0115: protected Schema m_schema;
0116:
0117: // ------------------------------------------------------------------------
0118: // Constructors
0119:
0120: /**
0121: * Create a new, empty Table. Rows can be added to the table using
0122: * the {@link #addRow()} method.
0123: */
0124: public Table() {
0125: this (0, 0);
0126: }
0127:
0128: /**
0129: * Create a new Table with a given number of rows, and the starting
0130: * capacity for a given number of columns.
0131: * @param nrows the starting number of table rows
0132: * @param ncols the starting capacity for columns
0133: */
0134: public Table(int nrows, int ncols) {
0135: this (nrows, ncols, TableTuple.class);
0136: }
0137:
0138: /**
0139: * Create a new Table.
0140: * @param nrows the starting number of table rows
0141: * @param ncols the starting capacity for columns
0142: * @param tupleType the class of the Tuple instances to use
0143: */
0144: protected Table(int nrows, int ncols, Class tupleType) {
0145: m_listeners = new CopyOnWriteArrayList();
0146: m_columns = new ArrayList(ncols);
0147: m_names = new ArrayList(ncols);
0148: m_rows = new RowManager(this );
0149: m_entries = new HashMap(ncols + 5);
0150: m_tuples = new TupleManager(this , null, tupleType);
0151:
0152: if (nrows > 0)
0153: addRows(nrows);
0154: }
0155:
0156: // ------------------------------------------------------------------------
0157: // Table Metadata
0158:
0159: /**
0160: * Get the number of columns / data fields in this table.
0161: * @return the number of columns
0162: */
0163: public int getColumnCount() {
0164: return m_columns.size();
0165: }
0166:
0167: /**
0168: * Get the data type of the column at the given column index.
0169: * @param col the column index
0170: * @return the data type (as a Java Class) of the column
0171: */
0172: public Class getColumnType(int col) {
0173: return getColumn(col).getColumnType();
0174: }
0175:
0176: /**
0177: * Get the data type of the column with the given data field name.
0178: * @param field the column / data field name
0179: * @return the data type (as a Java Class) of the column
0180: */
0181: public Class getColumnType(String field) {
0182: Column c = getColumn(field);
0183: return (c == null ? null : c.getColumnType());
0184: }
0185:
0186: /**
0187: * Get the number of rows in the table.
0188: * @return the number of rows
0189: */
0190: public int getRowCount() {
0191: return m_rows.getRowCount();
0192: }
0193:
0194: /**
0195: * Get the minimum row index currently in use by this Table.
0196: * @return the minimum row index
0197: */
0198: public int getMinimumRow() {
0199: return m_rows.getMinimumRow();
0200: }
0201:
0202: /**
0203: * Get the maximum row index currently in use by this Table.
0204: * @return the maximum row index
0205: */
0206: public int getMaximumRow() {
0207: return m_rows.getMaximumRow();
0208: }
0209:
0210: /**
0211: * Indicates if the value of the given table cell can be changed.
0212: * @param row the row number
0213: * @param col the column number
0214: * @return true if the value can be edited/changed, false otherwise
0215: */
0216: public boolean isCellEditable(int row, int col) {
0217: if (!m_rows.isValidRow(row)) {
0218: return false;
0219: } else {
0220: return getColumn(col).isCellEditable(row);
0221: }
0222: }
0223:
0224: /**
0225: * Get the number of times this Table has been modified. Adding rows,
0226: * deleting rows, and updating table cell values all contribute to
0227: * this count.
0228: * @return the number of modifications to this table
0229: */
0230: public int getModificationCount() {
0231: return m_modCount;
0232: }
0233:
0234: /**
0235: * Sets the TupleManager used by this Table. Use this method
0236: * carefully, as it will cause all existing Tuples retrieved
0237: * from this Table to be invalidated.
0238: * @param tm the TupleManager to use
0239: */
0240: public void setTupleManager(TupleManager tm) {
0241: m_tuples.invalidateAll();
0242: m_tuples = tm;
0243: }
0244:
0245: /**
0246: * Returns this Table's schema. The returned schema will be
0247: * locked, which means that any attempts to edit the returned schema
0248: * by adding additional columns will result in a runtime exception.
0249: *
0250: * If this Table subsequently has columns added or removed, this will not
0251: * be reflected in the returned schema. Instead, this method will need to
0252: * be called again to get a current schema. Accordingly, it is not
0253: * recommended that Schema instances returned by this method be stored
0254: * or reused across scopes unless that exact schema snapshot is
0255: * desired.
0256: *
0257: * @return a copy of this Table's schema
0258: */
0259: public Schema getSchema() {
0260: if (m_schema == null) {
0261: Schema s = new Schema();
0262: for (int i = 0; i < getColumnCount(); ++i) {
0263: s.addColumn(getColumnName(i), getColumnType(i),
0264: getColumn(i).getDefaultValue());
0265: }
0266: s.lockSchema();
0267: m_schema = s;
0268: }
0269: return m_schema;
0270: }
0271:
0272: /**
0273: * Invalidates this table's cached schema. This method should be called
0274: * whenever columns are added or removed from this table.
0275: */
0276: protected void invalidateSchema() {
0277: m_schema = null;
0278: }
0279:
0280: // ------------------------------------------------------------------------
0281: // Row Operations
0282:
0283: /**
0284: * Get the row value for accessing an underlying Column instance,
0285: * corresponding to the given table cell. For basic tables this just
0286: * returns the input row value. However, for tables that inherit
0287: * data columns from a parent table and present a filtered view on
0288: * this data, a mapping between the row numbers of the table and
0289: * the row numbers of the backing data column is needed. In those cases,
0290: * this method returns the result of that mapping. The method
0291: * {@link #getTableRow(int, int)} accesses this map in the reverse
0292: * direction.
0293: * @param row the table row to lookup
0294: * @param col the table column to lookup
0295: * @return the column row number for accessing the desired table cell
0296: */
0297: public int getColumnRow(int row, int col) {
0298: return m_rows.getColumnRow(row, col);
0299: }
0300:
0301: /**
0302: * Get the row number for this table given a row number for a backing
0303: * data column and the column number for the data column. For basic
0304: * tables this just returns the column row value. However, for tables that
0305: * inherit data columns from a parent table and present a filtered view on
0306: * this data, a mapping between the row numbers of the table and
0307: * the row numbers of the backing data column is needed. In those cases,
0308: * this method returns the result of this mapping, in the direction of
0309: * the backing column rows to the table rows of the cascaded table. The
0310: * method {@link #getColumnRow(int, int)} accesses this map in the reverse
0311: * direction.
0312: * @param colrow the row of the backing data column
0313: * @param col the table column to lookup.
0314: * @return the table row number for accessing the desired table cell
0315: */
0316: public int getTableRow(int colrow, int col) {
0317: return m_rows.getTableRow(colrow, col);
0318: }
0319:
0320: /**
0321: * Add a row to this table. All data columns will be notified and will
0322: * take on the appropriate default values for the added row.
0323: * @return the row number of the newly added row
0324: */
0325: public int addRow() {
0326: int r = m_rows.addRow();
0327: updateRowCount();
0328:
0329: fireTableEvent(r, r, TableModelEvent.ALL_COLUMNS,
0330: TableModelEvent.INSERT);
0331: return r;
0332: }
0333:
0334: /**
0335: * Add a given number of rows to this table. All data columns will be
0336: * notified and will take on the appropriate default values for the
0337: * added rows.
0338: * @param nrows the number of rows to add.
0339: */
0340: public void addRows(int nrows) {
0341: for (int i = 0; i < nrows; ++i) {
0342: addRow();
0343: }
0344: }
0345:
0346: /**
0347: * Internal method that updates the row counts for local data columns.
0348: */
0349: protected void updateRowCount() {
0350: int maxrow = m_rows.getMaximumRow() + 1;
0351:
0352: // update columns
0353: Iterator cols = getColumns();
0354: while (cols.hasNext()) {
0355: Column c = (Column) cols.next();
0356: c.setMaximumRow(maxrow);
0357: }
0358: }
0359:
0360: /**
0361: * Removes a row from this table.
0362: * @param row the row to delete
0363: * @return true if the row was successfully deleted, false if the
0364: * row was already invalid
0365: */
0366: public boolean removeRow(int row) {
0367: if (m_rows.isValidRow(row)) {
0368: // the order of operations here is extremely important
0369: // otherwise listeners may end up with corrupted state.
0370: // fire update *BEFORE* clearing values
0371: // allow listeners (e.g., indices) to perform clean-up
0372: fireTableEvent(row, row, TableModelEvent.ALL_COLUMNS,
0373: TableModelEvent.DELETE);
0374: // invalidate the tuple
0375: m_tuples.invalidate(row);
0376: // release row with row manager
0377: // do this before clearing column values, so that any
0378: // listeners can determine that the row is invalid
0379: m_rows.releaseRow(row);
0380: // now clear column values
0381: for (Iterator cols = getColumns(); cols.hasNext();) {
0382: Column c = (Column) cols.next();
0383: c.revertToDefault(row);
0384: }
0385: return true;
0386: }
0387: return false;
0388: }
0389:
0390: /**
0391: * Clear this table, removing all rows.
0392: * @see prefuse.data.tuple.TupleSet#clear()
0393: */
0394: public void clear() {
0395: IntIterator rows = rows(true);
0396: while (rows.hasNext()) {
0397: removeRow(rows.nextInt());
0398: }
0399: }
0400:
0401: /**
0402: * Indicates if the given row number corresponds to a valid table row.
0403: * @param row the row number to check for validity
0404: * @return true if the row is valid, false if it is not
0405: */
0406: public boolean isValidRow(int row) {
0407: return m_rows.isValidRow(row);
0408: }
0409:
0410: // ------------------------------------------------------------------------
0411: // Column Operations
0412:
0413: /**
0414: * Internal method indicating if the given data field is included as a
0415: * data column.
0416: */
0417: protected boolean hasColumn(String name) {
0418: return getColumnNumber(name) != -1;
0419: }
0420:
0421: /**
0422: * Get the data field name of the column at the given column number.
0423: * @param col the column number
0424: * @return the data field name of the column
0425: */
0426: public String getColumnName(int col) {
0427: return (String) m_names.get(col);
0428: }
0429:
0430: /**
0431: * Get the column number for a given data field name.
0432: * @param field the name of the column to lookup
0433: * @return the column number of the column, or -1 if the name is not found
0434: */
0435: public int getColumnNumber(String field) {
0436: ColumnEntry e = (ColumnEntry) m_entries.get(field);
0437: return (e == null ? -1 : e.colnum);
0438: }
0439:
0440: /**
0441: * Get the column number for the given Column instance.
0442: * @param col the Column instance to lookup
0443: * @return the column number of the column, or -1 if the name is not found
0444: */
0445: public int getColumnNumber(Column col) {
0446: return m_columns.indexOf(col);
0447: }
0448:
0449: /**
0450: * Get the column at the given column number.
0451: * @param col the column number
0452: * @return the Column instance
0453: */
0454: public Column getColumn(int col) {
0455: m_lastCol = col;
0456: return (Column) m_columns.get(col);
0457: }
0458:
0459: /**
0460: * Get the column with the given data field name
0461: * @param field the data field name of the column
0462: * @return the Column instance
0463: */
0464: public Column getColumn(String field) {
0465: ColumnEntry e = (ColumnEntry) m_entries.get(field);
0466: return (e != null ? e.column : null);
0467: }
0468:
0469: /**
0470: * Add a column with the given name and data type to this table.
0471: * @param name the data field name for the column
0472: * @param type the data type, as a Java Class, for the column
0473: * @see prefuse.data.tuple.TupleSet#addColumn(java.lang.String, java.lang.Class)
0474: */
0475: public void addColumn(String name, Class type) {
0476: addColumn(name, type, null);
0477: }
0478:
0479: /**
0480: * Add a column with the given name and data type to this table.
0481: * @param name the data field name for the column
0482: * @param type the data type, as a Java Class, for the column
0483: * @param defaultValue the default value for column data values
0484: * @see prefuse.data.tuple.TupleSet#addColumn(java.lang.String, java.lang.Class, java.lang.Object)
0485: */
0486: public void addColumn(String name, Class type, Object defaultValue) {
0487: Column col = ColumnFactory.getColumn(type, m_rows
0488: .getMaximumRow() + 1, defaultValue);
0489: addColumn(name, col);
0490: }
0491:
0492: /**
0493: * Add a derived column to this table, using an Expression instance to
0494: * dynamically calculate the column data values.
0495: * @param name the data field name for the column
0496: * @param expr a String expression in the prefuse expression language, to
0497: * be parsed into an {@link prefuse.data.expression.Expression} instance.
0498: * The string is parsed by the
0499: * {@link prefuse.data.expression.parser.ExpressionParser}. If an error
0500: * occurs during parsing, an exception will be thrown.
0501: * @see prefuse.data.tuple.TupleSet#addColumn(java.lang.String, java.lang.String)
0502: */
0503: public void addColumn(String name, String expr) {
0504: Expression ex = ExpressionParser.parse(expr);
0505: Throwable t = ExpressionParser.getError();
0506: if (t != null) {
0507: throw new RuntimeException(t);
0508: } else {
0509: addColumn(name, ex);
0510: }
0511: }
0512:
0513: /**
0514: * Add a derived column to this table, using an Expression instance to
0515: * dynamically calculate the column data values.
0516: * @param name the data field name for the column
0517: * @param expr the Expression that will determine the column values
0518: * @see prefuse.data.tuple.TupleSet#addColumn(java.lang.String, prefuse.data.expression.Expression)
0519: */
0520: public void addColumn(String name, Expression expr) {
0521: addColumn(name, ColumnFactory.getColumn(this , expr));
0522: }
0523:
0524: /**
0525: * Add a constant column to this table, which returns one constant value
0526: * for all column rows.
0527: * @param name the data field name for the column
0528: * @param type the data type, as a Java Class, for the column
0529: * @param dflt the default value for column data values
0530: */
0531: public void addConstantColumn(String name, Class type, Object dflt) {
0532: addColumn(name, ColumnFactory.getConstantColumn(type, dflt));
0533: }
0534:
0535: /**
0536: * Internal method for adding a column.
0537: * @param name the name of the column
0538: * @param col the actual Column instance
0539: */
0540: protected void addColumn(String name, Column col) {
0541: int idx = getColumnNumber(name);
0542: if (idx >= 0 && idx < m_columns.size()) {
0543: throw new IllegalArgumentException(
0544: "Table already has column with name \"" + name
0545: + "\"");
0546: }
0547:
0548: // add the column
0549: m_columns.add(col);
0550: m_names.add(name);
0551: m_lastCol = m_columns.size() - 1;
0552: ColumnEntry entry = new ColumnEntry(m_lastCol, col,
0553: new ColumnMetadata(this , name));
0554:
0555: // add entry, dispose of an overridden entry if needed
0556: ColumnEntry oldEntry = (ColumnEntry) m_entries.put(name, entry);
0557: if (oldEntry != null)
0558: oldEntry.dispose();
0559:
0560: invalidateSchema();
0561:
0562: // listen to what the column has to say
0563: col.addColumnListener(this );
0564:
0565: // fire notification
0566: fireTableEvent(m_rows.getMinimumRow(), m_rows.getMaximumRow(),
0567: m_lastCol, TableModelEvent.INSERT);
0568: }
0569:
0570: /**
0571: * Internal method for removing a column.
0572: * @param idx the column number of the column to remove
0573: * @return the removed Column instance
0574: */
0575: protected Column removeColumn(int idx) {
0576: // make sure index is legal
0577: if (idx < 0 || idx >= m_columns.size()) {
0578: throw new IllegalArgumentException(
0579: "Column index is not legal.");
0580: }
0581:
0582: String name = (String) m_names.get(idx);
0583: ((ColumnEntry) m_entries.get(name)).dispose();
0584: Column col = (Column) m_columns.remove(idx);
0585: m_entries.remove(name);
0586: m_names.remove(idx);
0587: renumberColumns();
0588:
0589: m_lastCol = -1;
0590: invalidateSchema();
0591:
0592: // ignore what the old column has to say
0593: col.removeColumnListener(this );
0594:
0595: // fire notification
0596: fireTableEvent(m_rows.getMinimumRow(), m_rows.getMaximumRow(),
0597: idx, TableModelEvent.DELETE);
0598:
0599: return col;
0600: }
0601:
0602: /**
0603: * Remove a data field from this table
0604: * @param field the name of the data field / column to remove
0605: * @return the removed Column instance
0606: */
0607: public Column removeColumn(String field) {
0608: int idx = m_names.indexOf(field);
0609: if (idx < 0) {
0610: throw new IllegalArgumentException("No such column.");
0611: }
0612: return removeColumn(idx);
0613: }
0614:
0615: /**
0616: * Remove a column from this table
0617: * @param c the column instance to remove
0618: */
0619: public void removeColumn(Column c) {
0620: int idx = m_columns.indexOf(c);
0621: if (idx < 0) {
0622: throw new IllegalArgumentException("No such column.");
0623: }
0624: removeColumn(idx);
0625: }
0626:
0627: /**
0628: * Internal method that re-numbers columns upon column removal.
0629: */
0630: protected void renumberColumns() {
0631: Iterator iter = m_names.iterator();
0632: for (int idx = 0; iter.hasNext(); ++idx) {
0633: String name = (String) iter.next();
0634: ColumnEntry e = (ColumnEntry) m_entries.get(name);
0635: e.colnum = idx;
0636: }
0637: }
0638:
0639: /**
0640: * Internal method that returns an iterator over columns
0641: * @return an iterator over columns
0642: */
0643: protected Iterator getColumns() {
0644: return m_columns.iterator();
0645: }
0646:
0647: /**
0648: * Internal method that returns an iterator over column names
0649: * @return an iterator over column name
0650: */
0651: protected Iterator getColumnNames() {
0652: return m_names.iterator();
0653: }
0654:
0655: // ------------------------------------------------------------------------
0656: // Column Metadata
0657:
0658: /**
0659: * Return a metadata instance providing summary information about a column.
0660: * @param field the data field name of the column
0661: * @return the columns' associated ColumnMetadata instance
0662: */
0663: public ColumnMetadata getMetadata(String field) {
0664: ColumnEntry e = (ColumnEntry) m_entries.get(field);
0665: if (e == null) {
0666: throw new IllegalArgumentException("Unknown column name: "
0667: + field);
0668: }
0669: return e.metadata;
0670: }
0671:
0672: // ------------------------------------------------------------------------
0673: // Index Methods
0674:
0675: /**
0676: * Create (if necessary) and return an index over the given data field.
0677: * The first call to this method with a given field name will cause the
0678: * index to be created and stored. Subsequent calls will simply return
0679: * the stored index. To attempt to retrieve an index without triggering
0680: * creation of a new index, use the {@link #getIndex(String)} method.
0681: * @param field the data field name of the column to index
0682: * @return the index over the specified data column
0683: */
0684: public Index index(String field) {
0685: ColumnEntry e = (ColumnEntry) m_entries.get(field);
0686: if (e == null) {
0687: throw new IllegalArgumentException("Unknown column name: "
0688: + field);
0689: } else if (e.index != null) {
0690: return e.index; // already indexed
0691: }
0692:
0693: Column col = e.column;
0694: try {
0695: e.index = new TreeIndex(this , m_rows, col, null);
0696: } catch (IncompatibleComparatorException ice) { /* can't happen */
0697: }
0698:
0699: return e.index;
0700: }
0701:
0702: /**
0703: * Retrieve, without creating, an index for the given data field.
0704: * @param field the data field name of the column
0705: * @return the stored index for the column, or null if no index has
0706: * been created
0707: */
0708: public Index getIndex(String field) {
0709: ColumnEntry e = (ColumnEntry) m_entries.get(field);
0710: if (e == null) {
0711: throw new IllegalArgumentException("Unknown column name: "
0712: + field);
0713: }
0714: return e.index;
0715: }
0716:
0717: /**
0718: * Internal method for index creation and retrieval.
0719: * @param field the data field name of the column
0720: * @param expType the expected data type of the index
0721: * @param create indicates whether or not a new index should be created
0722: * if none currently exists for the given data field
0723: * @return the Index for the given data field
0724: */
0725: protected Index getIndex(String field, Class expType, boolean create) {
0726: if (!expType.equals(getColumnType(field))) {
0727: // TODO: need more nuanced type checking here?
0728: throw new IllegalArgumentException(
0729: "Column type does not match.");
0730: }
0731: if (getIndex(field) == null && create) {
0732: index(field);
0733: }
0734: return getIndex(field);
0735: }
0736:
0737: /**
0738: * Remove the Index associated with the given data field / column name.
0739: * @param field the name of the column for which to remove the index
0740: * @return true if an index was successfully removed, false if no
0741: * such index was found
0742: */
0743: public boolean removeIndex(String field) {
0744: ColumnEntry e = (ColumnEntry) m_entries.get(field);
0745: if (e == null) {
0746: throw new IllegalArgumentException("Unknown column name: "
0747: + field);
0748: }
0749: if (e.index == null) {
0750: return false;
0751: } else {
0752: e.index.dispose();
0753: e.index = null;
0754: return true;
0755: }
0756: }
0757:
0758: // ------------------------------------------------------------------------
0759: // Tuple Methods
0760:
0761: /**
0762: * Get the Tuple instance providing object-oriented access to the given
0763: * table row.
0764: * @param row the table row
0765: * @return the Tuple for the given table row
0766: */
0767: public Tuple getTuple(int row) {
0768: return m_tuples.getTuple(row);
0769: }
0770:
0771: /**
0772: * Add a Tuple to this table. If the Tuple is already a member of this
0773: * table, nothing is done and null is returned. If the Tuple is not
0774: * a member of this Table but has a compatible data schema, as
0775: * determined by {@link Schema#isAssignableFrom(Schema)}, a new row
0776: * is created, the Tuple's values are copied, and the new Tuple that
0777: * is a member of this Table is returned. If the data schemas are not
0778: * compatible, nothing is done and null is returned.
0779: * @param t the Tuple to "add" to this table
0780: * @return the actual Tuple instance added to this table, or null if
0781: * no new Tuple has been added
0782: * @see prefuse.data.tuple.TupleSet#addTuple(prefuse.data.Tuple)
0783: */
0784: public Tuple addTuple(Tuple t) {
0785: if (t.getTable() == this ) {
0786: return null;
0787: } else {
0788: Schema s = t.getSchema();
0789: if (getSchema().isAssignableFrom(s)) {
0790: int r = addRow();
0791: for (int i = 0; i < s.getColumnCount(); ++i) {
0792: String field = s.getColumnName(i);
0793: this .set(r, field, t.get(i));
0794: }
0795: return getTuple(r);
0796: } else {
0797: return null;
0798: }
0799: }
0800: }
0801:
0802: /**
0803: * Clears the contents of this table and then attempts to add the given
0804: * Tuple instance.
0805: * @param t the Tuple to make the sole tuple in thie table
0806: * @return the actual Tuple instance added to this table, or null if
0807: * no new Tuple has been added
0808: * @see prefuse.data.tuple.TupleSet#setTuple(prefuse.data.Tuple)
0809: */
0810: public Tuple setTuple(Tuple t) {
0811: clear();
0812: return addTuple(t);
0813: }
0814:
0815: /**
0816: * Remove a tuple from this table. If the Tuple is a member of this table,
0817: * its row is deleted from the table. Otherwise, nothing is done.
0818: * @param t the Tuple to remove from the table
0819: * @return true if the Tuple row was successfully deleted, false if the
0820: * Tuple is invalid or not a member of this table
0821: * @see prefuse.data.tuple.TupleSet#removeTuple(prefuse.data.Tuple)
0822: */
0823: public boolean removeTuple(Tuple t) {
0824: if (containsTuple(t)) {
0825: removeRow(t.getRow());
0826: return true;
0827: } else {
0828: return false;
0829: }
0830: }
0831:
0832: /**
0833: * Indicates if this table contains the given Tuple instance.
0834: * @param t the Tuple to check for containment
0835: * @return true if the Tuple represents a row of this table, false if
0836: * it does not
0837: * @see prefuse.data.tuple.TupleSet#containsTuple(prefuse.data.Tuple)
0838: */
0839: public boolean containsTuple(Tuple t) {
0840: return (t.getTable() == this && isValidRow(t.getRow()));
0841: }
0842:
0843: /**
0844: * Get the number of tuples in this table. This is the same as the
0845: * value returned by {@link #getRowCount()}.
0846: * @return the number of tuples, which is the same as the number of rows
0847: * @see prefuse.data.tuple.TupleSet#getTupleCount()
0848: */
0849: public int getTupleCount() {
0850: return getRowCount();
0851: }
0852:
0853: /**
0854: * Returns true, as this table supports the addition of new data fields.
0855: * @see prefuse.data.tuple.TupleSet#isAddColumnSupported()
0856: */
0857: public boolean isAddColumnSupported() {
0858: return true;
0859: }
0860:
0861: // ------------------------------------------------------------------------
0862: // Data Access Methods
0863:
0864: /**
0865: * Check if the <code>get</code> method for the given data field returns
0866: * values that are compatible with a given target type.
0867: * @param field the data field to check
0868: * @param type a Class instance to check for compatibility with the
0869: * data field values.
0870: * @return true if the data field is compatible with provided type,
0871: * false otherwise. If the value is true, objects returned by
0872: * the {@link #get(int, String)} can be cast to the given type.
0873: * @see #get(int, String)
0874: */
0875: public boolean canGet(String field, Class type) {
0876: Column c = getColumn(field);
0877: return (c == null ? false : c.canGet(type));
0878: }
0879:
0880: /**
0881: * Check if the <code>set</code> method for the given data field can
0882: * accept values of a given target type.
0883: * @param field the data field to check
0884: * @param type a Class instance to check for compatibility with the
0885: * data field values.
0886: * @return true if the data field is compatible with provided type,
0887: * false otherwise. If the value is true, objects of the given type
0888: * can be used as parameters of the {@link #set(int, String, Object)}
0889: * method.
0890: * @see #set(int, String, Object)
0891: */
0892: public boolean canSet(String field, Class type) {
0893: Column c = getColumn(field);
0894: return (c == null ? false : c.canSet(type));
0895: }
0896:
0897: /**
0898: * Get the data value at the given row and field as an Object.
0899: * @param row the table row to get
0900: * @param field the data field to retrieve
0901: * @return the data value as an Object. The concrete type of this
0902: * Object is dependent on the underlying data column used.
0903: * @see #canGet(String, Class)
0904: * @see #getColumnType(String)
0905: */
0906: public Object get(int row, String field) {
0907: int col = getColumnNumber(field);
0908: row = getColumnRow(row, col);
0909: return getColumn(col).get(row);
0910: }
0911:
0912: /**
0913: * Set the value of a given row and data field.
0914: * @param row the table row to set
0915: * @param field the data field to set
0916: * @param val the value for the field. If the concrete type of this
0917: * Object is not compatible with the underlying data model, an
0918: * Exception will be thrown. Use the {@link #canSet(String, Class)}
0919: * method to check the type-safety ahead of time.
0920: * @see #canSet(String, Class)
0921: * @see #getColumnType(String)
0922: */
0923: public void set(int row, String field, Object val) {
0924: int col = getColumnNumber(field);
0925: row = getColumnRow(row, col);
0926: getColumn(col).set(val, row);
0927:
0928: // we don't fire a notification here, as we catch the
0929: // notification from the column itself and then dispatch
0930: }
0931:
0932: /**
0933: * Get the data value at the given row and column numbers as an Object.
0934: * @param row the row number
0935: * @param col the column number
0936: * @return the data value as an Object. The concrete type of this
0937: * Object is dependent on the underlying data column used.
0938: * @see #canGet(String, Class)
0939: * @see #getColumnType(int)
0940: */
0941: public Object get(int row, int col) {
0942: row = getColumnRow(row, col);
0943: return getColumn(col).get(row);
0944: }
0945:
0946: /**
0947: * Set the value of at the given row and column numbers.
0948: * @param row the row number
0949: * @param col the column number
0950: * @param val the value for the field. If the concrete type of this
0951: * Object is not compatible with the underlying data model, an
0952: * Exception will be thrown. Use the {@link #canSet(String, Class)}
0953: * method to check the type-safety ahead of time.
0954: * @see #canSet(String, Class)
0955: * @see #getColumnType(String)
0956: */
0957: public void set(int row, int col, Object val) {
0958: row = getColumnRow(row, col);
0959: getColumn(col).set(val, row);
0960:
0961: // we don't fire a notification here, as we catch the
0962: // notification from the column itself and then dispatch
0963: }
0964:
0965: /**
0966: * Get the default value for the given data field.
0967: * @param field the data field
0968: * @return the default value, as an Object, used to populate rows
0969: * of the data field.
0970: */
0971: public Object getDefault(String field) {
0972: int col = getColumnNumber(field);
0973: return getColumn(col).getDefaultValue();
0974: }
0975:
0976: /**
0977: * Revert this tuple's value for the given field to the default value
0978: * for the field.
0979: * @param field the data field
0980: * @see #getDefault(String)
0981: */
0982: public void revertToDefault(int row, String field) {
0983: int col = getColumnNumber(field);
0984: row = getColumnRow(row, col);
0985: getColumn(col).revertToDefault(row);
0986: }
0987:
0988: // ------------------------------------------------------------------------
0989: // Convenience Data Access Methods
0990:
0991: /**
0992: * Check if the given data field can return primitive <code>int</code>
0993: * values.
0994: * @param field the data field to check
0995: * @return true if the data field can return primitive <code>int</code>
0996: * values, false otherwise. If true, the {@link #getInt(int, String)}
0997: * method can be used safely.
0998: */
0999: public final boolean canGetInt(String field) {
1000: Column col = getColumn(field);
1001: return (col == null ? false : col.canGetInt());
1002: }
1003:
1004: /**
1005: * Check if the <code>setInt</code> method can safely be used for the
1006: * given data field.
1007: * @param field the data field to check
1008: * @return true if the {@link #setInt(int, String, int)} method can safely
1009: * be used for the given field, false otherwise.
1010: */
1011: public final boolean canSetInt(String field) {
1012: Column col = getColumn(field);
1013: return (col == null ? false : col.canSetInt());
1014: }
1015:
1016: /**
1017: * Get the data value at the given row and field as an
1018: * <code>int</code>.
1019: * @param row the table row to retrieve
1020: * @param field the data field to retrieve
1021: * @see #canGetInt(String)
1022: */
1023: public final int getInt(int row, String field) {
1024: int col = getColumnNumber(field);
1025: row = getColumnRow(row, col);
1026: return getColumn(col).getInt(row);
1027: }
1028:
1029: /**
1030: * Set the data value of the given row and field as an
1031: * <code>int</code>.
1032: * @param row the table row to set
1033: * @param field the data field to set
1034: * @param val the value to set
1035: * @see #canSetInt(String)
1036: */
1037: public final void setInt(int row, String field, int val) {
1038: int col = getColumnNumber(field);
1039: row = getColumnRow(row, col);
1040: getColumn(col).setInt(val, row);
1041: }
1042:
1043: /**
1044: * Get the data value at the given row and field as an
1045: * <code>int</code>.
1046: * @param row the table row to retrieve
1047: * @param col the column number of the data field to retrieve
1048: * @see #canGetInt(String)
1049: */
1050: public final int getInt(int row, int col) {
1051: row = getColumnRow(row, col);
1052: return getColumn(col).getInt(row);
1053: }
1054:
1055: /**
1056: * Set the data value of the given row and field as an
1057: * <code>int</code>.
1058: * @param row the table row to set
1059: * @param col the column number of the data field to set
1060: * @param val the value to set
1061: * @see #canSetInt(String)
1062: */
1063: public final void setInt(int row, int col, int val) {
1064: row = getColumnRow(row, col);
1065: getColumn(col).setInt(val, row);
1066: }
1067:
1068: // --------------------------------------------------------------
1069:
1070: /**
1071: * Check if the given data field can return primitive <code>long</code>
1072: * values.
1073: * @param field the data field to check
1074: * @return true if the data field can return primitive <code>long</code>
1075: * values, false otherwise. If true, the {@link #getLong(int, String)}
1076: * method can be used safely.
1077: */
1078: public final boolean canGetLong(String field) {
1079: Column col = getColumn(field);
1080: return (col == null ? false : col.canGetLong());
1081: }
1082:
1083: /**
1084: * Check if the <code>setLong</code> method can safely be used for the
1085: * given data field.
1086: * @param field the data field to check
1087: * @return true if the {@link #setLong(int, String, long)} method can
1088: * safely be used for the given field, false otherwise.
1089: */
1090: public final boolean canSetLong(String field) {
1091: Column col = getColumn(field);
1092: return (col == null ? false : col.canSetLong());
1093: }
1094:
1095: /**
1096: * Get the data value at the given row and field as a
1097: * <code>long</code>.
1098: * @param row the table row to retrieve
1099: * @param field the data field to retrieve
1100: * @see #canGetLong(String)
1101: */
1102: public final long getLong(int row, String field) {
1103: int col = getColumnNumber(field);
1104: row = getColumnRow(row, col);
1105: return getColumn(col).getLong(row);
1106: }
1107:
1108: /**
1109: * Set the data value of the given row and field as a
1110: * <code>long</code>.
1111: * @param row the table row to set
1112: * @param field the data field to set
1113: * @param val the value to set
1114: * @see #canSetLong(String)
1115: */
1116: public final void setLong(int row, String field, long val) {
1117: int col = getColumnNumber(field);
1118: row = getColumnRow(row, col);
1119: getColumn(col).setLong(val, row);
1120: }
1121:
1122: /**
1123: * Get the data value at the given row and field as an
1124: * <code>long</code>.
1125: * @param row the table row to retrieve
1126: * @param col the column number of the data field to retrieve
1127: * @see #canGetLong(String)
1128: */
1129: public final long getLong(int row, int col) {
1130: row = getColumnRow(row, col);
1131: return getColumn(col).getLong(row);
1132: }
1133:
1134: /**
1135: * Set the data value of the given row and field as an
1136: * <code>long</code>.
1137: * @param row the table row to set
1138: * @param col the column number of the data field to set
1139: * @param val the value to set
1140: * @see #canSetLong(String)
1141: */
1142: public final void setLong(int row, int col, long val) {
1143: row = getColumnRow(row, col);
1144: getColumn(col).setLong(val, row);
1145: }
1146:
1147: // --------------------------------------------------------------
1148:
1149: /**
1150: * Check if the given data field can return primitive <code>float</code>
1151: * values.
1152: * @param field the data field to check
1153: * @return true if the data field can return primitive <code>float</code>
1154: * values, false otherwise. If true, the {@link #getFloat(int, String)}
1155: * method can be used safely.
1156: */
1157: public final boolean canGetFloat(String field) {
1158: Column col = getColumn(field);
1159: return (col == null ? false : col.canGetFloat());
1160: }
1161:
1162: /**
1163: * Check if the <code>setFloat</code> method can safely be used for the
1164: * given data field.
1165: * @param field the data field to check
1166: * @return true if the {@link #setFloat(int, String, float)} method can
1167: * safely be used for the given field, false otherwise.
1168: */
1169: public final boolean canSetFloat(String field) {
1170: Column col = getColumn(field);
1171: return (col == null ? false : col.canSetFloat());
1172: }
1173:
1174: /**
1175: * Get the data value at the given row and field as a
1176: * <code>float</code>.
1177: * @param row the table row to retrieve
1178: * @param field the data field to retrieve
1179: * @see #canGetFloat(String)
1180: */
1181: public final float getFloat(int row, String field) {
1182: int col = getColumnNumber(field);
1183: row = getColumnRow(row, col);
1184: return getColumn(col).getFloat(row);
1185: }
1186:
1187: /**
1188: * Set the data value of the given row and field as a
1189: * <code>float</code>.
1190: * @param row the table row to set
1191: * @param field the data field to set
1192: * @param val the value to set
1193: * @see #canSetFloat(String)
1194: */
1195: public final void setFloat(int row, String field, float val) {
1196: int col = getColumnNumber(field);
1197: row = getColumnRow(row, col);
1198: getColumn(col).setFloat(val, row);
1199: }
1200:
1201: /**
1202: * Get the data value at the given row and field as a
1203: * <code>float</code>.
1204: * @param row the table row to retrieve
1205: * @param col the column number of the data field to get
1206: * @see #canGetFloat(String)
1207: */
1208: public final float getFloat(int row, int col) {
1209: row = getColumnRow(row, col);
1210: return getColumn(col).getFloat(row);
1211: }
1212:
1213: /**
1214: * Set the data value of the given row and field as a
1215: * <code>float</code>.
1216: * @param row the table row to set
1217: * @param col the column number of the data field to set
1218: * @param val the value to set
1219: * @see #canSetFloat(String)
1220: */
1221: public final void setFloat(int row, int col, float val) {
1222: row = getColumnRow(row, col);
1223: getColumn(col).setFloat(val, row);
1224: }
1225:
1226: // --------------------------------------------------------------
1227:
1228: /**
1229: * Check if the given data field can return primitive <code>double</code>
1230: * values.
1231: * @param field the data field to check
1232: * @return true if the data field can return primitive <code>double</code>
1233: * values, false otherwise. If true, the {@link #getDouble(int, String)}
1234: * method can be used safely.
1235: */
1236: public final boolean canGetDouble(String field) {
1237: Column col = getColumn(field);
1238: return (col == null ? false : col.canGetDouble());
1239: }
1240:
1241: /**
1242: * Check if the <code>setDouble</code> method can safely be used for the
1243: * given data field.
1244: * @param field the data field to check
1245: * @return true if the {@link #setDouble(int, String, double)} method can
1246: * safely be used for the given field, false otherwise.
1247: */
1248: public final boolean canSetDouble(String field) {
1249: Column col = getColumn(field);
1250: return (col == null ? false : col.canSetDouble());
1251: }
1252:
1253: /**
1254: * Get the data value at the given row and field as a
1255: * <code>double</code>.
1256: * @param row the table row to retrieve
1257: * @param field the data field to retrieve
1258: * @see #canGetDouble(String)
1259: */
1260: public final double getDouble(int row, String field) {
1261: int col = getColumnNumber(field);
1262: row = getColumnRow(row, col);
1263: return getColumn(col).getDouble(row);
1264: }
1265:
1266: /**
1267: * Set the data value of the given row and field as a
1268: * <code>double</code>.
1269: * @param row the table row to set
1270: * @param field the data field to set
1271: * @param val the value to set
1272: * @see #canSetDouble(String)
1273: */
1274: public final void setDouble(int row, String field, double val) {
1275: int col = getColumnNumber(field);
1276: row = getColumnRow(row, col);
1277: getColumn(col).setDouble(val, row);
1278: }
1279:
1280: /**
1281: * Get the data value at the given row and field as a
1282: * <code>double</code>.
1283: * @param row the table row to retrieve
1284: * @param col the column number of the data field to get
1285: * @see #canGetDouble(String)
1286: */
1287: public final double getDouble(int row, int col) {
1288: row = getColumnRow(row, col);
1289: return getColumn(col).getDouble(row);
1290: }
1291:
1292: /**
1293: * Set the data value of the given row and field as a
1294: * <code>double</code>.
1295: * @param row the table row to set
1296: * @param col the column number of the data field to set
1297: * @param val the value to set
1298: * @see #canSetDouble(String)
1299: */
1300: public final void setDouble(int row, int col, double val) {
1301: row = getColumnRow(row, col);
1302: getColumn(col).setDouble(val, row);
1303: }
1304:
1305: // --------------------------------------------------------------
1306:
1307: /**
1308: * Check if the given data field can return primitive <code>boolean</code>
1309: * values.
1310: * @param field the data field to check
1311: * @return true if the data field can return primitive <code>boolean</code>
1312: * values, false otherwise. If true, the {@link #getBoolean(int, String)}
1313: * method can be used safely.
1314: */
1315: public final boolean canGetBoolean(String field) {
1316: Column col = getColumn(field);
1317: return (col == null ? false : col.canGetBoolean());
1318: }
1319:
1320: /**
1321: * Check if the <code>setBoolean</code> method can safely be used for the
1322: * given data field.
1323: * @param field the data field to check
1324: * @return true if the {@link #setBoolean(int, String, boolean)} method can
1325: * safely be used for the given field, false otherwise.
1326: */
1327: public final boolean canSetBoolean(String field) {
1328: Column col = getColumn(field);
1329: return (col == null ? false : col.canSetBoolean());
1330: }
1331:
1332: /**
1333: * Get the data value at the given row and field as a
1334: * <code>boolean</code>.
1335: * @param row the table row to retrieve
1336: * @param field the data field to retrieve
1337: * @see #canGetBoolean(String)
1338: */
1339: public final boolean getBoolean(int row, String field) {
1340: int col = getColumnNumber(field);
1341: row = getColumnRow(row, col);
1342: return getColumn(col).getBoolean(row);
1343: }
1344:
1345: /**
1346: * Set the data value of the given row and field as a
1347: * <code>boolean</code>.
1348: * @param row the table row to set
1349: * @param field the data field to set
1350: * @param val the value to set
1351: * @see #canSetBoolean(String)
1352: */
1353: public final void setBoolean(int row, String field, boolean val) {
1354: int col = getColumnNumber(field);
1355: row = getColumnRow(row, col);
1356: getColumn(col).setBoolean(val, row);
1357: }
1358:
1359: /**
1360: * Get the data value at the given row and field as a
1361: * <code>boolean</code>.
1362: * @param row the table row to retrieve
1363: * @param col the column number of the data field to get
1364: * @see #canGetBoolean(String)
1365: */
1366: public final boolean getBoolean(int row, int col) {
1367: row = getColumnRow(row, col);
1368: return getColumn(col).getBoolean(row);
1369: }
1370:
1371: /**
1372: * Set the data value of the given row and field as a
1373: * <code>boolean</code>.
1374: * @param row the table row to set
1375: * @param col the column number of the data field to set
1376: * @param val the value to set
1377: * @see #canSetBoolean(String)
1378: */
1379: public final void setBoolean(int row, int col, boolean val) {
1380: row = getColumnRow(row, col);
1381: getColumn(col).setBoolean(val, row);
1382: }
1383:
1384: // --------------------------------------------------------------
1385:
1386: /**
1387: * Check if the given data field can return primitive <code>String</code>
1388: * values.
1389: * @param field the data field to check
1390: * @return true if the data field can return primitive <code>String</code>
1391: * values, false otherwise. If true, the {@link #getString(int, String)}
1392: * method can be used safely.
1393: */
1394: public final boolean canGetString(String field) {
1395: Column col = getColumn(field);
1396: return (col == null ? false : col.canGetString());
1397: }
1398:
1399: /**
1400: * Check if the <code>setString</code> method can safely be used for the
1401: * given data field.
1402: * @param field the data field to check
1403: * @return true if the {@link #setString(int, String, String)} method can
1404: * safely be used for the given field, false otherwise.
1405: */
1406: public final boolean canSetString(String field) {
1407: Column col = getColumn(field);
1408: return (col == null ? false : col.canSetString());
1409: }
1410:
1411: /**
1412: * Get the data value at the given row and field as a
1413: * <code>String</code>.
1414: * @param row the table row to retrieve
1415: * @param field the data field to retrieve
1416: * @see #canGetString(String)
1417: */
1418: public final String getString(int row, String field) {
1419: int col = getColumnNumber(field);
1420: row = getColumnRow(row, col);
1421: return getColumn(col).getString(row);
1422: }
1423:
1424: /**
1425: * Set the data value of the given row and field as a
1426: * <code>String</code>.
1427: * @param row the table row to set
1428: * @param field the data field to set
1429: * @param val the value to set
1430: * @see #canSetString(String)
1431: */
1432: public final void setString(int row, String field, String val) {
1433: int col = getColumnNumber(field);
1434: row = getColumnRow(row, col);
1435: getColumn(col).setString(val, row);
1436: }
1437:
1438: /**
1439: * Get the data value at the given row and field as a
1440: * <code>String</code>.
1441: * @param row the table row to retrieve
1442: * @param col the column number of the data field to retrieve
1443: * @see #canGetString(String)
1444: */
1445: public final String getString(int row, int col) {
1446: row = getColumnRow(row, col);
1447: return getColumn(col).getString(row);
1448: }
1449:
1450: /**
1451: * Set the data value of the given row and field as a
1452: * <code>String</code>.
1453: * @param row the table row to set
1454: * @param col the column number of the data field to set
1455: * @param val the value to set
1456: * @see #canSetString(String)
1457: */
1458: public final void setString(int row, int col, String val) {
1459: row = getColumnRow(row, col);
1460: getColumn(col).setString(val, row);
1461: }
1462:
1463: // --------------------------------------------------------------
1464:
1465: /**
1466: * Check if the given data field can return primitive <code>Date</code>
1467: * values.
1468: * @param field the data field to check
1469: * @return true if the data field can return primitive <code>Date</code>
1470: * values, false otherwise. If true, the {@link #getDate(int, String)}
1471: * method can be used safely.
1472: */
1473: public final boolean canGetDate(String field) {
1474: Column col = getColumn(field);
1475: return (col == null ? false : col.canGetDate());
1476: }
1477:
1478: /**
1479: * Check if the <code>setDate</code> method can safely be used for the
1480: * given data field.
1481: * @param field the data field to check
1482: * @return true if the {@link #setDate(int, String, Date)} method can
1483: * safely be used for the given field, false otherwise.
1484: */
1485: public final boolean canSetDate(String field) {
1486: Column col = getColumn(field);
1487: return (col == null ? false : col.canSetDate());
1488: }
1489:
1490: /**
1491: * Get the data value at the given row and field as a
1492: * <code>Date</code>.
1493: * @param row the table row to retrieve
1494: * @param field the data field to retrieve
1495: * @see #canGetDate(String)
1496: */
1497: public final Date getDate(int row, String field) {
1498: int col = getColumnNumber(field);
1499: row = getColumnRow(row, col);
1500: return getColumn(col).getDate(row);
1501: }
1502:
1503: /**
1504: * Set the data value of the given row and field as a
1505: * <code>Date</code>.
1506: * @param row the table row to set
1507: * @param field the data field to set
1508: * @param val the value to set
1509: * @see #canSetDate(String)
1510: */
1511: public final void setDate(int row, String field, Date val) {
1512: int col = getColumnNumber(field);
1513: row = getColumnRow(row, col);
1514: getColumn(col).setDate(val, row);
1515: }
1516:
1517: /**
1518: * Get the data value at the given row and field as a
1519: * <code>Date</code>.
1520: * @param row the table row to retrieve
1521: * @param col the column number of the data field to retrieve
1522: * @see #canGetDate(String)
1523: */
1524: public final Date getDate(int row, int col) {
1525: row = getColumnRow(row, col);
1526: return getColumn(col).getDate(row);
1527: }
1528:
1529: /**
1530: * Set the data value of the given row and field as a
1531: * <code>Date</code>.
1532: * @param row the table row to set
1533: * @param col the column number of the data field to set
1534: * @param val the value to set
1535: * @see #canSetDate(String)
1536: */
1537: public final void setDate(int row, int col, Date val) {
1538: row = getColumnRow(row, col);
1539: getColumn(col).setDate(val, row);
1540: }
1541:
1542: // ------------------------------------------------------------------------
1543: // Query Operations
1544:
1545: /**
1546: * Query this table for a filtered, sorted subset of this table. This
1547: * operation creates an entirely new table independent of this table.
1548: * If a filtered view of this same table is preferred, use the
1549: * {@link CascadedTable} class.
1550: * @param filter the predicate filter determining which rows to include
1551: * in the new table. If this value is null, all rows will be included.
1552: * @param sort the sorting criteria determining the order in which
1553: * rows are added to the new table. If this value is null, the rows
1554: * will not be sorted.
1555: * @return a new table meeting the query specification
1556: */
1557: public Table select(Predicate filter, Sort sort) {
1558: Table t = getSchema().instantiate();
1559: Iterator tuples = tuples(filter, sort);
1560: while (tuples.hasNext()) {
1561: t.addTuple((Tuple) tuples.next());
1562: }
1563: return t;
1564: }
1565:
1566: /**
1567: * Removes all table rows that meet the input predicate filter.
1568: * @param filter a predicate specifying which rows to remove from
1569: * the table.
1570: */
1571: public void remove(Predicate filter) {
1572: for (IntIterator ii = rows(filter); ii.hasNext();)
1573: removeRow(ii.nextInt());
1574: }
1575:
1576: // ------------------------------------------------------------------------
1577: // Iterators
1578:
1579: /**
1580: * Return a TableIterator over the rows of this table.
1581: * @return a TableIterator over this table
1582: */
1583: public TableIterator iterator() {
1584: return iterator(rows());
1585: }
1586:
1587: /**
1588: * Return a TableIterator over the given rows of this table.
1589: * @param rows an iterator over the table rows to visit
1590: * @return a TableIterator over this table
1591: */
1592: public TableIterator iterator(IntIterator rows) {
1593: return new TableIterator(this , rows);
1594: }
1595:
1596: /**
1597: * Get an iterator over the tuples in this table.
1598: * @return an iterator over the table tuples
1599: * @see prefuse.data.tuple.TupleSet#tuples()
1600: */
1601: public Iterator tuples() {
1602: return m_tuples.iterator(rows());
1603: }
1604:
1605: /**
1606: * Get an iterator over the tuples in this table in reverse order.
1607: * @return an iterator over the table tuples in reverse order
1608: */
1609: public Iterator tuplesReversed() {
1610: return m_tuples.iterator(rows(true));
1611: }
1612:
1613: /**
1614: * Get an iterator over the tuples for the given rows in this table.
1615: * @param rows an iterator over the table rows to visit
1616: * @return an iterator over the selected table tuples
1617: */
1618: public Iterator tuples(IntIterator rows) {
1619: return m_tuples.iterator(rows);
1620: }
1621:
1622: /**
1623: * Get an interator over the row numbers of this table.
1624: * @return an iterator over the rows of this table
1625: */
1626: public IntIterator rows() {
1627: return m_rows.rows();
1628: }
1629:
1630: /**
1631: * Get a filtered iterator over the row numbers of this table, returning
1632: * only the rows whose tuples match the given filter predicate.
1633: * @param filter the filter predicate to apply
1634: * @return a filtered iterator over the rows of this table
1635: */
1636: public IntIterator rows(Predicate filter) {
1637: return FilterIteratorFactory.rows(this , filter);
1638: }
1639:
1640: /**
1641: * Get an interator over the row numbers of this table.
1642: * @param reverse true to iterate in rever order, false for normal order
1643: * @return an iterator over the rows of this table
1644: */
1645: public IntIterator rows(boolean reverse) {
1646: return m_rows.rows(reverse);
1647: }
1648:
1649: /**
1650: * Get an iterator over the rows of this table, sorted by the given data
1651: * field. This method will create an index over the field if one does
1652: * not yet exist.
1653: * @param field the data field to sort by
1654: * @param ascend true if the iteration should proceed in an ascending
1655: * (lowest to highest) sort order, false for a descending order
1656: * @return the sorted iterator over rows of this table
1657: */
1658: public IntIterator rowsSortedBy(String field, boolean ascend) {
1659: Class type = getColumnType(field);
1660: Index index = getIndex(field, type, true);
1661: int t = ascend ? Index.TYPE_ASCENDING : Index.TYPE_DESCENDING;
1662: return index.allRows(t);
1663: }
1664:
1665: /**
1666: * Return an iterator over a range of rwos in this table, determined
1667: * by a bounded range for a given data field. A new index over the
1668: * data field will be created if it doesn't already exist.
1669: * @param field the data field for determining the bounded range
1670: * @param lo the minimum range value
1671: * @param hi the maximum range value
1672: * @param indexType indicate the sort order and inclusivity/exclusivity
1673: * of the range bounds, using the constants of the
1674: * {@link prefuse.data.util.Index} class.
1675: * @return an iterator over a range of table rows, determined by a
1676: * sorted bounded range of a data field
1677: */
1678: public IntIterator rangeSortedBy(String field, int lo, int hi,
1679: int indexType) {
1680: Index index = getIndex(field, int.class, true);
1681: return index.rows(lo, hi, indexType);
1682: }
1683:
1684: /**
1685: * Return an iterator over a range of rwos in this table, determined
1686: * by a bounded range for a given data field. A new index over the
1687: * data field will be created if it doesn't already exist.
1688: * @param field the data field for determining the bounded range
1689: * @param lo the minimum range value
1690: * @param hi the maximum range value
1691: * @param indexType indicate the sort order and inclusivity/exclusivity
1692: * of the range bounds, using the constants of the
1693: * {@link prefuse.data.util.Index} class.
1694: * @return an iterator over a range of table rows, determined by a
1695: * sorted bounded range of a data field
1696: */
1697: public IntIterator rangeSortedBy(String field, long lo, long hi,
1698: int indexType) {
1699: Index index = getIndex(field, long.class, true);
1700: return index.rows(lo, hi, indexType);
1701: }
1702:
1703: /**
1704: * Return an iterator over a range of rwos in this table, determined
1705: * by a bounded range for a given data field. A new index over the
1706: * data field will be created if it doesn't already exist.
1707: * @param field the data field for determining the bounded range
1708: * @param lo the minimum range value
1709: * @param hi the maximum range value
1710: * @param indexType indicate the sort order and inclusivity/exclusivity
1711: * of the range bounds, using the constants of the
1712: * {@link prefuse.data.util.Index} class.
1713: * @return an iterator over a range of table rows, determined by a
1714: * sorted bounded range of a data field
1715: */
1716: public IntIterator rangeSortedBy(String field, float lo, float hi,
1717: int indexType) {
1718: Index index = getIndex(field, float.class, true);
1719: return index.rows(lo, hi, indexType);
1720: }
1721:
1722: /**
1723: * Return an iterator over a range of rwos in this table, determined
1724: * by a bounded range for a given data field. A new index over the
1725: * data field will be created if it doesn't already exist.
1726: * @param field the data field for determining the bounded range
1727: * @param lo the minimum range value
1728: * @param hi the maximum range value
1729: * @param indexType indicate the sort order and inclusivity/exclusivity
1730: * of the range bounds, using the constants of the
1731: * {@link prefuse.data.util.Index} class.
1732: * @return an iterator over a range of table rows, determined by a
1733: * sorted bounded range of a data field
1734: */
1735: public IntIterator rangeSortedBy(String field, double lo,
1736: double hi, int indexType) {
1737: Index index = getIndex(field, double.class, true);
1738: return index.rows(lo, hi, indexType);
1739: }
1740:
1741: /**
1742: * Return an iterator over a range of rwos in this table, determined
1743: * by a bounded range for a given data field. A new index over the
1744: * data field will be created if it doesn't already exist.
1745: * @param field the data field for determining the bounded range
1746: * @param lo the minimum range value
1747: * @param hi the maximum range value
1748: * @param indexType indicate the sort order and inclusivity/exclusivity
1749: * of the range bounds, using the constants of the
1750: * {@link prefuse.data.util.Index} class.
1751: * @return an iterator over a range of table rows, determined by a
1752: * sorted bounded range of a data field
1753: */
1754: public IntIterator rangeSortedBy(String field, Object lo,
1755: Object hi, int indexType) {
1756: Class type = TypeLib.getSharedType(lo, hi);
1757: // TODO: check this for correctness
1758: if (type == null)
1759: throw new IllegalArgumentException("Incompatible arguments");
1760: Index index = getIndex(field, type, true);
1761: return index.rows(lo, hi, indexType);
1762: }
1763:
1764: // ------------------------------------------------------------------------
1765: // Listener Methods
1766:
1767: // -- ColumnListeners -----------------------------------------------------
1768:
1769: /**
1770: * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, boolean)
1771: */
1772: public void columnChanged(Column src, int idx, boolean prev) {
1773: handleColumnChanged(src, idx, idx);
1774: }
1775:
1776: /**
1777: * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, double)
1778: */
1779: public void columnChanged(Column src, int idx, double prev) {
1780: handleColumnChanged(src, idx, idx);
1781: }
1782:
1783: /**
1784: * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, float)
1785: */
1786: public void columnChanged(Column src, int idx, float prev) {
1787: handleColumnChanged(src, idx, idx);
1788: }
1789:
1790: /**
1791: * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, int)
1792: */
1793: public void columnChanged(Column src, int idx, int prev) {
1794: handleColumnChanged(src, idx, idx);
1795: }
1796:
1797: /**
1798: * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, long)
1799: */
1800: public void columnChanged(Column src, int idx, long prev) {
1801: handleColumnChanged(src, idx, idx);
1802: }
1803:
1804: /**
1805: * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, java.lang.Object)
1806: */
1807: public void columnChanged(Column src, int idx, Object prev) {
1808: handleColumnChanged(src, idx, idx);
1809: }
1810:
1811: /**
1812: * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, int, int)
1813: */
1814: public void columnChanged(Column src, int type, int start, int end) {
1815: handleColumnChanged(src, start, end);
1816: }
1817:
1818: /**
1819: * Handle a column change event.
1820: * @param c the modified column
1821: * @param start the starting row of the modified range
1822: * @param end the ending row (inclusive) of the modified range
1823: */
1824: protected void handleColumnChanged(Column c, int start, int end) {
1825: for (; !isValidRow(start) && start <= end; ++start)
1826: ;
1827: if (start > end)
1828: return; // bail if no valid rows
1829:
1830: // determine the index of the updated column
1831: int idx;
1832: if (m_lastCol != -1 && c == getColumn(m_lastCol)) {
1833: // constant time
1834: idx = m_lastCol;
1835: } else {
1836: // linear time
1837: idx = getColumnNumber(c);
1838: }
1839:
1840: // if we have a valid index, fire a notification
1841: if (idx >= 0) {
1842: fireTableEvent(start, end, idx, TableModelEvent.UPDATE);
1843: }
1844: }
1845:
1846: // -- TableListeners ------------------------------------------------------
1847:
1848: /**
1849: * Add a table listener to this table.
1850: * @param listnr the listener to add
1851: */
1852: public void addTableListener(TableListener listnr) {
1853: if (!m_listeners.contains(listnr))
1854: m_listeners.add(listnr);
1855: }
1856:
1857: /**
1858: * Remove a table listener from this table.
1859: * @param listnr the listener to remove
1860: */
1861: public void removeTableListener(TableListener listnr) {
1862: m_listeners.remove(listnr);
1863: }
1864:
1865: /**
1866: * Removes all table listeners from this table.
1867: */
1868: public void removeAllTableListeners() {
1869: m_listeners.clear();
1870: }
1871:
1872: /**
1873: * Fire a table event to notify listeners.
1874: * @param row0 the starting row of the modified range
1875: * @param row1 the ending row (inclusive) of the modified range
1876: * @param col the number of the column modified, or
1877: * {@link prefuse.data.event.EventConstants#ALL_COLUMNS} for operations
1878: * effecting all columns.
1879: * @param type the table modification type, one of
1880: * {@link prefuse.data.event.EventConstants#INSERT},
1881: * {@link prefuse.data.event.EventConstants#DELETE}, or
1882: * {@link prefuse.data.event.EventConstants#UPDATE}.
1883: */
1884: protected void fireTableEvent(int row0, int row1, int col, int type) {
1885: // increment the modification count
1886: ++m_modCount;
1887:
1888: if (type != EventConstants.UPDATE
1889: && col == EventConstants.ALL_COLUMNS) {
1890: // fire event to all tuple set listeners
1891: fireTupleEvent(this , row0, row1, type);
1892: }
1893:
1894: if (!m_listeners.isEmpty()) {
1895: // fire event to all table listeners
1896: Object[] lstnrs = m_listeners.getArray();
1897: for (int i = 0; i < lstnrs.length; ++i) {
1898: ((TableListener) lstnrs[i]).tableChanged(this , row0,
1899: row1, col, type);
1900: }
1901: }
1902: }
1903:
1904: // ------------------------------------------------------------------------
1905: // String Methods
1906:
1907: /**
1908: * @see java.lang.Object#toString()
1909: */
1910: public String toString() {
1911: StringBuffer sbuf = new StringBuffer();
1912: sbuf.append("Table[");
1913: sbuf.append("rows=").append(getRowCount());
1914: sbuf.append(", cols=").append(getColumnCount());
1915: sbuf.append(", maxrow=").append(m_rows.getMaximumRow());
1916: sbuf.append("]");
1917: return sbuf.toString();
1918: }
1919:
1920: // ------------------------------------------------------------------------
1921: // ColumnEntry helper
1922:
1923: /**
1924: * Helper class that encapsulates a map entry for a column, including the
1925: * column itself and its metadata and index.
1926: *
1927: * @author <a href="http://jheer.org">jeffrey heer</a>
1928: */
1929: protected static class ColumnEntry {
1930:
1931: /** The column number. */
1932: public int colnum;
1933: /** The Column instance. */
1934: public Column column;
1935: /** The column metadata instance. */
1936: public ColumnMetadata metadata;
1937: /** The column Index instance. */
1938: public Index index;
1939:
1940: /**
1941: * Create a new ColumnEntry.
1942: * @param col the column number
1943: * @param column the Column instance
1944: * @param metadata the ColumnMetadata instance
1945: */
1946: public ColumnEntry(int col, Column column,
1947: ColumnMetadata metadata) {
1948: this .colnum = col;
1949: this .column = column;
1950: this .metadata = metadata;
1951: this .index = null;
1952: }
1953:
1954: /**
1955: * Dispose of this column entry, disposing of any allocated
1956: * metadata or index instances.
1957: */
1958: public void dispose() {
1959: if (metadata != null)
1960: metadata.dispose();
1961: if (index != null)
1962: index.dispose();
1963: }
1964:
1965: } // end of inner class ColumnEntry
1966:
1967: } // end of class Table
|