KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdesktop > dataset > DataTable


1 /*
2  * $Id: DataTable.java,v 1.2 2005/02/27 00:24:55 rbair Exp $
3  *
4  * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
5  * Santa Clara, California 95054, U.S.A. All rights reserved.
6  */

7
8 package org.jdesktop.dataset;
9 import java.beans.PropertyChangeEvent JavaDoc;
10 import java.beans.PropertyChangeListener JavaDoc;
11 import java.beans.PropertyChangeSupport JavaDoc;
12 import java.util.ArrayList JavaDoc;
13 import java.util.Collections JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.LinkedHashMap JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Map JavaDoc;
18 import java.util.logging.Logger JavaDoc;
19 import org.jdesktop.dataset.NameGenerator;
20 import org.jdesktop.dataset.event.DataTableListener;
21 import org.jdesktop.dataset.event.TableChangeEvent;
22
23
24
25 /**
26  *
27  * @author rbair
28  */

29 public class DataTable {
30     /**
31      * The Logger
32      */

33     private static final Logger JavaDoc LOG = Logger.getLogger(DataTable.class.getName());
34     
35     //protected for testing
36
protected static final String JavaDoc DEFAULT_NAME_PREFIX = "DataTable";
37     private static final NameGenerator NAMEGEN = new NameGenerator(DEFAULT_NAME_PREFIX);
38
39     //used for communicating changes to this JavaBean, especially necessary for
40
//IDE tools, but also handy for any other component listening to this table
41
private PropertyChangeSupport JavaDoc pcs = new PropertyChangeSupport JavaDoc(this);
42
43     /**
44      * A reference to the DataSet to which this DataTable belongs
45      */

46     private DataSet dataSet;
47     /**
48      * The DataProvider that is used to manage this DataTables data. It is
49      * technically possible for a DataProvider to populate a DataTable to which
50      * it is not associated, but the results are undefined
51      */

52     private DataProvider dataProvider;
53     /**
54      * The name of this DataTable
55      */

56     private String JavaDoc name;
57     /**
58      * Mapping of DataColumn.name to DataColumn. Extremely useful for
59      * discovering the column object associated with a name. The resolver code
60      * responsible for locating a particular data cell will first convert the
61      * column name to a DataColumn, and then use the DataColumn in the hash for
62      * locating the data. This is done so that:
63      * <ol>
64      * <li>The lookup is constant time (or close to it)</li>
65      * <li>Avoid looping through a columns list looking for the column</li>
66      * <li>More efficient handling if the column name changes in a large table,
67      * which would not be possible if the column name was used in the hash</li>
68      * </ol>
69      */

70     protected Map JavaDoc<String JavaDoc,DataColumn> columns = new LinkedHashMap JavaDoc<String JavaDoc,DataColumn>();
71     /**
72      * The list of DataRows in this DataTable. The DataRow actually contains the
73      * data at the various cells.
74      */

75     protected List JavaDoc<DataRow> rows = new ArrayList JavaDoc<DataRow>();
76     /**
77      * Every DataTable contains a list of selectors, which manage tracking
78      * "selected" state in a DataTable. This is necessary for proper handling
79      * of master/detail relationships, also known as parent/child. This data
80      * structure maps the data selectors name to the selector itself.
81      */

82     protected Map JavaDoc<String JavaDoc,DataSelector> selectors = new HashMap JavaDoc<String JavaDoc,DataSelector>();
83     /**
84      * Indicates whether deleting rows is supported. Attempts to
85      * delete a row when row deletion is not supported will be ignored
86      */

87     private boolean deleteRowSupported = true;
88     /**
89      * Indicates whether appending rows is supported. Attempts to append a
90      * row when row appending is not supported will be ignored.
91      */

92     private boolean appendRowSupported = true;
93     /**
94      * A list of DataTableListeners to notify of various events
95      */

96     private List JavaDoc<DataTableListener> listeners = new ArrayList JavaDoc<DataTableListener>();
97     
98     /**
99      * A PropertyChangeListener for listening to name property change events
100      * on DataSelectors and DataColumns. The listener makes sure that the name
101      * used as the hash key in the selectors and columns maps is always
102      * accurate.
103      */

104     private final PropertyChangeListener JavaDoc nameChangeListener = new PropertyChangeListener JavaDoc() {
105         public void propertyChange(PropertyChangeEvent JavaDoc evt) {
106             if (evt.getSource() instanceof DataSelector) {
107                 DataSelector sel = selectors.remove(evt.getOldValue());
108                 if (sel != null) {
109                     //name changed
110
selectors.put((String JavaDoc)evt.getNewValue(), sel);
111                 }
112             } else if (evt.getSource() instanceof DataColumn) {
113                 DataColumn c = columns.remove(evt.getOldValue());
114                 if (c != null) {
115                     //name changed
116
columns.put((String JavaDoc)evt.getNewValue(), c);
117                 }
118             }
119         }
120     };
121     
122     /**
123      * Create a new DataTable
124      */

125     protected DataTable(DataSet ds) {
126         assert ds != null;
127         this.dataSet = ds;
128         name = NAMEGEN.generateName(this);
129     }
130     
131     /**
132      * creates a new DataColumn, and adds it to this DataTable. A name will
133      * be automatically generated for the DataColumn.
134      *
135      * @return the DataColumn that was created
136      */

137     public DataColumn createColumn() {
138         return createColumn(null);
139     }
140     
141     /**
142      * Creates a new DataColumn with the given name, and adds it to this
143      * DataTable.
144      *
145      * @param colName the name to give the DataColumn. If the name is invalid
146      * or has already been used in the DataSet, an assertion will be raised
147      *
148      * @return the new DataColumn
149      */

150     public DataColumn createColumn(String JavaDoc colName) {
151         DataColumn col = new DataColumn(this);
152         if (colName != null) {
153             col.setName(colName);
154         }
155         columns.put(col.getName(), col);
156         col.addPropertyChangeListener("name", nameChangeListener);
157         
158         fireColumnAdded(col);
159         return col;
160     }
161     
162     /**
163      * Drops the column with the given name. If there is no column by the
164      * specified name, then nothing is done.
165      *
166      * @param colName
167      */

168     public void dropColumn(String JavaDoc colName) {
169         DataColumn col = columns.remove(colName);
170         if (col != null) {
171             col.removePropertyChangeListener("name", nameChangeListener);
172             fireColumnRemoved(col);
173         }
174     }
175     
176     /**
177      * @return a List of DataColumns representing all of the columns in this
178      * DataTable.
179      */

180     public List JavaDoc<DataColumn> getColumns() {
181         return Collections.unmodifiableList(new ArrayList JavaDoc<DataColumn>(columns.values()));
182     }
183     
184     /**
185      * @param colName the name of the column that you want to retrieve
186      * @return the DataColumn with the given name. If the given name does not
187      * map to a DataColumn in this DataTable, then null is returned.
188      */

189     public DataColumn getColumn(String JavaDoc colName) {
190         return columns.get(colName);
191     }
192     
193     /**
194      * @return an unmodifiable list of all of the rows in this DataTable. The
195      * individual DataRow elements are modifiable, but the List is not.
196      */

197     public List JavaDoc<DataRow> getRows() {
198         return Collections.unmodifiableList(rows);
199     }
200     
201     /**
202      * @param index the Index in this table associated with the DataRow to be
203      * retrieved. This must be &gt;0, and &lt;rows.size()
204      * @return the DataRow at the given 0 based index. The index must be valid
205      */

206     public DataRow getRow(int index) {
207         assert index > 0 && index < rows.size();
208         return rows.get(index);
209     }
210     
211     /**
212      * Creates and returns a selector. The DataSelector will have a generated
213      * name by default.
214      */

215     public DataSelector createSelector() {
216         return createSelector(null);
217     }
218     
219     /**
220      * Creates and returns a selector with the given name. If the name is
221      * invalid, an assertion will be raised
222      * @param name the name for the new DataSelector
223      */

224     public DataSelector createSelector(String JavaDoc name) {
225         DataSelector sel = new DataSelector(this);
226         if (name != null) {
227             sel.setName(name);
228         }
229         selectors.put(sel.getName(), sel);
230         sel.addPropertyChangeListener("name", nameChangeListener);
231         return sel;
232     }
233     
234     /**
235      * @return a List of DataSelectors associated with this DataTable.
236      */

237     public List JavaDoc<DataSelector> getSelectors() {
238         return Collections.unmodifiableList(new ArrayList JavaDoc<DataSelector>(selectors.values()));
239     }
240     
241     /**
242      * @param name the name of the selector to create or return
243      * @return the DataSelector with the given name. If no such DataSelector
244      * exists, then a new DataSelector will be created and added to this
245      * DataTable by the given name.
246      */

247     public DataSelector getSelector(String JavaDoc name) {
248         //if the given selector doesn't exist, create it implicitly
249
if (!selectors.containsKey(name)) {
250             return createSelector(name);
251         } else {
252             return selectors.get(name);
253         }
254     }
255     
256     /**
257      * Drop the given selector
258      * @param the selector to drop
259      */

260     public void dropSelector(DataSelector selector) {
261         dropSelector(selector.getName());
262     }
263     
264     /**
265      * drops the given named selector. If a selector by that name does not
266      * exist, then do nothing
267      * @param selectorName the name of the selector to drop
268      */

269     public void dropSelector(String JavaDoc selectorName) {
270         DataSelector sel = selectors.remove(selectorName);
271         if (sel != null) {
272             sel.removePropertyChangeListener("name", nameChangeListener);
273             fireSelectorRemoved(sel);
274         }
275     }
276     
277     /**
278      * Sets the given name to be the name of this DataTable
279      * @param name
280      */

281     public void setName(String JavaDoc name) {
282         if (this.name != name) {
283             assert DataSetUtils.isValidName(name);
284             assert !dataSet.hasElement(name);
285             String JavaDoc oldName = this.name;
286             this.name = name;
287             pcs.firePropertyChange("name", oldName, name);
288         }
289     }
290
291     /**
292      * @return the name of this DataTable
293      */

294     public String JavaDoc getName() {
295         return name;
296     }
297
298     /**
299      * @return true if deletion of rows is supported
300      */

301     public boolean isDeleteRowSupported() {
302         return deleteRowSupported;
303     }
304
305     /**
306      * Sets the deleteRowSupported flag
307      * @param deleteRowSupported the new value for deleteRowSupported
308      */

309     public void setDeleteRowSupported(boolean deleteRowSupported) {
310         if (this.deleteRowSupported != deleteRowSupported) {
311             boolean oldValue = this.deleteRowSupported;
312             this.deleteRowSupported = deleteRowSupported;
313             pcs.firePropertyChange("deleteRowSupported", oldValue, deleteRowSupported);
314         }
315     }
316
317     /**
318      * @return true if appending rows is supported
319      */

320     public boolean isAppendRowSupported() {
321         return appendRowSupported;
322     }
323
324     /**
325      * Sets whether appending rows is supported
326      * @param appendRowSupported the new value for appendRowSupported
327      */

328     public void setAppendRowSupported(boolean appendRowSupported) {
329         if (this.appendRowSupported != appendRowSupported) {
330             boolean oldValue = this.appendRowSupported;
331             this.appendRowSupported = appendRowSupported;
332             pcs.firePropertyChange("appendRowSupported", oldValue, appendRowSupported);
333         }
334     }
335
336     /**
337      * @return the DataProvider for this DataTable. May be null
338      */

339     public DataProvider getDataProvider() {
340         return dataProvider;
341     }
342
343     /**
344      * Sets the DataProvider for this DataTable.
345      * @param dataProvider the DataProvider for this DataTable. This may be null.
346      */

347     public void setDataProvider(DataProvider dataProvider) {
348         if (this.dataProvider != dataProvider) {
349             DataProvider oldValue = this.dataProvider;
350             this.dataProvider = dataProvider;
351             pcs.firePropertyChange("dataProvider", oldValue, dataProvider);
352         }
353     }
354     
355     /**
356      * @return the DataSet that is associated with this DataTable
357      */

358     public DataSet getDataSet() {
359         return dataSet;
360     }
361
362     /**
363      * Append a new DataRow to this DataTable, and return the newly appended
364      * Row. If appendRowSupported is false, then this method returns null
365      * @return null if !appendRowSupported, else the newly created row
366      */

367     public DataRow appendRow() {
368         DataRow row = appendRowNoEvent();
369         if (row != null) {
370             fireRowAdded(row);
371         }
372         return row;
373     }
374     
375     /**
376      * Appends a new DataRow to this DataTable, and returns the newly appended
377      * row. This method differs from #appendRow in that it does not fire
378      * any event. This is useful to the DataProvider, which will be adding many
379      * rows and may not want many event notifications
380      */

381     public DataRow appendRowNoEvent() {
382         if (appendRowSupported) {
383             DataRow row = new DataRow(this);
384             rows.add(row);
385             return row;
386         } else {
387             return null;
388         }
389     }
390
391     /**
392      * Doesn't actually remove the row, just marks it for deletion. If
393      * deletion of rows is not supported, nothing happens.
394      * @param rowIndex the index of the row to delete. If the index is invalid,
395      * an assertion will be raised
396      */

397     public void deleteRow(int rowIndex) {
398         assert rowIndex > 0 && rowIndex < rows.size();
399         if (deleteRowSupported) {
400             rows.get(rowIndex).setStatus(DataRow.DataRowStatus.DELETED);
401         }
402     }
403     
404     /**
405      * Loads this DataTable using this tables DataProvider. If DataProvider is
406      * null, then nothing is loaded. This method <b>does not</b> clear out the
407      * DataTable prior to loading. Calling load() <i>n</i> times will cause the
408      * DataTable to contain <i>rowCount * n</i> rows to be added.
409      */

410     public void load() {
411         if (dataProvider != null) {
412             dataProvider.load(this);
413             fireDataTableChanged(new TableChangeEvent(this));
414         }
415     }
416    
417     /**
418      * Saves this DataTable to the underlying DataStore. This calls the
419      * DataProviders save method. If no DataProvider is specified, then nothing
420      * is done.
421      */

422     public void save() {
423         if (dataProvider != null) {
424             dataProvider.save(this);
425         }
426     }
427     
428     /**
429      * Clears all of the rows from this DataTable. <em>If any rows were altered,
430      * these changes are lost!</em>. Call #save to save the changes before
431      * clearing. An event is posted indicating that the rows in this DataTable
432      * have changed.
433      */

434     public void clear() {
435         rows.clear();
436         fireDataTableChanged(new TableChangeEvent(this));
437     }
438     
439     /**
440      * Refreshes the DataSet. This is symantically the same as:
441      * <code>
442      * clear();
443      * load();
444      * </code>
445      */

446     public void refresh() {
447         clear();
448         load();
449     }
450     
451     /**
452      * @return the value at the given rowIndex, for the given columnName. If
453      * either the index is invalid or the columnName, assertions will be raised.
454      * @param index the row index of the row that you want to retrieve a value
455      * for
456      * @param columnName the name of the column who's value you want retrieved
457      */

458     public Object JavaDoc getValue(int index, String JavaDoc columnName) {
459         assert index > 0 && index < rows.size();
460         assert columns.containsKey(columnName);
461         return rows.get(index).getValue(columnName);
462     }
463
464     /**
465      * Sets the field value at the given row idnex and column to the given
466      * value. If either the index is invalid or the columnName, assertions will
467      * be raised.
468      * @param index
469      * @param columnName
470      * @param value
471      */

472     public void setValue(int index, String JavaDoc columnName, Object JavaDoc value) {
473         assert index > 0 && index < rows.size();
474         assert columns.containsKey(columnName);
475         rows.get(index).setValue(columnName, value);
476     }
477
478     /**
479      * Retrieves the data value for the given row and column.
480      *
481      * @param row The DataRow to retrieve the value from. This row must be
482      * a member of this table, or an assertion will be raised. If null, a
483      * NullPointerException will be thrown.
484      *
485      * @param col The DataColumn to retrieve the value from. This col must
486      * be a member of this table, or an assertion will be raised. If null, a
487      * NullPointerException will be thrown.
488      */

489     public Object JavaDoc getValue(DataRow row, DataColumn col) {
490         assert row.getTable() == this;
491         assert col.getTable() == this;
492         return row.getValue(col);
493     }
494
495     public void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
496         pcs.addPropertyChangeListener(listener);
497     }
498     
499     public void addPropertyChangeListener(String JavaDoc property, PropertyChangeListener JavaDoc listener) {
500         pcs.addPropertyChangeListener(property, listener);
501     }
502     
503     public void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
504         pcs.removePropertyChangeListener(listener);
505     }
506     
507     public void removePropertyChangeListener(String JavaDoc propertyName, PropertyChangeListener JavaDoc listener) {
508         pcs.removePropertyChangeListener(propertyName, listener);
509     }
510     
511     public void addDataTableListener(DataTableListener listener) {
512         if (!listeners.contains(listener)) {
513             listeners.add(listener);
514         }
515     }
516     
517     public void removeDataTableListener(DataTableListener listener) {
518         listeners.remove(listener);
519     }
520     
521     protected void fireColumnAdded(DataColumn col) {
522         fireDataTableChanged(new TableChangeEvent(this));
523     }
524     
525     protected void fireRowAdded(DataRow row) {
526         fireDataTableChanged(new TableChangeEvent(this));
527     }
528     
529     public void fireDataTableChanged(TableChangeEvent evt) {
530         for (DataTableListener listener : listeners) {
531             listener.tableChanged(evt);
532         }
533     }
534     
535     protected void fireSelectorRemoved(DataSelector sel) {
536         fireDataTableChanged(new TableChangeEvent(this));
537     }
538     
539     protected void fireColumnRemoved(DataColumn col) {
540         fireDataTableChanged(new TableChangeEvent(this));
541     }
542     
543     /**
544      * Internal method that returns the int index of the given DataRow. This is
545      * currently only used for constructing toString on DataRow, and testing.
546      */

547     protected int indexOfRow(DataRow row) {
548         return rows.indexOf(row);
549     }
550 }
Popular Tags