001/* ----------------------------------------------------------------------------
002   The Kiwi Toolkit - A Java Class Library
003   Copyright (C) 1998-2004 Mark A. Lindner
004
005   This library is free software; you can redistribute it and/or
006   modify it under the terms of the GNU General Public License as
007   published by the Free Software Foundation; either version 2 of the
008   License, or (at your option) any later version.
009
010   This library is distributed in the hope that it will be useful,
011   but WITHOUT ANY WARRANTY; without even the implied warranty of
012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013   General Public License for more details.
014
015   You should have received a copy of the GNU General Public License
016   along with this library; if not, write to the Free Software
017   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018   02111-1307, USA.
019 
020   The author may be contacted at: mark_a_lindner@yahoo.com
021   ----------------------------------------------------------------------------
022   $Log: DomainObjectTableModel.java,v $
023   Revision 1.7  2004/05/13 21:38:56  markl
024   comment block updates
025
026   Revision 1.6  2003/04/30 04:36:00  markl
027   Fire model events as appropriate. Added updateObjectAt() method.
028
029   Revision 1.5  2003/01/19 09:33:06  markl
030   Javadoc & comment header updates.
031
032   Revision 1.4  2001/03/12 04:11:42  markl
033   Source code cleanup.
034
035   Revision 1.3  1999/08/13 07:11:35  markl
036   Added support for column widths.
037
038   Revision 1.2  1999/07/25 13:41:44  markl
039   Fixes and API additions.
040
041   Revision 1.1  1999/07/16 07:11:22  markl
042   Initial revision
043   ----------------------------------------------------------------------------
044*/
045
046package kiwi.ui.model;
047
048import java.sql.*;
049import java.util.*;
050import javax.swing.*;
051import javax.swing.table.*;
052
053import kiwi.db.*;
054import kiwi.util.*;
055
056/** A <code>TableModel</code> that may be used to display a (potentially
057 * editable) list of persistent domain objects in a <code>JTable</code>.
058 *
059 * @author Mark Lindner
060 *
061 * @see kiwi.db.PersistentObject
062 * @see kiwi.db.DomainObjectFieldAdapter
063 */
064
065public class DomainObjectTableModel extends AbstractTableModel
066  {
067  private DomainObjectFieldAdapter fieldAdapter;
068  private Vector data;
069  private boolean modified = false;
070  private boolean deferredStore = false;
071  private ExceptionHandler errorHandler = null;
072
073  /** Construct a new <code>DomainObjectTableModel</code> for the given
074   * field adapter.
075   *
076   * @param adapter The field adapter to use.
077   */
078  
079  public DomainObjectTableModel(DomainObjectFieldAdapter adapter)
080    {
081    super();
082    fieldAdapter = adapter;
083    data = new Vector();
084    }
085
086  /** Set the <i>deferred store</i> mode for this model. If deferred store is
087   * on (default), editing of a value in the model causes the corresponding
088   * <code>PersistentObject</code> to be written to the persistent store
089   * immediately via a call to its <code>store()</code> method. If deferred
090   * store is off, no automatic stores are performed.
091   *
092   * @param flag A flag that specifies whether deferred mode should be on
093   * (<code>true</code>) or off (<code>false</code>).
094   */
095  
096  public void setDeferredStore(boolean flag)
097    {
098    deferredStore = flag;
099    }
100
101  /** Get the <i>deferred store</i> mode for this model.
102   *
103   * @return <code>true</code> if deferred store is on, and <code>false</code>
104   * otherwise.
105   */
106
107  public boolean getDeferredStore()
108    {
109    return(deferredStore);
110    }
111  
112  /** Get the name of a given column.
113   *
114   * @param column The index of the column.
115   * @return The name of the field that corresponds to the given column.
116   */
117  
118  public String getColumnName(int column)
119    {
120    return(fieldAdapter.getFieldName(column));
121    }
122
123  /** Add an object to the end of the data vector. A new row will be added
124   * at the bottom of the table model.
125   *
126   * @param object The <code>PersistentObject</code> to add.
127   */
128  
129  public void addObject(PersistentObject object)
130    {
131    int index = data.size();
132    data.addElement(object);
133    modified = true;
134
135    fireTableRowsInserted(index, index);
136    }
137
138  /** Insert an object at the specified position in the data vector. A new row
139   * will be inserted at the corresponding position in the table model.
140   *
141   * @param object The <code>PersistentObject</code> to add.
142   * @param row The row at which to insert the object.
143   */
144  
145  public void insertObjectAt(PersistentObject object, int row)
146    {
147    data.insertElementAt(object, row);
148    modified = true;
149
150    fireTableRowsInserted(row, row);
151    }
152
153  /** Notify the model that the object at the given row has been updated.
154   *
155   * @param row The row of the updated object.
156   * @since Kiwi 1.4.2
157   */
158
159  public void updateObjectAt(int row)
160    {
161    if((row < 0) || (row >= data.size()))
162      throw new IllegalArgumentException("Row out of bounds");
163
164    fireTableRowsUpdated(row, row);
165    }
166
167  /** Remove all objects from the data vector. All rows will be removed from
168   * the table model.
169   */
170  
171  public void clear()
172    {
173    data.removeAllElements();
174    modified = true;
175    }
176
177  /** Remove the object at the specified position from the data vector. The
178   * row at the corresponding position in the table model will be removed.
179   *
180   * @param row The row of the object to remove.
181   */
182  
183  public void removeObjectAt(int row)
184    {
185    data.removeElementAt(row);
186    modified = true;
187
188    fireTableRowsDeleted(row, row);
189    }
190
191  /** Get the row count for this model.
192   *
193   * @return The number of objects in the data vector (e.g., the number of
194   * rows in the table model).
195   */
196  
197  public int getRowCount()
198    {
199    return(data.size());
200    }
201
202  /** Get the column count for this model.
203   *
204   * @return The number of columns in this table model (e.g., the number of
205   * fields as reported by the field adapter).
206   */
207  
208  public int getColumnCount()
209    {
210    return(fieldAdapter.getFieldCount());
211    }
212
213  /** Get the value at the specified row and column in the table model.
214   *
215   * @param row The row.
216   * @param column The column
217   * @return The object at the specified coordinates.
218   */
219  
220  public Object getValueAt(int row, int column)
221    {
222    PersistentObject object = (PersistentObject)data.elementAt(row);
223    return(fieldAdapter.getField(object, column));
224    }
225
226  /** Set the value at the specified row and column in the table model.
227   *
228   * @param row The row.
229   * @param column The column
230   * @param value The new object for the specified coordinates.
231   */
232  
233  public void setValueAt(Object value, int row, int column)
234    {
235    PersistentObject object = (PersistentObject)data.elementAt(row);
236    try
237      {
238      fieldAdapter.setField(object, column, value);
239      if(!deferredStore)
240        object.store();
241      modified = true;
242
243      fireTableRowsUpdated(row, row);
244      }
245    catch(MutatorException ex)
246      {
247      _deliverException(ex);
248      }
249    catch(SQLException ex)
250      {
251      _deliverException(ex);
252      }
253    }
254
255  /** Prepare a <code>JTable</code> for use with this model. This method
256   * creates the necessary columns in the <code>JTable</code> and assigns
257   * the appropriate cell renderers and editors (as provided by the field
258   * adapter) for each column.
259   *
260   * @param table The <code>JTable</code> with which this model will be used.
261   */
262  
263  public void prepareJTable(JTable table)
264    {
265    TableColumnModel cmodel = table.getColumnModel();
266    int cols = cmodel.getColumnCount();
267
268    for(int i = 0; i < cols; i++)
269      {
270      TableColumn tc = cmodel.getColumn(i);
271      tc.setCellRenderer(fieldAdapter.getCellRenderer(i));
272      tc.setCellEditor(fieldAdapter.getCellEditor(i));
273
274      int w = fieldAdapter.getFieldPreferredWidth(i);
275      if(w > 0)
276        tc.setPreferredWidth(w);
277
278      w = fieldAdapter.getFieldMinWidth(i);
279      if(w > 0)
280        tc.setMinWidth(w);
281
282      w = fieldAdapter.getFieldMaxWidth(i);
283      if(w > 0)
284        tc.setMaxWidth(w);
285      }
286    }
287
288  /** Get the <code>PersistentObject</code> for the specified row in the
289   * table model.
290   *
291   * @param row The row.
292   * @return The <code>PersistentObject</code> represented in the given row.
293   * @see #getRowForObject
294   */
295
296  public PersistentObject getObjectForRow(int row)
297    {
298    return((PersistentObject)data.elementAt(row));
299    }
300
301  /** Get the row for the specified <code>PersistentObject</code>.
302   *
303   * @param object The <code>PersistentObject</code> to locate.
304   * @return The row in the table model that represents the given object.
305   * @see #getObjectForRow
306   */
307  
308  public int getRowForObject(PersistentObject object)
309    {
310    return(data.indexOf(object));
311    }
312
313  /** Determine if the cell at the given coordinates is editable.
314   *
315   * @param row The row.
316   * @param column The column.
317   * @return <code>true</code> if the cell at the given row and column is
318   * editable and <code>false</code> otherwise. The field adapter is consulted
319   * to determine if the field corresponding to the given column is mutable.
320   */
321
322  public boolean isCellEditable(int row, int column)
323    {
324    return(fieldAdapter.isFieldEditable(column));
325    }
326
327  /** Determine if the data in this model has been modified.
328   *
329   * @return <code>true</code> if modifications have been made, and
330   * <code>false</code> otherwise.
331   *
332   * @see #clearModified
333   */
334  
335  public boolean isModified()
336    {
337    return(modified);
338    }
339
340  /** Clear the modification flag for this model.
341   *
342   * @see #isModified
343   */
344  
345  public void clearModified()
346    {
347    modified = false;
348    }
349
350  /** Get the object type for the specified column.
351   *
352   * @param column The column.
353   * @return A <code>Class</code> object for the specified column. This value
354   * is ultimately obtained by calling the <code>getFieldClass()</code> method
355   * of the field adapter.
356   */
357  
358  public Class getColumnClass(int column)
359    {
360    return(fieldAdapter.getFieldClass(column));
361    }
362
363  /** Set the <code>ExceptionHandler</code> for this model. If an exception
364   * occurs while a domain object is being updated as a result of a change
365   * to one of the values in this model, the exception is caught and delivered
366   * to the handler (if one is registered).
367   *
368   * @param handler The new (possibly <code>null</code>) exception handler.
369   */
370  
371  public void setExceptionHandler(ExceptionHandler handler)
372    {
373    this.errorHandler = handler;
374    }
375
376  /** Get the current <code>ExceptionHandler</code> for this model.
377   *
378   * @return The current (possibly <code>null</code>) exception handler.
379   */
380
381  public ExceptionHandler getExceptionHandler()
382    {
383    return(errorHandler);
384    }
385  
386  /* Deliver an exception to a handler, if one is registered. */
387
388  private void _deliverException(Exception ex)
389    {
390    if(errorHandler != null)
391      errorHandler.exceptionRaised(ex);
392    }
393  
394  }
395
396/* end of source file */