KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdesktop > swing > data > DefaultTableModelExt


1 /*
2  * $Id: DefaultTableModelExt.java,v 1.2 2005/01/27 14:40:26 kleopatra Exp $
3  *
4  * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
5  * Santa Clara, California 95054, U.S.A. All rights reserved.
6  */

7
8 package org.jdesktop.swing.data;
9
10 import org.jdesktop.swing.data.MetaData;
11
12 import org.jdesktop.swing.event.MessageEvent;
13 import org.jdesktop.swing.event.MessageListener;
14 import org.jdesktop.swing.event.ProgressEvent;
15 import org.jdesktop.swing.event.ProgressListener;
16
17 import java.beans.PropertyChangeEvent JavaDoc;
18 import java.beans.PropertyChangeListener JavaDoc;
19 import java.beans.PropertyChangeSupport JavaDoc;
20
21 import java.io.InputStream JavaDoc;
22 import java.io.IOException JavaDoc;
23
24 import java.net.MalformedURLException JavaDoc;
25 import java.net.URL JavaDoc;
26
27 import java.util.List JavaDoc;
28 import java.util.Vector JavaDoc;
29
30 import javax.swing.event.TableModelEvent JavaDoc;
31 import javax.swing.event.TableModelListener JavaDoc;
32 import javax.swing.table.AbstractTableModel JavaDoc;
33
34
35 /**
36  * Class used to represent a tabular data model which holds 2 dimensional
37  * data. This class provides a simple alternative to <code>javax.sql.RowSet</code> and
38  * is intended for use when the program is not obtaining its data directly
39  * from a JDBC data source. Programs which are interfacing with databases using
40  * JDBC should use the <code>RowSet</code> API instead.</p>
41  * <p>
42  * A tabular data model is structured with a fixed number of columns where
43  * each column is represented by an integer index. The first
44  * column's index is 0 and the last column's index is columnCount - 1. Each
45  * column in the tabular data model has a <code>MetaData</code> instance which describes the
46  * column's data-type and edit constraints.</p>
47  * <p>
48  * The structure of the model (number of columns and associated column meta-data
49  * which describes those columns) must be initialized before the rows of data
50  * are loaded into the model. The structure can be obtained directly
51  * from the data source (if set) or it can be set explicitly by the program.
52  * The following example configures the table's structure explicitly:
53  * <pre><code>
54  * DefaultTableModelExt data = new DefaultTableModelExt();
55  * data.setColumnCount(3);
56  *
57  * MetaData columnMetaData = data.getColumnMetaData(0);
58  * columnMetaData.setName("firstname");
59  * columnMetaData = data.getColumnMetaData(1);
60  * columnMetaData.setName("lastname");
61  * columnMetaData = data.getColumnMetaData(2);
62  * columnMetaData.setName("employeeid");
63  * columnMetaData.setElementClass(Integer.class);
64  * </code></pre>
65  * <p>
66  * If the meta-data properties for each column are not explicitly set, the type
67  * of each column will default to <code>java.lang.String</code>.</p>
68  * <p>
69  * The data for the model may be loaded either by explicitly invoking the
70  * <code>addRows</code> or <code>insertRows</code> method, or by setting the &quot;source&quot; property, in
71  * which case the tabular data model will handle streaming the data in from that
72  * source when <code>startLoading</code> is called. The format of the data at that
73  * source URL must be readible by the DataLoader instance set as the &quot;loader&quot;
74  * property. By default the loader is set to a <code>TableModelExtTextLoader</code> instance,
75  * which can read the data in simple ascii format where the columns are
76  * separated by configurable regular expression delimeters. Examples of this format are
77  * tab-separated-values (TSV) and comma-separated-values (CSV). If the data at the
78  * source is coming in an alternate format, the caller is responsible for setting
79  * an appropriate loader object before invoking <code>startLoading</code>.</p>
80  * <p>
81  * If the data for the above example was available from a tsv file, the tabular
82  * data model could be loaded with the following code:
83  * <pre><code>
84  * data.setSource("file:///D:/acme/employees.tsv");
85  * data.startLoading();
86  * </code></pre>
87  * </p>
88  * <p>
89  * It is often desirable to determine the structure of the tabular data model
90  * (number of columns, types, etc) from the source rather than setting it explicitly.
91  * The amount of structural information available from a source will depend on
92  * the format of the data. The plain text formats do not typically contain meta-data
93  * information beyond optional column names encoded as the first row, and thus
94  * it is desirable to explicitly configure the column meta-data for these simple
95  * formats.</p>
96  * <p>
97  * Once the data has been loaded into the model, values at a specific row and
98  * column may be retreived or modified. For example:
99  * <pre><code>
100  * int rowCount = data.getRowCount();
101  * for (int i = 0; i < rowCount; i++) {
102  * <b>Float payrate = (Float)data.getValueAt(i, 4);</b>
103  * if (payrate < 10.00) {
104  * // underpaid - give raise
105  * <b>data.setValueAt(i, 4, new Float(payrate*.05));</b>
106  * }
107  * }
108  * </code></pre>
109  * </p>
110  * Additionally, rows can be inserted or deleted. Example:
111  * <pre><code>
112  * // hired
113  * Object employee[] = new Object[4];
114  * employee[0] = "Einstein";
115  * employee[1] = "Albert";
116  * employee[2] = new Integer(430);
117  * employee[3] = new Float(12.50);
118  * <b>data.insertRow(5, employee);</b>
119  *
120  * // fired
121  * <b>data.deleteRow(6);</b>
122  * </code></pre>
123  *
124  * <p>
125  * This class is also an instance of <code>javax.swing.table.TableModel</code> and thus
126  * can be set as the model for any <code>javax.swing.JTable</code> component instance.</p>
127  * <p>
128  * DefaultTableModelExt generates table model events when either its structure or
129  * values are modified. To listen for these events, register a <code>TableModelListener</code>
130  * instance. Example:
131  * <pre><code>
132  * DefaultTableModelExt data = new DefaultTableModelExt("http://www.foo.com/barQuery");
133  * <b>data.addTableModelListener(new TableModelListener() {
134  * public void tableChanged(TableModelEvent e) {
135  * // handle tabular data model change
136  * }
137  * });</b>
138  * </code></pre>
139  * </p>
140  *
141  * @see MetaData
142  * @see javax.swing.table.TableModel
143  * @see javax.swing.event.TableModelEvent
144  * @see javax.swing.event.TableModelListener
145  * @see TableModelExtTextLoader
146  *
147  * @author Amy Fowler
148  * @version 1.0
149  */

150 public class DefaultTableModelExt extends AbstractTableModel JavaDoc implements MetaDataProvider {
151
152     private URL JavaDoc sourceURL;
153
154     //private DataLoader loader;
155
private DataLoader loader;
156
157     private MetaData[] columnMetaData;
158     private Vector JavaDoc rows;
159
160     private boolean loading = false;
161     private boolean readOnly = false;
162
163     private PropertyChangeListener JavaDoc columnMetaDataListener;
164
165     private PropertyChangeSupport JavaDoc pcs;
166
167     /**
168      * Creates a new tabular data model with 0 columns.
169      */

170     public DefaultTableModelExt() {
171         this(0);
172     }
173
174     /**
175      * Creates a new tabular data model with the specified number of
176      * columns.
177      * @throws IllegalArgumentException if columnCount < 0
178      * @param columnCount integer indicating the number of columns in this
179      * tabular data model
180      */

181     public DefaultTableModelExt(int columnCount) {
182         init();
183         setColumnCount(columnCount);
184     }
185
186     /**
187      * Creates a new tabular data model configured to obtain its data
188      * in plain text format from the specified URL.
189      * @param urlName String containing the name of the URL which provides the
190      * data for this tabular data model
191      * @throws MalformedURLException if urlName cannot be converted to a valid URL
192      */

193     public DefaultTableModelExt(String JavaDoc urlName) throws MalformedURLException JavaDoc {
194         this();
195         setSource(urlName);
196     }
197
198     /**
199       * Create a new tabular data model configured to obtain its data
200       * in plain text format from the specified URL.
201       * @param sourceURL URL which provides the data for this tabular data model
202       */

203      public DefaultTableModelExt(URL JavaDoc sourceURL) {
204          this();
205          setSource(sourceURL);
206      }
207
208      /**
209       * Creates a new tabular data model configured to obtain its data
210       * in plain text format from the specified URL. The number of columns
211       * will be initialized to the specified column count.
212       * @param urlName String containing the name of the URL which provides the
213       * data for this tabular data model
214       * @param columnCount integer indicating the number of columns in this
215       * tabular data model
216       * @throws IllegalArgumentException if columnCount < 0
217       * @throws MalformedURLException if urlName cannot be converted to a valid URL
218       */

219      public DefaultTableModelExt(String JavaDoc urlName, int columnCount) throws MalformedURLException JavaDoc {
220          this(columnCount);
221          setSource(urlName);
222      }
223
224      private void init() {
225          columnMetaDataListener = new MetaDataChangeListener();
226          pcs = new PropertyChangeSupport JavaDoc(this);
227          setLoader(new TableModelExtTextLoader());
228      }
229
230     /**
231      * Sets the &quot;source&quot; URL property after converting the URL string
232      * to a <code>URL</code> instance.
233      * @see #getSource
234      * @param urlName String containing the name of the URL which provides the
235      * data for this tabular data model
236      * @throws MalformedURLException if urlName cannot be converted to a valid URL
237      */

238     public void setSource(String JavaDoc urlName) throws MalformedURLException JavaDoc {
239         setSource(new URL JavaDoc(urlName));
240     }
241
242     /**
243      * Sets the &quot;source&quot; property.
244      * @see #getSource
245      * @param sourceURL URL which provides the data for this tabular data model
246      */

247     public void setSource(URL JavaDoc sourceURL) {
248         URL JavaDoc oldSource = this.sourceURL;
249         this.sourceURL = sourceURL;
250         pcs.firePropertyChange("source", oldSource, sourceURL);
251     }
252
253     /**
254      * @see #setSource
255      * @return URL which provides the data for this tabular data model
256      */

257     public URL JavaDoc getSource() {
258         return sourceURL;
259     }
260
261     /**
262      * Sets the data loader object to be used to stream in the data from the source URL.
263      * @see #getLoader
264      * @param loader DataLoader object which can read data in the format provided
265      * from the source
266      */

267     public void setLoader(DataLoader loader) {
268         DataLoader oldLoader = this.loader;
269         this.loader = loader;
270         pcs.firePropertyChange("loader", oldLoader, loader);
271     }
272
273     /**
274      * Returns a TableModelExtTextLoader instance by default, which can read
275      * data in plain text format where rows are separated by newlines and
276      * columns within each row are separated by the tab character (delimeters
277      * are configurable).
278      * @see TableModelExtTextLoader
279      * @see #setLoader
280      * @return DataLoader object which can read data in the format provided
281      * from the source
282      */

283     public DataLoader getLoader() {
284         return loader;
285     }
286
287     /**
288      * Returns the number of columns in this tabular data model.
289      * If the data source is non-null and the number of columns has not yet
290      * been initialized, this method will attempt to initialize the number
291      * of columns by reading any available meta-data from the source.
292      * @return integer indicating the number of columns in this
293      * tabular data model
294      */

295     public int getColumnCount() {
296         synchronized(this) {
297             ensureMetaDataInitialized();
298             return columnMetaData.length;
299         }
300     }
301
302     /**
303      * Initializes the number of columns in this tabular data model and
304      * creates new instances of column MetaData objects for each column.
305      * Each column meta-data object will default to type String and will
306      * have a column name &quot;column+&lt;columnIndex&gt;&quot;.
307      * This method will also cause any existing rows of data to be cleared
308      * so that rowCount will be 0.
309      * @throws IllegalArgumentException if columnCount < 0
310      * @param columnCount integer indicating the number of columns in this
311      * tabular data model
312      */

313     public void setColumnCount(int columnCount) {
314         if (columnCount < 0) {
315             throw new IllegalArgumentException JavaDoc("column count, " + columnCount +
316                                                " is < 0");
317         }
318         int oldColumnCount = columnMetaData != null ? columnMetaData.length :
319                 0;
320         synchronized(this) {
321             if (columnMetaData != null) {
322                 for (int i = 0; i < columnMetaData.length; i++) {
323                     columnMetaData[i].removePropertyChangeListener(
324                         columnMetaDataListener);
325                 }
326             }
327             columnMetaData = new MetaData[columnCount];
328             for (int i = 0; i < columnMetaData.length; i++) {
329                 columnMetaData[i] = new MetaData("column" + i);
330                 columnMetaData[i].addPropertyChangeListener(
331                     columnMetaDataListener);
332             }
333             rows = new Vector JavaDoc();
334         }
335         fireTableStructureChanged();
336         pcs.firePropertyChange("columnCount", oldColumnCount, columnCount);
337     }
338
339     /**
340      * Returns the meta-data for the specified column.
341      * If the data source is non-null and the number of columns has not yet
342      * been initialized, this method will attempt to initialize the number
343      * of columns by reading any available meta-data from the source.
344      * @throws IndexOutOfBoundsException if
345      * columnIndex < 0 or columnIndex >= columnCount
346      * @param columnIndex integer index indicating column in tabular data model
347      * @return MetaData object describing the data element at the specified
348      * column
349      */

350     public MetaData getColumnMetaData(int columnIndex) {
351         synchronized(this) {
352             ensureMetaDataInitialized();
353             return columnMetaData[columnIndex];
354         }
355     }
356
357 //-------------------------- implementing MetaDataProvider
358
/**
359      *
360      * @param columnName String containing the id for the column
361      * @return MetaData object describing the data element at the specified column
362      */

363     public MetaData getMetaData(String JavaDoc columnName) {
364         int columnIndex = getColumnIndex(columnName);
365         return getColumnMetaData(columnIndex);
366     }
367
368     /**
369      * If the data source is non-null and the number of columns has not yet
370      * been initialized, this method will attempt to initialize the number
371      * of columns by reading any available meta-data from the source.
372      *
373      * @return array containing the MetaData objects for each column in the
374      * tabular data model
375      */

376     public MetaData[] getMetaData() {
377         ensureMetaDataInitialized();
378         MetaData metaDataCopy[] = new MetaData[columnMetaData.length];
379         System.arraycopy(columnMetaData, 0, metaDataCopy, 0, columnMetaData.length);
380         return metaDataCopy;
381     }
382
383
384     public String JavaDoc[] getFieldNames() {
385         ensureMetaDataInitialized();
386         String JavaDoc[] names = new String JavaDoc[getFieldCount()];
387         for (int i = 0; i < names.length; i++) {
388             names[i] = getColumnMetaData(i).getName();
389         }
390         return names;
391     }
392     
393     public int getFieldCount() {
394         return getColumnCount();
395     }
396     
397     /**
398      * Sets the meta data object for the specified column.
399      * @throws IndexOutOfBoundsException if
400      * columnIndex < 0 or columnIndex >= columnCount
401      * @param columnIndex integer index indicating column in tabular data model
402      * @param metaData object describing the data element at the specified
403      * column
404      */

405     public void setColumnMetaData(int columnIndex, MetaData metaData) {
406         synchronized(this) {
407             if (columnMetaData[columnIndex] != null) {
408                 columnMetaData[columnIndex].removePropertyChangeListener(
409                     columnMetaDataListener);
410             }
411             columnMetaData[columnIndex] = metaData;
412             metaData.addPropertyChangeListener(columnMetaDataListener);
413         }
414         fireTableStructureChanged();
415     }
416
417     private void ensureMetaDataInitialized() {
418         if (columnMetaData == null || columnMetaData.length == 0) {
419             initializeMetaData();
420         }
421     }
422
423     private boolean initializeMetaData() {
424         if (sourceURL != null && loader != null) {
425             try {
426                 final InputStream JavaDoc is = sourceURL.openStream();
427                 loader.loadMetaData(this, is);
428                 is.close();
429                 return true;
430             } catch (Exception JavaDoc e) {
431                 return false;
432             }
433         }
434         return false;
435     }
436
437     /**
438      * Returns the value of the specified column's meta-data &quot;elementClass&quot;
439      * property.
440      * @throws IndexOutOfBoundsException if
441      * columnIndex < 0 or columnIndex >= columnCount
442      * @see MetaData#getElementClass
443      * @param columnIndex integer index indicating column in tabular data model
444      * @return Class indicating column data element's type
445      */

446     public Class JavaDoc getColumnClass(int columnIndex) {
447         ensureMetaDataInitialized();
448         return columnMetaData[columnIndex].getElementClass();
449     }
450
451     /**
452      * Returns the value of the specified column's meta-data &quot;label&quot;
453      * property. Note: javax.swing.table.TableModel specifies this method returns
454      * the header value, which is why this returns the label and not the name.
455      * @see MetaData#getLabel
456      * @throws IndexOutOfBoundsException if
457      * columnIndex < 0 or columnIndex >= columnCount
458      * @param columnIndex integer index indicating column in tabular data model
459      * @return String containing the column's label
460      */

461     public String JavaDoc getColumnName(int columnIndex) {
462         ensureMetaDataInitialized();
463         return columnMetaData[columnIndex].getLabel();
464     }
465
466     /**
467      * Returns the negation of the specified column's meta-data &quot;readOnly&quot;
468      * property.
469      * @see MetaData#isReadOnly
470      * @throws IndexOutOfBoundsException if
471      * columnIndex < 0 or columnIndex >= columnCount or
472      * rowIndex < 0 or rowIndex >= rowCount
473      * @param rowIndex integer index indicating row in tabular data model
474      * @param columnIndex integer index indicating column in tabular data model
475      * @return boolean indicating whether or not the tabular data element value
476      * at the specified row and column may be modified
477      */

478     public boolean isCellEditable(int rowIndex, int columnIndex) {
479         ensureMetaDataInitialized();
480         return !columnMetaData[columnIndex].isReadOnly();
481     }
482
483     /**
484      * @return integer indicating the number of rows currently in this
485      * tabular data model
486      */

487     public int getRowCount() {
488         return rows.size();
489     }
490
491     /**
492      * @throws IndexOutOfBoundsException if
493      * columnIndex < 0 or columnIndex >= columnCount or
494      * rowIndex < 0 or rowIndex >= rowCount
495      * @param rowIndex integer index indicating row in tabular data model
496      * @param columnIndex integer index indicating column in tabular data model
497      * @return Object containing value of tabular data element at the
498      * specified row and column
499      */

500     public Object JavaDoc getValueAt(int rowIndex, int columnIndex) {
501         Object JavaDoc row[] = (Object JavaDoc[])rows.get(rowIndex);
502         return row[columnIndex];
503     }
504
505     /**
506      * Sets the value of the tabular data element at the specified row and column.
507      * The caller is responsible for ensuring the object is an instance of the
508      * column's class.
509      * @see #getColumnClass
510      * @see #isCellEditable
511      * @throws RuntimeException if the column is not editable
512      * @throws IndexOutOfBoundsException if
513      * columnIndex < 0 or columnIndex >= columnCount or
514      * rowIndex < 0 or rowIndex >= rowCount
515      * @param value Object containing value of tabular data element
516      * @param rowIndex integer index indicating row in tabular data model
517      * @param columnIndex integer index indicating column in tabular data model
518      */

519     public void setValueAt(Object JavaDoc value, int rowIndex, int columnIndex) {
520         if (isCellEditable(rowIndex, columnIndex)) {
521             synchronized(this.rows) {
522                 Object JavaDoc row[] = (Object JavaDoc[]) rows.get(rowIndex);
523                 row[columnIndex] = value;
524                 fireTableCellUpdated(rowIndex, columnIndex);
525             }
526         } else {
527             throw new RuntimeException JavaDoc("row " + rowIndex +" column " + columnIndex +
528                                         " is not editable");
529         }
530     }
531
532     /**
533      * Deletes the row at the specified row index from the tabular data model.
534      * @throws IndexOutOfBoundsException if
535      * rowIndex < 0 or rowIndex >= rowCount
536      * @param rowIndex integer index indicating row in tabular data model
537      */

538     public void deleteRow(int rowIndex) {
539         synchronized(this.rows) {
540             rows.remove(rowIndex);
541             fireTableRowsDeleted(rowIndex, rowIndex);
542         }
543     }
544
545     /**
546      * Deletes the contiguous set of rows in the specified range from
547      * this tabular data model.
548      * @throws IllegalArgumentException if lastRowIndex < firstRowIndex
549      * @throws IndexOutOfBoundsException if
550      * firstRowIndex < 0 or firstRowIndex >= rowCount or
551      * lastRowIndex < 0 or lastRowIndex >= rowCount
552      *
553      * @param firstRowIndex integer index indicating first row in range
554      * @param lastRowIndex integer index indicating last row in range
555      */

556     public void deleteRows(int firstRowIndex, int lastRowIndex) {
557         if (lastRowIndex < firstRowIndex) {
558             throw new IllegalArgumentException JavaDoc("lastRowIndex, " + lastRowIndex +
559                                                " cannot be less than firstRowIndex, "+
560                                                firstRowIndex);
561         }
562         synchronized(this.rows) {
563             int deletedRowCount = lastRowIndex - firstRowIndex + 1;
564             for(int i = 0; i < deletedRowCount; i++) {
565                 rows.remove(firstRowIndex);
566             }
567             fireTableRowsDeleted(firstRowIndex, lastRowIndex);
568         }
569     }
570
571     /**@todo aim: run some tests to see if array copy bogs down on load */
572
573     /**
574      * Inserts a new row in the tabular data model at the specified row index.
575      * The elements in the row array are copied into the tabular data model,
576      * therefore subsequent changes to the content of the row parameter
577      * will have no affect on the contents of the tabular data model.
578      * @throws IndexOutOfBoundsException if
579      * rowIndex < 0 or rowIndex >= rowCount
580      * @param rowIndex integer index indicating row in tabular data model
581      * @param row array containing the column elements for the row
582      */

583     public void insertRow(int rowIndex, Object JavaDoc row[]) {
584         insertRowImpl(rowIndex, row);
585         fireTableRowsInserted(rowIndex, rowIndex);
586     }
587
588     /**
589      * Inserts a collection of new rows into the tabular data model at the
590      * specified row index. Each element in the collection must be an
591      * array of objects with a length equal to the number of columns in this
592      * tabular data model.
593      * The elements in the row arrays are copied into the tabular data model,
594      * therefore subsequent changes to the content of the rows parameter
595      * will have no affect on the contents of the tabular data model.
596      * @throws IndexOutOfBoundsException if
597      * rowIndex < 0 or rowIndex >= rowCount
598      * @param rowIndex integer index indicating row in tabular data model
599      * @param rows list containing an object array for each row to be added
600      */

601     public void insertRows(int rowIndex, List JavaDoc rows) {
602         synchronized(this.rows) {
603             for (int i = 0; i < rows.size(); i++) {
604                 Object JavaDoc row[] = (Object JavaDoc[]) rows.get(i);
605                 insertRowImpl(rowIndex + i, row);
606             }
607             fireTableRowsInserted(rowIndex, rowIndex+rows.size()-1);
608         }
609     }
610
611     /**
612      * Adds a new row at the end of the tabular data model.
613      * The elements in the row array are copied into the tabular data model,
614      * therefore subsequent changes to the content of the row array parameter
615      * will have no affect on the contents of the tabular data model.
616      * @param row array containing the column elements for the row
617      */

618     public void addRow(Object JavaDoc row[]) {
619         int rowIndex = rows.size();
620         insertRowImpl(rowIndex, row);
621         fireTableRowsInserted(rowIndex, rowIndex);
622     }
623
624     /**
625      * Adds a collection of new rows at the end of the tabular data model.
626      * Each element in the collection must be an array of objects with a
627      * length equal to the number of columns in this tabular data model.
628      * The elements in the rows list are copied into the tabular data model,
629      * therefore subsequent changes to the content of the row parameter
630      * will have no affect on the contents of the tabular data model.
631      * @param rows list containing an object array for each row to be added
632      */

633     public void addRows(List JavaDoc rows) {
634         insertRows(this.rows.size(), rows);
635     }
636
637     /* package-private method for loading row arrays without copying */
638     void loadRows(List JavaDoc rows) {
639         synchronized(this.rows) {
640             int rowIndex = this.rows.size();
641             this.rows.addAll(rows);
642             fireTableRowsInserted(rowIndex, rowIndex+rows.size());
643         }
644     }
645
646     private void insertRowImpl(int rowIndex, Object JavaDoc[] row) {
647         int rowLength = getColumnCount();
648         Object JavaDoc[] newRow = new Object JavaDoc[rowLength];
649         System.arraycopy(row, 0, newRow, 0, rowLength);
650         rows.insertElementAt(newRow, rowIndex);
651     }
652
653
654     /**
655      * Starts loading the data asynchronously from the tabular data object's source.
656      * If this tabular data object has existing data when this method is called,
657      * it will be cleared before loading any new data.
658      * This method will initiate a new thread to handle the loading and return
659      * immediately, thus it is safe to call from the UI thread.
660      * @throws IllegalStateException if the &quot;source&quot property is null
661      * @throws IOException
662      */

663     public void startLoading() throws IOException JavaDoc {
664         if (sourceURL == null) {
665             throw new IllegalStateException JavaDoc(
666                 "data cannot be loaded when source URL is null");
667         }
668         if (isLoading()) {
669             return;
670         }
671         ensureMetaDataInitialized();
672         // make sure we can connect to source first...
673
final InputStream JavaDoc is = sourceURL.openStream();
674
675         setLoading(true);
676         rows.clear();
677
678         // listener will be called on event-dispatch-thread
679
MessageAdapter a = new MessageAdapter(is);
680         loader.addProgressListener(a);
681         loader.addMessageListener(a);
682
683         loader.startLoading(this, is);
684     }
685
686     private class MessageAdapter implements ProgressListener, MessageListener {
687
688     private InputStream JavaDoc is;
689
690     /**
691      * This is a good way of doing this.
692      */

693     public MessageAdapter(InputStream JavaDoc is) {
694         this.is = is;
695     }
696
697     public void progressStarted(ProgressEvent event) {
698     }
699
700     public void progressIncremented(ProgressEvent event) {
701     }
702
703     public void progressEnded(ProgressEvent event) {
704         cleanup((DataLoader) event.getSource());
705     }
706
707     public void message(MessageEvent event) {
708         if (event.getThrowable() != null) {
709             cleanup((DataLoader) event.getSource());
710         }
711         // XXX - show messages to System.out for now. Move
712
// to a message bus.
713
System.out.println(event);
714
715     }
716
717     private void cleanup(DataLoader loader) {
718         loader.removeProgressListener(this);
719         setLoading(false);
720         try {
721         is.close();
722         }
723         catch (IOException JavaDoc e) {
724         }
725     }
726     }
727
728     /**
729      * @see #startLoading
730      * @return boolean indicating whether or not data is currently being loaded
731      */

732     public boolean isLoading() {
733         return loading;
734     }
735
736     private void setLoading(boolean loading) {
737         boolean oldLoading = this.loading;
738         this.loading = loading;
739         pcs.firePropertyChange("loading", oldLoading, loading);
740     }
741
742     /**
743      * Returns the contents of the row at the specified row index.
744      * The elements in the row are copied from the tabular data model,
745      * therefore subsequent changes to the content of the returned row
746      * array will have no affect on the contents of the tabular data model.
747      * Use <code>setValueAt</code> to change the contents of the tabular data
748      * model.
749      * @see #setValueAt
750      * @throws IndexOutOfBoundsException if
751      * rowIndex < 0 or rowIndex >= rowCount
752      * @param rowIndex integer index indicating row in tabular data model
753      * @return array containing the column element values for the row
754      */

755     public Object JavaDoc[] getRow(int rowIndex) {
756         int columnCount = getColumnCount();
757         Object JavaDoc row[] = new Object JavaDoc[columnCount];
758         System.arraycopy((Object JavaDoc[])rows.get(rowIndex), 0,
759                          row, 0, columnCount);
760         return row;
761
762     }
763
764     /**
765      * Note that the columnName parameter must be the &quot;name&quot; of the column
766      * as stored in the column's MetaData and should not be confused with
767      * the <code>getColumnName</code> method from TableModel, which actually
768      * refers to the &quot;label&quot; property on the MetaData.
769      * @see MetaData#getName
770      * @throws IllegalArgumentException if the column name does not exist in
771      * this tabular data model
772      * @param columnName String containing the name of the column
773      * @return integer index of column in tabular data model which corresponds
774      * to the specified column name
775      */

776     public int getColumnIndex(String JavaDoc columnName) {
777         for (int i = 0; i < columnMetaData.length; i++) {
778             if (columnMetaData[i].getName().equals(columnName)) {
779                 return i;
780             }
781         }
782         throw new IllegalArgumentException JavaDoc("\""+columnName+"\" not found in tabular data object");
783     }
784
785     /**
786      * Adds the specified property change listener to this tabular data model.
787      * @param pcl PropertyChangeListener object to receive events when
788      * tabular data model properties change
789      */

790     public void addPropertyChangeListener(PropertyChangeListener JavaDoc pcl) {
791         pcs.addPropertyChangeListener(pcl);
792     }
793
794     /**
795      * Removes the specified property change listener from this tabular data model.
796      * @param pcl PropertyChangeListener object to receive events when
797      * tabular data model properties change
798      */

799     public void removePropertyChangeListener(PropertyChangeListener JavaDoc pcl) {
800         pcs.removePropertyChangeListener(pcl);
801     }
802
803     /**
804      *
805      * @return array containing the PropertyChangeListener objects registered
806      * on this tabular data model
807      */

808     public PropertyChangeListener JavaDoc[] getPropertyChangeListeners() {
809         return pcs.getPropertyChangeListeners();
810     }
811
812     private class MetaDataChangeListener implements PropertyChangeListener JavaDoc {
813         public void propertyChange(PropertyChangeEvent JavaDoc e) {
814             String JavaDoc propertyName = e.getPropertyName();
815             if (propertyName.equals("elementClass") ||
816                 propertyName.equals("name")) {
817                 DefaultTableModelExt.this.fireTableStructureChanged();
818             }
819         }
820     }
821
822 }
823
Popular Tags