KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > prefuse > data > Table


1 package prefuse.data;
2
3 import java.util.ArrayList JavaDoc;
4 import java.util.Date JavaDoc;
5 import java.util.HashMap JavaDoc;
6 import java.util.Iterator JavaDoc;
7
8 import javax.swing.event.TableModelEvent JavaDoc;
9
10 import prefuse.data.column.Column;
11 import prefuse.data.column.ColumnFactory;
12 import prefuse.data.column.ColumnMetadata;
13 import prefuse.data.event.ColumnListener;
14 import prefuse.data.event.EventConstants;
15 import prefuse.data.event.TableListener;
16 import prefuse.data.expression.Expression;
17 import prefuse.data.expression.Predicate;
18 import prefuse.data.expression.parser.ExpressionParser;
19 import prefuse.data.tuple.AbstractTupleSet;
20 import prefuse.data.tuple.TableTuple;
21 import prefuse.data.tuple.TupleManager;
22 import prefuse.data.util.FilterIteratorFactory;
23 import prefuse.data.util.Index;
24 import prefuse.data.util.RowManager;
25 import prefuse.data.util.Sort;
26 import prefuse.data.util.TableIterator;
27 import prefuse.data.util.TreeIndex;
28 import prefuse.util.TypeLib;
29 import prefuse.util.collections.CopyOnWriteArrayList;
30 import prefuse.util.collections.IncompatibleComparatorException;
31 import prefuse.util.collections.IntIterator;
32
33
34 /**
35  * <p>A Table organizes a collection of data into rows and columns, each row
36  * containing a data record, and each column containing data values for a
37  * named data field with a specific data type. Table data can be accessed
38  * directly using the row number and column name, or rows can be treated
39  * in an object-oriented fashion using {@link prefuse.data.Tuple}
40  * instances that represent a single row of data in the table. As such,
41  * tables implement the {@link prefuse.data.tuple.TupleSet} interface.</p>
42  *
43  * <p>Table rows can be inserted or deleted. In any case, none of the other
44  * existing table rows are effected by an insertion or deletion. A deleted
45  * row simply becomes invalid--any subsequent attempts to access the row
46  * either directly or through a pre-existing Tuple instance will result
47  * in an exception. However, if news rows are later added to the table,
48  * the row number for previously deleted nodes can be reused. In fact, the
49  * lower row number currently unused is assigned to the new row. This results
50  * in an efficient reuse of the table rows, but carries an important side
51  * effect -- rows do not necesarily maintain the order in which they were
52  * added once deletions have occurred on the table. If not deletions
53  * occur, the ordering of table rows will reflect the order in which
54  * rows were added to the table.</p>
55  *
56  * <p>Collections of table rows can be accessed using both iterators over
57  * the actual row numbers and iterators over the Tuple instances that
58  * encapsulate access to that row. Both types of iteration can also be
59  * filtered by providing a {@link prefuse.data.expression.Predicate},
60  * allowing tables to be queried for specific values.</p>
61  *
62  * <p>Columns (alternativele referred to as data fields) can be added to
63  * the Table using {@link #addColumn(String, Class)} and a host of
64  * similar methods. This method will automatically determine the right
65  * kind of backing column instance to use. Furthermore, Table columns
66  * can be specified using a {@link Schema} instance, which describes
67  * the column names, data types, and default values. The Table class
68  * also maintains its own internal Schema, which be accessed (in a
69  * read-only way) using the {@link #getSchema()} method.</p>
70  *
71  * <p>Tables also support additional structures. The {@link ColumnMetadata}
72  * class returned by the {@link #getMetadata(String)} method supports
73  * calculation of different statistics for a column, including minimum
74  * and maximum values, and the number of unique data values in the column.
75  * {@link prefuse.data.util.Index} instances can be created and retrieved
76  * using the {@link #index(String)} method and retrieved without triggering
77  * creation using {@link #getIndex(String)} method. An index keeps a
78  * sorted collection of all data values in a column, accelerating the creation
79  * of filtered iterators by optimizing query calculations and also providing
80  * faster computation of many of the {@link ColumnMetadata} methods. If
81  * you will be issuing a number of queries (i.e., requesting filtered
82  * iterators) dependent on the values of a given column, indexing that column
83  * may result in a significant performance increase, though at the cost
84  * of storing and maintaining the backing index structure.</p>
85  *
86  * @author <a HREF="http://jheer.org">jeffrey heer</a>
87  */

88 public class Table extends AbstractTupleSet implements ColumnListener {
89     
90     /** Listeners for changes to this table */
91     protected CopyOnWriteArrayList m_listeners;
92     
93     /** Locally stored data columns */
94     protected ArrayList JavaDoc m_columns;
95     /** Column names for locally store data columns */
96     protected ArrayList JavaDoc m_names;
97     
98     /** Mapping between column names and column entries
99      * containing column, metadata, and index references */

100     protected HashMap JavaDoc m_entries;
101     
102     /** Manager for valid row indices */
103     protected RowManager m_rows;
104     
105     /** manager for tuples, which are object representations for rows */
106     protected TupleManager m_tuples;
107     
108     /** Tracks the number of edits of this table */
109     protected int m_modCount = 0;
110     
111     /** Memoize the index of the last column operated on,
112      * used to expedite handling of column updates. */

113     protected int m_lastCol = -1;
114     
115     /** A cached schema instance, loaded lazily */
116     protected Schema m_schema;
117     
118     // ------------------------------------------------------------------------
119
// Constructors
120

121     /**
122      * Create a new, empty Table. Rows can be added to the table using
123      * the {@link #addRow()} method.
124      */

125     public Table() {
126         this(0, 0);
127     }
128     
129     /**
130      * Create a new Table with a given number of rows, and the starting
131      * capacity for a given number of columns.
132      * @param nrows the starting number of table rows
133      * @param ncols the starting capacity for columns
134      */

135     public Table(int nrows, int ncols) {
136         this(nrows, ncols, TableTuple.class);
137     }
138     
139     /**
140      * Create a new Table.
141      * @param nrows the starting number of table rows
142      * @param ncols the starting capacity for columns
143      * @param tupleType the class of the Tuple instances to use
144      */

145     protected Table(int nrows, int ncols, Class JavaDoc tupleType) {
146         m_listeners = new CopyOnWriteArrayList();
147         m_columns = new ArrayList JavaDoc(ncols);
148         m_names = new ArrayList JavaDoc(ncols);
149         m_rows = new RowManager(this);
150         m_entries = new HashMap JavaDoc(ncols+5);
151         m_tuples = new TupleManager(this, null, tupleType);
152
153         if ( nrows > 0 )
154             addRows(nrows);
155     }
156     
157     // ------------------------------------------------------------------------
158
// Table Metadata
159

160     /**
161      * Get the number of columns / data fields in this table.
162      * @return the number of columns
163      */

164     public int getColumnCount() {
165         return m_columns.size();
166     }
167
168     /**
169      * Get the data type of the column at the given column index.
170      * @param col the column index
171      * @return the data type (as a Java Class) of the column
172      */

173     public Class JavaDoc getColumnType(int col) {
174         return getColumn(col).getColumnType();
175     }
176
177     /**
178      * Get the data type of the column with the given data field name.
179      * @param field the column / data field name
180      * @return the data type (as a Java Class) of the column
181      */

182     public Class JavaDoc getColumnType(String JavaDoc field) {
183         Column c = getColumn(field);
184         return (c==null ? null : c.getColumnType());
185     }
186
187     /**
188      * Get the number of rows in the table.
189      * @return the number of rows
190      */

191     public int getRowCount() {
192         return m_rows.getRowCount();
193     }
194     
195     /**
196      * Get the minimum row index currently in use by this Table.
197      * @return the minimum row index
198      */

199     public int getMinimumRow() {
200         return m_rows.getMinimumRow();
201     }
202
203     /**
204      * Get the maximum row index currently in use by this Table.
205      * @return the maximum row index
206      */

207     public int getMaximumRow() {
208         return m_rows.getMaximumRow();
209     }
210     
211     /**
212      * Indicates if the value of the given table cell can be changed.
213      * @param row the row number
214      * @param col the column number
215      * @return true if the value can be edited/changed, false otherwise
216      */

217     public boolean isCellEditable(int row, int col) {
218         if ( !m_rows.isValidRow(row) ) {
219             return false;
220         } else {
221             return getColumn(col).isCellEditable(row);
222         }
223     }
224     
225     /**
226      * Get the number of times this Table has been modified. Adding rows,
227      * deleting rows, and updating table cell values all contribute to
228      * this count.
229      * @return the number of modifications to this table
230      */

231     public int getModificationCount() {
232         return m_modCount;
233     }
234     
235     /**
236      * Sets the TupleManager used by this Table. Use this method
237      * carefully, as it will cause all existing Tuples retrieved
238      * from this Table to be invalidated.
239      * @param tm the TupleManager to use
240      */

241     public void setTupleManager(TupleManager tm) {
242         m_tuples.invalidateAll();
243         m_tuples = tm;
244     }
245     
246     /**
247      * Returns this Table's schema. The returned schema will be
248      * locked, which means that any attempts to edit the returned schema
249      * by adding additional columns will result in a runtime exception.
250      *
251      * If this Table subsequently has columns added or removed, this will not
252      * be reflected in the returned schema. Instead, this method will need to
253      * be called again to get a current schema. Accordingly, it is not
254      * recommended that Schema instances returned by this method be stored
255      * or reused across scopes unless that exact schema snapshot is
256      * desired.
257      *
258      * @return a copy of this Table's schema
259      */

260     public Schema getSchema() {
261         if ( m_schema == null ) {
262             Schema s = new Schema();
263             for ( int i=0; i<getColumnCount(); ++i ) {
264                 s.addColumn(getColumnName(i), getColumnType(i),
265                             getColumn(i).getDefaultValue());
266             }
267             s.lockSchema();
268             m_schema = s;
269         }
270         return m_schema;
271     }
272     
273     /**
274      * Invalidates this table's cached schema. This method should be called
275      * whenever columns are added or removed from this table.
276      */

277     protected void invalidateSchema() {
278         m_schema = null;
279     }
280     
281     // ------------------------------------------------------------------------
282
// Row Operations
283

284     /**
285      * Get the row value for accessing an underlying Column instance,
286      * corresponding to the given table cell. For basic tables this just
287      * returns the input row value. However, for tables that inherit
288      * data columns from a parent table and present a filtered view on
289      * this data, a mapping between the row numbers of the table and
290      * the row numbers of the backing data column is needed. In those cases,
291      * this method returns the result of that mapping. The method
292      * {@link #getTableRow(int, int)} accesses this map in the reverse
293      * direction.
294      * @param row the table row to lookup
295      * @param col the table column to lookup
296      * @return the column row number for accessing the desired table cell
297      */

298     public int getColumnRow(int row, int col) {
299         return m_rows.getColumnRow(row, col);
300     }
301     
302     /**
303      * Get the row number for this table given a row number for a backing
304      * data column and the column number for the data column. For basic
305      * tables this just returns the column row value. However, for tables that
306      * inherit data columns from a parent table and present a filtered view on
307      * this data, a mapping between the row numbers of the table and
308      * the row numbers of the backing data column is needed. In those cases,
309      * this method returns the result of this mapping, in the direction of
310      * the backing column rows to the table rows of the cascaded table. The
311      * method {@link #getColumnRow(int, int)} accesses this map in the reverse
312      * direction.
313      * @param colrow the row of the backing data column
314      * @param col the table column to lookup.
315      * @return the table row number for accessing the desired table cell
316      */

317     public int getTableRow(int colrow, int col) {
318         return m_rows.getTableRow(colrow, col);
319     }
320     
321     /**
322      * Add a row to this table. All data columns will be notified and will
323      * take on the appropriate default values for the added row.
324      * @return the row number of the newly added row
325      */

326     public int addRow() {
327         int r = m_rows.addRow();
328         updateRowCount();
329         
330         fireTableEvent(r, r, TableModelEvent.ALL_COLUMNS,
331                        TableModelEvent.INSERT);
332         return r;
333     }
334     
335     /**
336      * Add a given number of rows to this table. All data columns will be
337      * notified and will take on the appropriate default values for the
338      * added rows.
339      * @param nrows the number of rows to add.
340      */

341     public void addRows(int nrows) {
342         for ( int i=0; i<nrows; ++i ) {
343             addRow();
344         }
345     }
346     
347     /**
348      * Internal method that updates the row counts for local data columns.
349      */

350     protected void updateRowCount() {
351         int maxrow = m_rows.getMaximumRow() + 1;
352         
353         // update columns
354
Iterator JavaDoc cols = getColumns();
355         while ( cols.hasNext() ) {
356             Column c = (Column)cols.next();
357             c.setMaximumRow(maxrow);
358         }
359     }
360     
361     /**
362      * Removes a row from this table.
363      * @param row the row to delete
364      * @return true if the row was successfully deleted, false if the
365      * row was already invalid
366      */

367     public boolean removeRow(int row) {
368         if ( m_rows.isValidRow(row) ) {
369             // the order of operations here is extremely important
370
// otherwise listeners may end up with corrupted state.
371
// fire update *BEFORE* clearing values
372
// allow listeners (e.g., indices) to perform clean-up
373
fireTableEvent(row, row, TableModelEvent.ALL_COLUMNS,
374                            TableModelEvent.DELETE);
375             // invalidate the tuple
376
m_tuples.invalidate(row);
377             // release row with row manager
378
// do this before clearing column values, so that any
379
// listeners can determine that the row is invalid
380
m_rows.releaseRow(row);
381             // now clear column values
382
for ( Iterator JavaDoc cols = getColumns(); cols.hasNext(); ) {
383                 Column c = (Column)cols.next();
384                 c.revertToDefault(row);
385             }
386             return true;
387         }
388         return false;
389     }
390     
391     /**
392      * Clear this table, removing all rows.
393      * @see prefuse.data.tuple.TupleSet#clear()
394      */

395     public void clear() {
396         IntIterator rows = rows(true);
397         while ( rows.hasNext() ) {
398             removeRow(rows.nextInt());
399         }
400     }
401     
402     /**
403      * Indicates if the given row number corresponds to a valid table row.
404      * @param row the row number to check for validity
405      * @return true if the row is valid, false if it is not
406      */

407     public boolean isValidRow(int row) {
408         return m_rows.isValidRow(row);
409     }
410     
411     // ------------------------------------------------------------------------
412
// Column Operations
413

414     /**
415      * Internal method indicating if the given data field is included as a
416      * data column.
417      */

418     protected boolean hasColumn(String JavaDoc name) {
419         return getColumnNumber(name) != -1;
420     }
421     
422     /**
423      * Get the data field name of the column at the given column number.
424      * @param col the column number
425      * @return the data field name of the column
426      */

427     public String JavaDoc getColumnName(int col) {
428         return (String JavaDoc)m_names.get(col);
429     }
430
431     /**
432      * Get the column number for a given data field name.
433      * @param field the name of the column to lookup
434      * @return the column number of the column, or -1 if the name is not found
435      */

436     public int getColumnNumber(String JavaDoc field) {
437         ColumnEntry e = (ColumnEntry)m_entries.get(field);
438         return ( e==null ? -1 : e.colnum );
439     }
440
441     /**
442      * Get the column number for the given Column instance.
443      * @param col the Column instance to lookup
444      * @return the column number of the column, or -1 if the name is not found
445      */

446     public int getColumnNumber(Column col) {
447         return m_columns.indexOf(col);
448     }
449     
450     /**
451      * Get the column at the given column number.
452      * @param col the column number
453      * @return the Column instance
454      */

455     public Column getColumn(int col) {
456         m_lastCol = col;
457         return (Column)m_columns.get(col);
458     }
459     
460     /**
461      * Get the column with the given data field name
462      * @param field the data field name of the column
463      * @return the Column instance
464      */

465     public Column getColumn(String JavaDoc field) {
466         ColumnEntry e = (ColumnEntry)m_entries.get(field);
467         return ( e != null ? e.column : null );
468     }
469     
470     /**
471      * Add a column with the given name and data type to this table.
472      * @param name the data field name for the column
473      * @param type the data type, as a Java Class, for the column
474      * @see prefuse.data.tuple.TupleSet#addColumn(java.lang.String, java.lang.Class)
475      */

476     public void addColumn(String JavaDoc name, Class JavaDoc type) {
477         addColumn(name, type, null);
478     }
479
480     /**
481      * Add a column with the given name and data type to this table.
482      * @param name the data field name for the column
483      * @param type the data type, as a Java Class, for the column
484      * @param defaultValue the default value for column data values
485      * @see prefuse.data.tuple.TupleSet#addColumn(java.lang.String, java.lang.Class, java.lang.Object)
486      */

487     public void addColumn(String JavaDoc name, Class JavaDoc type, Object JavaDoc defaultValue) {
488         Column col = ColumnFactory.getColumn(type,
489                         m_rows.getMaximumRow()+1, defaultValue);
490         addColumn(name, col);
491     }
492     
493     /**
494      * Add a derived column to this table, using an Expression instance to
495      * dynamically calculate the column data values.
496      * @param name the data field name for the column
497      * @param expr a String expression in the prefuse expression language, to
498      * be parsed into an {@link prefuse.data.expression.Expression} instance.
499      * The string is parsed by the
500      * {@link prefuse.data.expression.parser.ExpressionParser}. If an error
501      * occurs during parsing, an exception will be thrown.
502      * @see prefuse.data.tuple.TupleSet#addColumn(java.lang.String, java.lang.String)
503      */

504     public void addColumn(String JavaDoc name, String JavaDoc expr) {
505         Expression ex = ExpressionParser.parse(expr);
506         Throwable JavaDoc t = ExpressionParser.getError();
507         if ( t != null ) {
508             throw new RuntimeException JavaDoc(t);
509         } else {
510             addColumn(name, ex);
511         }
512     }
513     
514     /**
515      * Add a derived column to this table, using an Expression instance to
516      * dynamically calculate the column data values.
517      * @param name the data field name for the column
518      * @param expr the Expression that will determine the column values
519      * @see prefuse.data.tuple.TupleSet#addColumn(java.lang.String, prefuse.data.expression.Expression)
520      */

521     public void addColumn(String JavaDoc name, Expression expr) {
522         addColumn(name, ColumnFactory.getColumn(this, expr));
523     }
524     
525     /**
526      * Add a constant column to this table, which returns one constant value
527      * for all column rows.
528      * @param name the data field name for the column
529      * @param type the data type, as a Java Class, for the column
530      * @param dflt the default value for column data values
531      */

532     public void addConstantColumn(String JavaDoc name, Class JavaDoc type, Object JavaDoc dflt) {
533         addColumn(name, ColumnFactory.getConstantColumn(type, dflt));
534     }
535     
536     /**
537      * Internal method for adding a column.
538      * @param name the name of the column
539      * @param col the actual Column instance
540      */

541     protected void addColumn(String JavaDoc name, Column col) {
542         int idx = getColumnNumber(name);
543         if ( idx >= 0 && idx < m_columns.size() ) {
544             throw new IllegalArgumentException JavaDoc(
545                 "Table already has column with name \""+name+"\"");
546         }
547         
548         // add the column
549
m_columns.add(col);
550         m_names.add(name);
551         m_lastCol = m_columns.size()-1;
552         ColumnEntry entry = new ColumnEntry(m_lastCol, col,
553                 new ColumnMetadata(this, name));
554         
555         // add entry, dispose of an overridden entry if needed
556
ColumnEntry oldEntry = (ColumnEntry)m_entries.put(name, entry);
557         if ( oldEntry != null ) oldEntry.dispose();
558         
559         invalidateSchema();
560         
561         // listen to what the column has to say
562
col.addColumnListener(this);
563         
564         // fire notification
565
fireTableEvent(m_rows.getMinimumRow(), m_rows.getMaximumRow(),
566                 m_lastCol, TableModelEvent.INSERT);
567     }
568
569     /**
570      * Internal method for removing a column.
571      * @param idx the column number of the column to remove
572      * @return the removed Column instance
573      */

574     protected Column removeColumn(int idx) {
575         // make sure index is legal
576
if ( idx < 0 || idx >= m_columns.size() ) {
577             throw new IllegalArgumentException JavaDoc("Column index is not legal.");
578         }
579         
580         String JavaDoc name = (String JavaDoc)m_names.get(idx);
581         ((ColumnEntry)m_entries.get(name)).dispose();
582         Column col = (Column)m_columns.remove(idx);
583         m_entries.remove(name);
584         m_names.remove(idx);
585         renumberColumns();
586         
587         m_lastCol = -1;
588         invalidateSchema();
589         
590         // ignore what the old column has to say
591
col.removeColumnListener(this);
592         
593         // fire notification
594
fireTableEvent(m_rows.getMinimumRow(), m_rows.getMaximumRow(),
595                        idx, TableModelEvent.DELETE);
596         
597         return col;
598     }
599     
600     /**
601      * Remove a data field from this table
602      * @param field the name of the data field / column to remove
603      * @return the removed Column instance
604      */

605     public Column removeColumn(String JavaDoc field) {
606         int idx = m_names.indexOf(field);
607         if ( idx < 0 ) {
608             throw new IllegalArgumentException JavaDoc("No such column.");
609         }
610         return removeColumn(idx);
611     }
612     
613     /**
614      * Remove a column from this table
615      * @param c the column instance to remove
616      */

617     public void removeColumn(Column c) {
618         int idx = m_columns.indexOf(c);
619         if ( idx < 0 ) {
620             throw new IllegalArgumentException JavaDoc("No such column.");
621         }
622         removeColumn(idx);
623     }
624     
625     /**
626      * Internal method that re-numbers columns upon column removal.
627      */

628     protected void renumberColumns() {
629         Iterator JavaDoc iter = m_names.iterator();
630         for ( int idx=0; iter.hasNext(); ++idx ) {
631             String JavaDoc name = (String JavaDoc)iter.next();
632             ColumnEntry e = (ColumnEntry)m_entries.get(name);
633             e.colnum = idx;
634         }
635     }
636     
637     /**
638      * Internal method that returns an iterator over columns
639      * @return an iterator over columns
640      */

641     protected Iterator JavaDoc getColumns() {
642         return m_columns.iterator();
643     }
644
645     /**
646      * Internal method that returns an iterator over column names
647      * @return an iterator over column name
648      */

649     protected Iterator JavaDoc getColumnNames() {
650         return m_names.iterator();
651     }
652     
653     // ------------------------------------------------------------------------
654
// Column Metadata
655

656     /**
657      * Return a metadata instance providing summary information about a column.
658      * @param field the data field name of the column
659      * @return the columns' associated ColumnMetadata instance
660      */

661     public ColumnMetadata getMetadata(String JavaDoc field) {
662         ColumnEntry e = (ColumnEntry)m_entries.get(field);
663         if ( e == null ) {
664             throw new IllegalArgumentException JavaDoc("Unknown column name: "+field);
665         }
666         return e.metadata;
667     }
668     
669     // ------------------------------------------------------------------------
670
// Index Methods
671

672     /**
673      * Create (if necessary) and return an index over the given data field.
674      * The first call to this method with a given field name will cause the
675      * index to be created and stored. Subsequent calls will simply return
676      * the stored index. To attempt to retrieve an index without triggering
677      * creation of a new index, use the {@link #getIndex(String)} method.
678      * @param field the data field name of the column to index
679      * @return the index over the specified data column
680      */

681     public Index index(String JavaDoc field) {
682         ColumnEntry e = (ColumnEntry)m_entries.get(field);
683         if ( e == null ) {
684             throw new IllegalArgumentException JavaDoc("Unknown column name: "+field);
685         } else if ( e.index != null ) {
686             return e.index; // already indexed
687
}
688         
689         Column col = e.column;
690         try {
691             e.index = new TreeIndex(this, m_rows, col, null);
692         } catch ( IncompatibleComparatorException ice ) { /* can't happen */ }
693         
694         return e.index;
695     }
696     
697     /**
698      * Retrieve, without creating, an index for the given data field.
699      * @param field the data field name of the column
700      * @return the stored index for the column, or null if no index has
701      * been created
702      */

703     public Index getIndex(String JavaDoc field) {
704         ColumnEntry e = (ColumnEntry)m_entries.get(field);
705         if ( e == null ) {
706             throw new IllegalArgumentException JavaDoc("Unknown column name: "+field);
707         }
708         return e.index;
709     }
710     
711     /**
712      * Internal method for index creation and retrieval.
713      * @param field the data field name of the column
714      * @param expType the expected data type of the index
715      * @param create indicates whether or not a new index should be created
716      * if none currently exists for the given data field
717      * @return the Index for the given data field
718      */

719     protected Index getIndex(String JavaDoc field, Class JavaDoc expType, boolean create) {
720         if ( !expType.equals(getColumnType(field)) ) {
721             // TODO: need more nuanced type checking here?
722
throw new IllegalArgumentException JavaDoc("Column type does not match.");
723         }
724         if ( getIndex(field)==null && create) {
725             index(field);
726         }
727         return getIndex(field);
728     }
729     
730     /**
731      * Remove the Index associated with the given data field / column name.
732      * @param field the name of the column for which to remove the index
733      * @return true if an index was successfully removed, false if no
734      * such index was found
735      */

736     public boolean removeIndex(String JavaDoc field) {
737         ColumnEntry e = (ColumnEntry)m_entries.get(field);
738         if ( e == null ) {
739             throw new IllegalArgumentException JavaDoc("Unknown column name: "+field);
740         }
741         if ( e.index == null ) {
742             return false;
743         } else {
744             e.index.dispose();
745             e.index = null;
746             return true;
747         }
748     }
749     
750     // ------------------------------------------------------------------------
751
// Tuple Methods
752

753     /**
754      * Get the Tuple instance providing object-oriented access to the given
755      * table row.
756      * @param row the table row
757      * @return the Tuple for the given table row
758      */

759     public Tuple getTuple(int row) {
760         return m_tuples.getTuple(row);
761     }
762     
763     /**
764      * Add a Tuple to this table. If the Tuple is already a member of this
765      * table, nothing is done and null is returned. If the Tuple is not
766      * a member of this Table but has a compatible data schema, as
767      * determined by {@link Schema#isAssignableFrom(Schema)}, a new row
768      * is created, the Tuple's values are copied, and the new Tuple that
769      * is a member of this Table is returned. If the data schemas are not
770      * compatible, nothing is done and null is returned.
771      * @param t the Tuple to "add" to this table
772      * @return the actual Tuple instance added to this table, or null if
773      * no new Tuple has been added
774      * @see prefuse.data.tuple.TupleSet#addTuple(prefuse.data.Tuple)
775      */

776     public Tuple addTuple(Tuple t) {
777         if ( t.getTable() == this ) {
778             return null;
779         } else {
780             Schema s = t.getSchema();
781             if ( getSchema().isAssignableFrom(s) ) {
782                 int r = addRow();
783                 for ( int i=0; i<s.getColumnCount(); ++i ) {
784                     String JavaDoc field = s.getColumnName(i);
785                     this.set(r, field, t.get(i));
786                 }
787                 return getTuple(r);
788             } else {
789                 return null;
790             }
791         }
792     }
793     
794     /**
795      * Clears the contents of this table and then attempts to add the given
796      * Tuple instance.
797      * @param t the Tuple to make the sole tuple in thie table
798      * @return the actual Tuple instance added to this table, or null if
799      * no new Tuple has been added
800      * @see prefuse.data.tuple.TupleSet#setTuple(prefuse.data.Tuple)
801      */

802     public Tuple setTuple(Tuple t) {
803         clear();
804         return addTuple(t);
805     }
806     
807     /**
808      * Remove a tuple from this table. If the Tuple is a member of this table,
809      * its row is deleted from the table. Otherwise, nothing is done.
810      * @param t the Tuple to remove from the table
811      * @return true if the Tuple row was successfully deleted, false if the
812      * Tuple is invalid or not a member of this table
813      * @see prefuse.data.tuple.TupleSet#removeTuple(prefuse.data.Tuple)
814      */

815     public boolean removeTuple(Tuple t) {
816         if ( containsTuple(t) ) {
817             removeRow(t.getRow());
818             return true;
819         } else {
820             return false;
821         }
822     }
823     
824     /**
825      * Indicates if this table contains the given Tuple instance.
826      * @param t the Tuple to check for containment
827      * @return true if the Tuple represents a row of this table, false if
828      * it does not
829      * @see prefuse.data.tuple.TupleSet#containsTuple(prefuse.data.Tuple)
830      */

831     public boolean containsTuple(Tuple t) {
832         return (t.getTable()==this && isValidRow(t.getRow()));
833     }
834     
835     /**
836      * Get the number of tuples in this table. This is the same as the
837      * value returned by {@link #getRowCount()}.
838      * @return the number of tuples, which is the same as the number of rows
839      * @see prefuse.data.tuple.TupleSet#getTupleCount()
840      */

841     public int getTupleCount() {
842         return getRowCount();
843     }
844     
845     /**
846      * Returns true, as this table supports the addition of new data fields.
847      * @see prefuse.data.tuple.TupleSet#isAddColumnSupported()
848      */

849     public boolean isAddColumnSupported() {
850         return true;
851     }
852     
853     // ------------------------------------------------------------------------
854
// Data Access Methods
855

856     /**
857      * Check if the <code>get</code> method for the given data field returns
858      * values that are compatible with a given target type.
859      * @param field the data field to check
860      * @param type a Class instance to check for compatibility with the
861      * data field values.
862      * @return true if the data field is compatible with provided type,
863      * false otherwise. If the value is true, objects returned by
864      * the {@link #get(int, String)} can be cast to the given type.
865      * @see #get(int, String)
866      */

867     public boolean canGet(String JavaDoc field, Class JavaDoc type) {
868         Column c = getColumn(field);
869         return ( c==null ? false : c.canGet(type) );
870     }
871     
872     /**
873      * Check if the <code>set</code> method for the given data field can
874      * accept values of a given target type.
875      * @param field the data field to check
876      * @param type a Class instance to check for compatibility with the
877      * data field values.
878      * @return true if the data field is compatible with provided type,
879      * false otherwise. If the value is true, objects of the given type
880      * can be used as parameters of the {@link #set(int, String, Object)}
881      * method.
882      * @see #set(int, String, Object)
883      */

884     public boolean canSet(String JavaDoc field, Class JavaDoc type) {
885         Column c = getColumn(field);
886         return ( c==null ? false : c.canSet(type) );
887     }
888     
889     /**
890      * Get the data value at the given row and field as an Object.
891      * @param row the table row to get
892      * @param field the data field to retrieve
893      * @return the data value as an Object. The concrete type of this
894      * Object is dependent on the underlying data column used.
895      * @see #canGet(String, Class)
896      * @see #getColumnType(String)
897      */

898     public Object JavaDoc get(int row, String JavaDoc field) {
899         int col = getColumnNumber(field);
900         row = getColumnRow(row, col);
901         return getColumn(col).get(row);
902     }
903     
904     /**
905      * Set the value of a given row and data field.
906      * @param row the table row to set
907      * @param field the data field to set
908      * @param val the value for the field. If the concrete type of this
909      * Object is not compatible with the underlying data model, an
910      * Exception will be thrown. Use the {@link #canSet(String, Class)}
911      * method to check the type-safety ahead of time.
912      * @see #canSet(String, Class)
913      * @see #getColumnType(String)
914      */

915     public void set(int row, String JavaDoc field, Object JavaDoc val) {
916         int col = getColumnNumber(field);
917         row = getColumnRow(row, col);
918         getColumn(col).set(val, row);
919         
920         // we don't fire a notification here, as we catch the
921
// notification from the column itself and then dispatch
922
}
923     
924     /**
925      * Get the data value at the given row and column numbers as an Object.
926      * @param row the row number
927      * @param col the column number
928      * @return the data value as an Object. The concrete type of this
929      * Object is dependent on the underlying data column used.
930      * @see #canGet(String, Class)
931      * @see #getColumnType(int)
932      */

933     public Object JavaDoc get(int row, int col) {
934         row = getColumnRow(row, col);
935         return getColumn(col).get(row);
936     }
937
938     /**
939      * Set the value of at the given row and column numbers.
940      * @param row the row number
941      * @param col the column number
942      * @param val the value for the field. If the concrete type of this
943      * Object is not compatible with the underlying data model, an
944      * Exception will be thrown. Use the {@link #canSet(String, Class)}
945      * method to check the type-safety ahead of time.
946      * @see #canSet(String, Class)
947      * @see #getColumnType(String)
948      */

949     public void set(int row, int col, Object JavaDoc val) {
950         row = getColumnRow(row, col);
951         getColumn(col).set(val, row);
952         
953         // we don't fire a notification here, as we catch the
954
// notification from the column itself and then dispatch
955
}
956     
957     /**
958      * Get the default value for the given data field.
959      * @param field the data field
960      * @return the default value, as an Object, used to populate rows
961      * of the data field.
962      */

963     public Object JavaDoc getDefault(String JavaDoc field) {
964         int col = getColumnNumber(field);
965         return getColumn(col).getDefaultValue();
966     }
967     
968     /**
969      * Revert this tuple's value for the given field to the default value
970      * for the field.
971      * @param field the data field
972      * @see #getDefault(String)
973      */

974     public void revertToDefault(int row, String JavaDoc field) {
975         int col = getColumnNumber(field);
976         row = getColumnRow(row, col);
977         getColumn(col).revertToDefault(row);
978     }
979
980     // ------------------------------------------------------------------------
981
// Convenience Data Access Methods
982

983     /**
984      * Check if the given data field can return primitive <code>int</code>
985      * values.
986      * @param field the data field to check
987      * @return true if the data field can return primitive <code>int</code>
988      * values, false otherwise. If true, the {@link #getInt(int, String)}
989      * method can be used safely.
990      */

991     public final boolean canGetInt(String JavaDoc field) {
992         Column col = getColumn(field);
993         return ( col==null ? false : col.canGetInt() );
994     }
995     
996     /**
997      * Check if the <code>setInt</code> method can safely be used for the
998      * given data field.
999      * @param field the data field to check
1000     * @return true if the {@link #setInt(int, String, int)} method can safely
1001     * be used for the given field, false otherwise.
1002     */

1003    public final boolean canSetInt(String JavaDoc field) {
1004        Column col = getColumn(field);
1005        return ( col==null ? false : col.canSetInt() );
1006    }
1007    
1008    /**
1009     * Get the data value at the given row and field as an
1010     * <code>int</code>.
1011     * @param row the table row to retrieve
1012     * @param field the data field to retrieve
1013     * @see #canGetInt(String)
1014     */

1015    public final int getInt(int row, String JavaDoc field) {
1016        int col = getColumnNumber(field);
1017        row = getColumnRow(row, col);
1018        return getColumn(col).getInt(row);
1019    }
1020    
1021    /**
1022     * Set the data value of the given row and field as an
1023     * <code>int</code>.
1024     * @param row the table row to set
1025     * @param field the data field to set
1026     * @param val the value to set
1027     * @see #canSetInt(String)
1028     */

1029    public final void setInt(int row, String JavaDoc field, int val) {
1030        int col = getColumnNumber(field);
1031        row = getColumnRow(row, col);
1032        getColumn(col).setInt(val, row);
1033    }
1034    
1035    /**
1036     * Get the data value at the given row and field as an
1037     * <code>int</code>.
1038     * @param row the table row to retrieve
1039     * @param col the column number of the data field to retrieve
1040     * @see #canGetInt(String)
1041     */

1042    public final int getInt(int row, int col) {
1043        row = getColumnRow(row, col);
1044        return getColumn(col).getInt(row);
1045    }
1046    
1047    /**
1048     * Set the data value of the given row and field as an
1049     * <code>int</code>.
1050     * @param row the table row to set
1051     * @param col the column number of the data field to set
1052     * @param val the value to set
1053     * @see #canSetInt(String)
1054     */

1055    public final void setInt(int row, int col, int val) {
1056        row = getColumnRow(row, col);
1057        getColumn(col).setInt(val, row);
1058    }
1059    
1060    // --------------------------------------------------------------
1061

1062    /**
1063     * Check if the given data field can return primitive <code>long</code>
1064     * values.
1065     * @param field the data field to check
1066     * @return true if the data field can return primitive <code>long</code>
1067     * values, false otherwise. If true, the {@link #getLong(int, String)}
1068     * method can be used safely.
1069     */

1070    public final boolean canGetLong(String JavaDoc field) {
1071        Column col = getColumn(field);
1072        return ( col==null ? false : col.canGetLong() );
1073    }
1074    
1075    /**
1076     * Check if the <code>setLong</code> method can safely be used for the
1077     * given data field.
1078     * @param field the data field to check
1079     * @return true if the {@link #setLong(int, String, long)} method can
1080     * safely be used for the given field, false otherwise.
1081     */

1082    public final boolean canSetLong(String JavaDoc field) {
1083        Column col = getColumn(field);
1084        return ( col==null ? false : col.canSetLong() );
1085    }
1086    
1087    /**
1088     * Get the data value at the given row and field as a
1089     * <code>long</code>.
1090     * @param row the table row to retrieve
1091     * @param field the data field to retrieve
1092     * @see #canGetLong(String)
1093     */

1094    public final long getLong(int row, String JavaDoc field) {
1095        int col = getColumnNumber(field);
1096        row = getColumnRow(row, col);
1097        return getColumn(col).getLong(row);
1098    }
1099    
1100    /**
1101     * Set the data value of the given row and field as a
1102     * <code>long</code>.
1103     * @param row the table row to set
1104     * @param field the data field to set
1105     * @param val the value to set
1106     * @see #canSetLong(String)
1107     */

1108    public final void setLong(int row, String JavaDoc field, long val) {
1109        int col = getColumnNumber(field);
1110        row = getColumnRow(row, col);
1111        getColumn(col).setLong(val, row);
1112    }
1113
1114    /**
1115     * Get the data value at the given row and field as an
1116     * <code>long</code>.
1117     * @param row the table row to retrieve
1118     * @param col the column number of the data field to retrieve
1119     * @see #canGetLong(String)
1120     */

1121    public final long getLong(int row, int col) {
1122        row = getColumnRow(row, col);
1123        return getColumn(col).getLong(row);
1124    }
1125    
1126    /**
1127     * Set the data value of the given row and field as an
1128     * <code>long</code>.
1129     * @param row the table row to set
1130     * @param col the column number of the data field to set
1131     * @param val the value to set
1132     * @see #canSetLong(String)
1133     */

1134    public final void setLong(int row, int col, long val) {
1135        row = getColumnRow(row, col);
1136        getColumn(col).setLong(val, row);
1137    }
1138    
1139    // --------------------------------------------------------------
1140

1141    /**
1142     * Check if the given data field can return primitive <code>float</code>
1143     * values.
1144     * @param field the data field to check
1145     * @return true if the data field can return primitive <code>float</code>
1146     * values, false otherwise. If true, the {@link #getFloat(int, String)}
1147     * method can be used safely.
1148     */

1149    public final boolean canGetFloat(String JavaDoc field) {
1150        Column col = getColumn(field);
1151        return ( col==null ? false : col.canGetFloat() );
1152    }
1153    
1154    /**
1155     * Check if the <code>setFloat</code> method can safely be used for the
1156     * given data field.
1157     * @param field the data field to check
1158     * @return true if the {@link #setFloat(int, String, float)} method can
1159     * safely be used for the given field, false otherwise.
1160     */

1161    public final boolean canSetFloat(String JavaDoc field) {
1162        Column col = getColumn(field);
1163        return ( col==null ? false : col.canSetFloat() );
1164    }
1165    
1166    /**
1167     * Get the data value at the given row and field as a
1168     * <code>float</code>.
1169     * @param row the table row to retrieve
1170     * @param field the data field to retrieve
1171     * @see #canGetFloat(String)
1172     */

1173    public final float getFloat(int row, String JavaDoc field) {
1174        int col = getColumnNumber(field);
1175        row = getColumnRow(row, col);
1176        return getColumn(col).getFloat(row);
1177    }
1178    
1179    /**
1180     * Set the data value of the given row and field as a
1181     * <code>float</code>.
1182     * @param row the table row to set
1183     * @param field the data field to set
1184     * @param val the value to set
1185     * @see #canSetFloat(String)
1186     */

1187    public final void setFloat(int row, String JavaDoc field, float val) {
1188        int col = getColumnNumber(field);
1189        row = getColumnRow(row, col);
1190        getColumn(col).setFloat(val, row);
1191    }
1192    
1193    /**
1194     * Get the data value at the given row and field as a
1195     * <code>float</code>.
1196     * @param row the table row to retrieve
1197     * @param col the column number of the data field to get
1198     * @see #canGetFloat(String)
1199     */

1200    public final float getFloat(int row, int col) {
1201        row = getColumnRow(row, col);
1202        return getColumn(col).getFloat(row);
1203    }
1204    
1205    /**
1206     * Set the data value of the given row and field as a
1207     * <code>float</code>.
1208     * @param row the table row to set
1209     * @param col the column number of the data field to set
1210     * @param val the value to set
1211     * @see #canSetFloat(String)
1212     */

1213    public final void setFloat(int row, int col, float val) {
1214        row = getColumnRow(row, col);
1215        getColumn(col).setFloat(val, row);
1216    }
1217    
1218    // --------------------------------------------------------------
1219

1220    /**
1221     * Check if the given data field can return primitive <code>double</code>
1222     * values.
1223     * @param field the data field to check
1224     * @return true if the data field can return primitive <code>double</code>
1225     * values, false otherwise. If true, the {@link #getDouble(int, String)}
1226     * method can be used safely.
1227     */

1228    public final boolean canGetDouble(String JavaDoc field) {
1229        Column col = getColumn(field);
1230        return ( col==null ? false : col.canGetDouble() );
1231    }
1232    
1233    /**
1234     * Check if the <code>setDouble</code> method can safely be used for the
1235     * given data field.
1236     * @param field the data field to check
1237     * @return true if the {@link #setDouble(int, String, double)} method can
1238     * safely be used for the given field, false otherwise.
1239     */

1240    public final boolean canSetDouble(String JavaDoc field) {
1241        Column col = getColumn(field);
1242        return ( col==null ? false : col.canSetDouble() );
1243    }
1244    
1245    /**
1246     * Get the data value at the given row and field as a
1247     * <code>double</code>.
1248     * @param row the table row to retrieve
1249     * @param field the data field to retrieve
1250     * @see #canGetDouble(String)
1251     */

1252    public final double getDouble(int row, String JavaDoc field) {
1253        int col = getColumnNumber(field);
1254        row = getColumnRow(row, col);
1255        return getColumn(col).getDouble(row);
1256    }
1257    
1258    /**
1259     * Set the data value of the given row and field as a
1260     * <code>double</code>.
1261     * @param row the table row to set
1262     * @param field the data field to set
1263     * @param val the value to set
1264     * @see #canSetDouble(String)
1265     */

1266    public final void setDouble(int row, String JavaDoc field, double val) {
1267        int col = getColumnNumber(field);
1268        row = getColumnRow(row, col);
1269        getColumn(col).setDouble(val, row);
1270    }
1271    
1272    /**
1273     * Get the data value at the given row and field as a
1274     * <code>double</code>.
1275     * @param row the table row to retrieve
1276     * @param col the column number of the data field to get
1277     * @see #canGetDouble(String)
1278     */

1279    public final double getDouble(int row, int col) {
1280        row = getColumnRow(row, col);
1281        return getColumn(col).getDouble(row);
1282    }
1283    
1284    /**
1285     * Set the data value of the given row and field as a
1286     * <code>double</code>.
1287     * @param row the table row to set
1288     * @param col the column number of the data field to set
1289     * @param val the value to set
1290     * @see #canSetDouble(String)
1291     */

1292    public final void setDouble(int row, int col, double val) {
1293        row = getColumnRow(row, col);
1294        getColumn(col).setDouble(val, row);
1295    }
1296
1297    // --------------------------------------------------------------
1298

1299    /**
1300     * Check if the given data field can return primitive <code>boolean</code>
1301     * values.
1302     * @param field the data field to check
1303     * @return true if the data field can return primitive <code>boolean</code>
1304     * values, false otherwise. If true, the {@link #getBoolean(int, String)}
1305     * method can be used safely.
1306     */

1307    public final boolean canGetBoolean(String JavaDoc field) {
1308        Column col = getColumn(field);
1309        return ( col==null ? false : col.canGetBoolean() );
1310    }
1311    
1312    /**
1313     * Check if the <code>setBoolean</code> method can safely be used for the
1314     * given data field.
1315     * @param field the data field to check
1316     * @return true if the {@link #setBoolean(int, String, boolean)} method can
1317     * safely be used for the given field, false otherwise.
1318     */

1319    public final boolean canSetBoolean(String JavaDoc field) {
1320        Column col = getColumn(field);
1321        return ( col==null ? false : col.canSetBoolean() );
1322    }
1323    
1324    /**
1325     * Get the data value at the given row and field as a
1326     * <code>boolean</code>.
1327     * @param row the table row to retrieve
1328     * @param field the data field to retrieve
1329     * @see #canGetBoolean(String)
1330     */

1331    public final boolean getBoolean(int row, String JavaDoc field) {
1332        int col = getColumnNumber(field);
1333        row = getColumnRow(row, col);
1334        return getColumn(col).getBoolean(row);
1335    }
1336    
1337    /**
1338     * Set the data value of the given row and field as a
1339     * <code>boolean</code>.
1340     * @param row the table row to set
1341     * @param field the data field to set
1342     * @param val the value to set
1343     * @see #canSetBoolean(String)
1344     */

1345    public final void setBoolean(int row, String JavaDoc field, boolean val) {
1346        int col = getColumnNumber(field);
1347        row = getColumnRow(row, col);
1348        getColumn(col).setBoolean(val, row);
1349    }
1350    
1351    /**
1352     * Get the data value at the given row and field as a
1353     * <code>boolean</code>.
1354     * @param row the table row to retrieve
1355     * @param col the column number of the data field to get
1356     * @see #canGetBoolean(String)
1357     */

1358    public final boolean getBoolean(int row, int col) {
1359        row = getColumnRow(row, col);
1360        return getColumn(col).getBoolean(row);
1361    }
1362    
1363    /**
1364     * Set the data value of the given row and field as a
1365     * <code>boolean</code>.
1366     * @param row the table row to set
1367     * @param col the column number of the data field to set
1368     * @param val the value to set
1369     * @see #canSetBoolean(String)
1370     */

1371    public final void setBoolean(int row, int col, boolean val) {
1372        row = getColumnRow(row, col);
1373        getColumn(col).setBoolean(val, row);
1374    }
1375    
1376    // --------------------------------------------------------------
1377

1378    /**
1379     * Check if the given data field can return primitive <code>String</code>
1380     * values.
1381     * @param field the data field to check
1382     * @return true if the data field can return primitive <code>String</code>
1383     * values, false otherwise. If true, the {@link #getString(int, String)}
1384     * method can be used safely.
1385     */

1386    public final boolean canGetString(String JavaDoc field) {
1387        Column col = getColumn(field);
1388        return ( col==null ? false : col.canGetString() );
1389    }
1390    
1391    /**
1392     * Check if the <code>setString</code> method can safely be used for the
1393     * given data field.
1394     * @param field the data field to check
1395     * @return true if the {@link #setString(int, String, String)} method can
1396     * safely be used for the given field, false otherwise.
1397     */

1398    public final boolean canSetString(String JavaDoc field) {
1399        Column col = getColumn(field);
1400        return ( col==null ? false : col.canSetString() );
1401    }
1402    
1403    /**
1404     * Get the data value at the given row and field as a
1405     * <code>String</code>.
1406     * @param row the table row to retrieve
1407     * @param field the data field to retrieve
1408     * @see #canGetString(String)
1409     */

1410    public final String JavaDoc getString(int row, String JavaDoc field) {
1411        int col = getColumnNumber(field);
1412        row = getColumnRow(row, col);
1413        return getColumn(col).getString(row);
1414    }
1415    
1416    /**
1417     * Set the data value of the given row and field as a
1418     * <code>String</code>.
1419     * @param row the table row to set
1420     * @param field the data field to set
1421     * @param val the value to set
1422     * @see #canSetString(String)
1423     */

1424    public final void setString(int row, String JavaDoc field, String JavaDoc val) {
1425        int col = getColumnNumber(field);
1426        row = getColumnRow(row, col);
1427        getColumn(col).setString(val, row);
1428    }
1429    
1430    /**
1431     * Get the data value at the given row and field as a
1432     * <code>String</code>.
1433     * @param row the table row to retrieve
1434     * @param col the column number of the data field to retrieve
1435     * @see #canGetString(String)
1436     */

1437    public final String JavaDoc getString(int row, int col) {
1438        row = getColumnRow(row, col);
1439        return getColumn(col).getString(row);
1440    }
1441    
1442    /**
1443     * Set the data value of the given row and field as a
1444     * <code>String</code>.
1445     * @param row the table row to set
1446     * @param col the column number of the data field to set
1447     * @param val the value to set
1448     * @see #canSetString(String)
1449     */

1450    public final void setString(int row, int col, String JavaDoc val) {
1451        row = getColumnRow(row, col);
1452        getColumn(col).setString(val, row);
1453    }
1454    
1455    // --------------------------------------------------------------
1456

1457    /**
1458     * Check if the given data field can return primitive <code>Date</code>
1459     * values.
1460     * @param field the data field to check
1461     * @return true if the data field can return primitive <code>Date</code>
1462     * values, false otherwise. If true, the {@link #getDate(int, String)}
1463     * method can be used safely.
1464     */

1465    public final boolean canGetDate(String JavaDoc field) {
1466        Column col = getColumn(field);
1467        return ( col==null ? false : col.canGetDate() );
1468    }
1469    
1470    /**
1471     * Check if the <code>setDate</code> method can safely be used for the
1472     * given data field.
1473     * @param field the data field to check
1474     * @return true if the {@link #setDate(int, String, Date)} method can
1475     * safely be used for the given field, false otherwise.
1476     */

1477    public final boolean canSetDate(String JavaDoc field) {
1478        Column col = getColumn(field);
1479        return ( col==null ? false : col.canSetDate() );
1480    }
1481    
1482    /**
1483     * Get the data value at the given row and field as a
1484     * <code>Date</code>.
1485     * @param row the table row to retrieve
1486     * @param field the data field to retrieve
1487     * @see #canGetDate(String)
1488     */

1489    public final Date JavaDoc getDate(int row, String JavaDoc field) {
1490        int col = getColumnNumber(field);
1491        row = getColumnRow(row, col);
1492        return getColumn(col).getDate(row);
1493    }
1494    
1495    /**
1496     * Set the data value of the given row and field as a
1497     * <code>Date</code>.
1498     * @param row the table row to set
1499     * @param field the data field to set
1500     * @param val the value to set
1501     * @see #canSetDate(String)
1502     */

1503    public final void setDate(int row, String JavaDoc field, Date JavaDoc val) {
1504        int col = getColumnNumber(field);
1505        row = getColumnRow(row, col);
1506        getColumn(col).setDate(val, row);
1507    }
1508    
1509    /**
1510     * Get the data value at the given row and field as a
1511     * <code>Date</code>.
1512     * @param row the table row to retrieve
1513     * @param col the column number of the data field to retrieve
1514     * @see #canGetDate(String)
1515     */

1516    public final Date JavaDoc getDate(int row, int col) {
1517        row = getColumnRow(row, col);
1518        return getColumn(col).getDate(row);
1519    }
1520    
1521    /**
1522     * Set the data value of the given row and field as a
1523     * <code>Date</code>.
1524     * @param row the table row to set
1525     * @param col the column number of the data field to set
1526     * @param val the value to set
1527     * @see #canSetDate(String)
1528     */

1529    public final void setDate(int row, int col, Date JavaDoc val) {
1530        row = getColumnRow(row, col);
1531        getColumn(col).setDate(val, row);
1532    }
1533
1534    // ------------------------------------------------------------------------
1535
// Query Operations
1536

1537    /**
1538     * Query this table for a filtered, sorted subset of this table. This
1539     * operation creates an entirely new table independent of this table.
1540     * If a filtered view of this same table is preferred, use the
1541     * {@link CascadedTable} class.
1542     * @param filter the predicate filter determining which rows to include
1543     * in the new table. If this value is null, all rows will be included.
1544     * @param sort the sorting criteria determining the order in which
1545     * rows are added to the new table. If this value is null, the rows
1546     * will not be sorted.
1547     * @return a new table meeting the query specification
1548     */

1549    public Table select(Predicate filter, Sort sort) {
1550        Table t = getSchema().instantiate();
1551        Iterator JavaDoc tuples = tuples(filter, sort);
1552        while ( tuples.hasNext() ) {
1553            t.addTuple((Tuple)tuples.next());
1554        }
1555        return t;
1556    }
1557    
1558    /**
1559     * Removes all table rows that meet the input predicate filter.
1560     * @param filter a predicate specifying which rows to remove from
1561     * the table.
1562     */

1563    public void remove(Predicate filter) {
1564        for ( IntIterator ii = rows(filter); ii.hasNext(); )
1565            removeRow(ii.nextInt());
1566    }
1567    
1568    // ------------------------------------------------------------------------
1569
// Iterators
1570

1571    /**
1572     * Return a TableIterator over the rows of this table.
1573     * @return a TableIterator over this table
1574     */

1575    public TableIterator iterator() {
1576        return iterator(rows());
1577    }
1578
1579    /**
1580     * Return a TableIterator over the given rows of this table.
1581     * @param rows an iterator over the table rows to visit
1582     * @return a TableIterator over this table
1583     */

1584    public TableIterator iterator(IntIterator rows) {
1585        return new TableIterator(this, rows);
1586    }
1587    
1588    /**
1589     * Get an iterator over the tuples in this table.
1590     * @return an iterator over the table tuples
1591     * @see prefuse.data.tuple.TupleSet#tuples()
1592     */

1593    public Iterator JavaDoc tuples() {
1594        return m_tuples.iterator(rows());
1595    }
1596    
1597    /**
1598     * Get an iterator over the tuples in this table in reverse order.
1599     * @return an iterator over the table tuples in reverse order
1600     */

1601    public Iterator JavaDoc tuplesReversed() {
1602        return m_tuples.iterator(rows(true));
1603    }
1604    
1605    /**
1606     * Get an iterator over the tuples for the given rows in this table.
1607     * @param rows an iterator over the table rows to visit
1608     * @return an iterator over the selected table tuples
1609     */

1610    public Iterator JavaDoc tuples(IntIterator rows) {
1611        return m_tuples.iterator(rows);
1612    }
1613    
1614    /**
1615     * Get an interator over the row numbers of this table.
1616     * @return an iterator over the rows of this table
1617     */

1618    public IntIterator rows() {
1619        return m_rows.rows();
1620    }
1621    
1622    /**
1623     * Get a filtered iterator over the row numbers of this table, returning
1624     * only the rows whose tuples match the given filter predicate.
1625     * @param filter the filter predicate to apply
1626     * @return a filtered iterator over the rows of this table
1627     */

1628    public IntIterator rows(Predicate filter) {
1629        return FilterIteratorFactory.rows(this, filter);
1630    }
1631    
1632    /**
1633     * Get an interator over the row numbers of this table.
1634     * @param reverse true to iterate in rever order, false for normal order
1635     * @return an iterator over the rows of this table
1636     */

1637    public IntIterator rows(boolean reverse) {
1638        return m_rows.rows(reverse);
1639    }
1640    
1641    /**
1642     * Get an iterator over the rows of this table, sorted by the given data
1643     * field. This method will create an index over the field if one does
1644     * not yet exist.
1645     * @param field the data field to sort by
1646     * @param ascend true if the iteration should proceed in an ascending
1647     * (lowest to highest) sort order, false for a descending order
1648     * @return the sorted iterator over rows of this table
1649     */

1650    public IntIterator rowsSortedBy(String JavaDoc field, boolean ascend) {
1651        Class JavaDoc type = getColumnType(field);
1652        Index index = getIndex(field, type, true);
1653        int t = ascend ? Index.TYPE_ASCENDING : Index.TYPE_DESCENDING;
1654        return index.allRows(t);
1655    }
1656    
1657    /**
1658     * Return an iterator over a range of rwos in this table, determined
1659     * by a bounded range for a given data field. A new index over the
1660     * data field will be created if it doesn't already exist.
1661     * @param field the data field for determining the bounded range
1662     * @param lo the minimum range value
1663     * @param hi the maximum range value
1664     * @param indexType indicate the sort order and inclusivity/exclusivity
1665     * of the range bounds, using the constants of the
1666     * {@link prefuse.data.util.Index} class.
1667     * @return an iterator over a range of table rows, determined by a
1668     * sorted bounded range of a data field
1669     */

1670    public IntIterator rangeSortedBy(String JavaDoc field, int lo, int hi, int indexType) {
1671        Index index = getIndex(field, int.class, true);
1672        return index.rows(lo, hi, indexType);
1673    }
1674    
1675    /**
1676     * Return an iterator over a range of rwos in this table, determined
1677     * by a bounded range for a given data field. A new index over the
1678     * data field will be created if it doesn't already exist.
1679     * @param field the data field for determining the bounded range
1680     * @param lo the minimum range value
1681     * @param hi the maximum range value
1682     * @param indexType indicate the sort order and inclusivity/exclusivity
1683     * of the range bounds, using the constants of the
1684     * {@link prefuse.data.util.Index} class.
1685     * @return an iterator over a range of table rows, determined by a
1686     * sorted bounded range of a data field
1687     */

1688    public IntIterator rangeSortedBy(String JavaDoc field, long lo, long hi, int indexType) {
1689        Index index = getIndex(field, long.class, true);
1690        return index.rows(lo, hi, indexType);
1691    }
1692    
1693    /**
1694     * Return an iterator over a range of rwos in this table, determined
1695     * by a bounded range for a given data field. A new index over the
1696     * data field will be created if it doesn't already exist.
1697     * @param field the data field for determining the bounded range
1698     * @param lo the minimum range value
1699     * @param hi the maximum range value
1700     * @param indexType indicate the sort order and inclusivity/exclusivity
1701     * of the range bounds, using the constants of the
1702     * {@link prefuse.data.util.Index} class.
1703     * @return an iterator over a range of table rows, determined by a
1704     * sorted bounded range of a data field
1705     */

1706    public IntIterator rangeSortedBy(String JavaDoc field, float lo, float hi, int indexType) {
1707        Index index = getIndex(field, float.class, true);
1708        return index.rows(lo, hi, indexType);
1709    }
1710    
1711    /**
1712     * Return an iterator over a range of rwos in this table, determined
1713     * by a bounded range for a given data field. A new index over the
1714     * data field will be created if it doesn't already exist.
1715     * @param field the data field for determining the bounded range
1716     * @param lo the minimum range value
1717     * @param hi the maximum range value
1718     * @param indexType indicate the sort order and inclusivity/exclusivity
1719     * of the range bounds, using the constants of the
1720     * {@link prefuse.data.util.Index} class.
1721     * @return an iterator over a range of table rows, determined by a
1722     * sorted bounded range of a data field
1723     */

1724    public IntIterator rangeSortedBy(String JavaDoc field, double lo, double hi, int indexType) {
1725        Index index = getIndex(field, double.class, true);
1726        return index.rows(lo, hi, indexType);
1727    }
1728    
1729    /**
1730     * Return an iterator over a range of rwos in this table, determined
1731     * by a bounded range for a given data field. A new index over the
1732     * data field will be created if it doesn't already exist.
1733     * @param field the data field for determining the bounded range
1734     * @param lo the minimum range value
1735     * @param hi the maximum range value
1736     * @param indexType indicate the sort order and inclusivity/exclusivity
1737     * of the range bounds, using the constants of the
1738     * {@link prefuse.data.util.Index} class.
1739     * @return an iterator over a range of table rows, determined by a
1740     * sorted bounded range of a data field
1741     */

1742    public IntIterator rangeSortedBy(String JavaDoc field, Object JavaDoc lo, Object JavaDoc hi, int indexType) {
1743        Class JavaDoc type = TypeLib.getSharedType(lo, hi);
1744        // TODO: check this for correctness
1745
if ( type == null )
1746            throw new IllegalArgumentException JavaDoc("Incompatible arguments");
1747        Index index = getIndex(field, type, true);
1748        return index.rows(lo, hi, indexType);
1749    }
1750    
1751    // ------------------------------------------------------------------------
1752
// Listener Methods
1753

1754    // -- ColumnListeners -----------------------------------------------------
1755

1756    /**
1757     * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, boolean)
1758     */

1759    public void columnChanged(Column src, int idx, boolean prev) {
1760        handleColumnChanged(src, idx, idx);
1761    }
1762
1763    /**
1764     * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, double)
1765     */

1766    public void columnChanged(Column src, int idx, double prev) {
1767        handleColumnChanged(src, idx, idx);
1768    }
1769
1770    /**
1771     * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, float)
1772     */

1773    public void columnChanged(Column src, int idx, float prev) {
1774        handleColumnChanged(src, idx, idx);
1775    }
1776
1777    /**
1778     * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, int)
1779     */

1780    public void columnChanged(Column src, int idx, int prev) {
1781        handleColumnChanged(src, idx, idx);
1782    }
1783
1784    /**
1785     * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, long)
1786     */

1787    public void columnChanged(Column src, int idx, long prev) {
1788        handleColumnChanged(src, idx, idx);
1789    }
1790
1791    /**
1792     * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, java.lang.Object)
1793     */

1794    public void columnChanged(Column src, int idx, Object JavaDoc prev) {
1795        handleColumnChanged(src, idx, idx);
1796    }
1797
1798    /**
1799     * @see prefuse.data.event.ColumnListener#columnChanged(prefuse.data.column.Column, int, int, int)
1800     */

1801    public void columnChanged(Column src, int type, int start, int end) {
1802        handleColumnChanged(src, start, end);
1803    }
1804    
1805    /**
1806     * Handle a column change event.
1807     * @param c the modified column
1808     * @param start the starting row of the modified range
1809     * @param end the ending row (inclusive) of the modified range
1810     */

1811    protected void handleColumnChanged(Column c, int start, int end) {
1812        for ( ; !isValidRow(start) && start <= end; ++start );
1813        if ( start > end ) return; // bail if no valid rows
1814

1815        // determine the index of the updated column
1816
int idx;
1817        if ( m_lastCol != -1 && c == getColumn(m_lastCol) ) {
1818            // constant time
1819
idx = m_lastCol;
1820        } else {
1821            // linear time
1822
idx = getColumnNumber(c);
1823        }
1824        
1825        // if we have a valid index, fire a notification
1826
if ( idx >= 0 ) {
1827            fireTableEvent(start, end, idx, TableModelEvent.UPDATE);
1828        }
1829    }
1830    
1831    // -- TableListeners ------------------------------------------------------
1832

1833    /**
1834     * Add a table listener to this table.
1835     * @param listnr the listener to add
1836     */

1837    public void addTableListener(TableListener listnr) {
1838        if ( !m_listeners.contains(listnr) )
1839            m_listeners.add(listnr);
1840    }
1841
1842    /**
1843     * Remove a table listener from this table.
1844     * @param listnr the listener to remove
1845     */

1846    public void removeTableListener(TableListener listnr) {
1847        m_listeners.remove(listnr);
1848    }
1849    
1850    /**
1851     * Fire a table event to notify listeners.
1852     * @param row0 the starting row of the modified range
1853     * @param row1 the ending row (inclusive) of the modified range
1854     * @param col the number of the column modified, or
1855     * {@link prefuse.data.event.EventConstants#ALL_COLUMNS} for operations
1856     * effecting all columns.
1857     * @param type the table modification type, one of
1858     * {@link prefuse.data.event.EventConstants#INSERT},
1859     * {@link prefuse.data.event.EventConstants#DELETE}, or
1860     * {@link prefuse.data.event.EventConstants#UPDATE}.
1861     */

1862    protected void fireTableEvent(int row0, int row1, int col, int type) {
1863        // increment the modification count
1864
++m_modCount;
1865        
1866        if ( type != EventConstants.UPDATE &&
1867             col == EventConstants.ALL_COLUMNS )
1868        {
1869            // fire event to all tuple set listeners
1870
fireTupleEvent(this, row0, row1, type);
1871        }
1872        
1873        if ( !m_listeners.isEmpty() ) {
1874            // fire event to all table listeners
1875
Object JavaDoc[] lstnrs = m_listeners.getArray();
1876            for ( int i=0; i<lstnrs.length; ++i ) {
1877                ((TableListener)lstnrs[i]).tableChanged(
1878                        this, row0, row1, col, type);
1879            }
1880        }
1881    }
1882    
1883    // ------------------------------------------------------------------------
1884
// String Methods
1885

1886    /**
1887     * @see java.lang.Object#toString()
1888     */

1889    public String JavaDoc toString() {
1890        StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
1891        sbuf.append("Table[");
1892        sbuf.append("rows=").append(getRowCount());
1893        sbuf.append(", cols=").append(getColumnCount());
1894        sbuf.append(", maxrow=").append(m_rows.getMaximumRow());
1895        sbuf.append("]");
1896        return sbuf.toString();
1897    }
1898    
1899    // ------------------------------------------------------------------------
1900
// ColumnEntry helper
1901

1902    /**
1903     * Helper class that encapsulates a map entry for a column, including the
1904     * column itself and its metadata and index.
1905     *
1906     * @author <a HREF="http://jheer.org">jeffrey heer</a>
1907     */

1908    protected static class ColumnEntry {
1909
1910        /** The column number. */
1911        public int colnum;
1912        /** The Column instance. */
1913        public Column column;
1914        /** The column metadata instance. */
1915        public ColumnMetadata metadata;
1916        /** The column Index instance. */
1917        public Index index;
1918        
1919        /**
1920         * Create a new ColumnEntry.
1921         * @param col the column number
1922         * @param column the Column instance
1923         * @param metadata the ColumnMetadata instance
1924         */

1925        public ColumnEntry(int col, Column column, ColumnMetadata metadata) {
1926            this.colnum = col;
1927            this.column = column;
1928            this.metadata = metadata;
1929            this.index = null;
1930        }
1931        
1932        /**
1933         * Dispose of this column entry, disposing of any allocated
1934         * metadata or index instances.
1935         */

1936        public void dispose() {
1937            if ( metadata != null )
1938                metadata.dispose();
1939            if ( index != null )
1940                index.dispose();
1941        }
1942
1943    } // end of inner class ColumnEntry
1944

1945} // end of class Table
1946
Popular Tags