Inside the Undo Package

Java > Core SWING advanced programming > 7. TABLE EDITING > Overview of the Table Editing Mechanism

 

Overview of the Table Editing Mechanism

So far, you've seen several changes to the currency table that we've been using in this book that have enhanced its appearance and usability. The table as it stands now is useful for showing exchange rate changes for reference only, but suppose you wanted to allow the user to enter more recent values than the table currently contains. With this capability, you could use the same table to allow privileged users to update whatever information source lies behind the table's data model (perhaps a database). You can add this facility by making use of the editors that are built into the JTable control.

An Editable Currency Table

Making a table editable involves two steps:

Step 1.

Deciding which columns contain data that could be modified and enhancing the table model to allow those columns to be edited.

Step 2.

Choosing and installing the editors that will control the editing of information in the table.

In our first implementation of an editable table, we'll look mainly at what is involved in the first of these two steps. You'll learn about table editors later in this section for now, we'll rely entirely on the default editors that every table has available to allow the user to make changes to the data.

Implementing an Editable Table Model

Ultimately, the table model controls which cells, if any, of a table are editable. There are two TableModel methods that work together to control cell editability:

 public boolean isCellEditable (int row, int column); public void setValueAt (Object value, int row, int column); 

When the user tries to change the value in a table cell (by means that you'll see later), the JTable user interface (UI) class calls the JTable isCellEditable method, passing it the cell's row and column number. If this method returns true, an appropriate editor is assigned to the cell and the user is allowed to change the value displayed in the cell. When the user has updated the cell, the new value is stored in the model by passing it to the setValueAt method, which is also given the cell's row and column number, together with the new value in the form of an Object. If isCellEditable returns false, the data cannot be edited and the user won't be able to type anything into the cell.

All of the examples you have seen so far have used the CurrencyTableModel shown in Listing 7-1, which didn't directly implement either isCellEditable or setValueAt. Instead, it inherited these methods from AbstractTableModel, which implements them as follows:

 public boolean isCellEditable (int row, int column) {    return false; } public void setValueAt (Object value, int row, int column) { } 

As implemented here, the isCellEditable method ensures that none of the table's cells can have their values changed, which is why all the tables in the previous chapter were not editable. When is CellEditable returns false for a cell, the table will not attempt to change the cell's value, so the default implementation of setValueAt just does nothing because, of course, it will never be called.

To make the CurrencyTableModel editable, suitable implementations of is CellEditable and setValueAt need to be added. Because CurrencyTableModel is useful as a read-only currency model, instead of changing it, the new methods will be implemented in a derived class called EditableCurrencyTableModel, the implementation of which is shown in Listing 7-1.

Listing 7-1 An Editable Table Model
 package AdvancedSwing.Chapter7; import javax.swing.*; import AdvancedSwing.Chapters6.*; // An editable version of the currency table model public class EditableCurrencyTableModel                              extends CurrencyTableModel {    public boolean isCellEditable (int row, int column) {       return column == OLD_RATE_COLUMN ||                         column == NEW_RATE_COLUMN;    }    public void setValueAt (Object value, int row, int column) {    try {          if (column == OLD_RATE_COLUMN ||                         column == NEW_RATE_COLUMN) {             Double newObjectValue; // New value as an Object             double newValue; // double, for validity checking             if (value instanceof Number) {                // Convert Number to Double                newValue = ((Number)value).doubleValue();                newObjectValue = new Double(newValue);             } else if (value instanceof String) {                // Convert a String to a Double                newObjectValue = new Double ((String)value);                newValue = newObjectValue.doubleValue();             } else {                // Unrecognized - ignore                return;             }             if (newValue > (double)0.0) {                // Store new value, but reject zero or                // negative values                data [row] [column] = newObjectValue;                data [row] [DIFF_COLUMN] = new Double(((Double) data [row] [NEW_RATE_COLUMN]). doubleValue()         - ((Double) data [row] [OLD_RATE_COLUMN]). doubleValue ());                fireTableRowsUpdated (row, row);             }          }       } catch (NumberFormatException e) {          // Ignore a badly formatted number       }    } } 

As you can see, this class inherits most of its behavior from the existing CurrencyTableModel, as well as the initial currency values. The isCellEditable method is very simple: The editable version of the currency table will allow only today's or yesterday's exchange rates to be edited. Obviously, it makes no sense to allow the user to edit the difference between the two rates, while changing the currency name really implies the need to change all the values in the affected row, which is really a delete operation followed by the insertion of a new row, rather than an edit. These constraints are applied by arranging for isCellEditable to return true if, and only if, the cell being edited is in column 1 or 2, which were symbolically defined (by CurrencyTableModel) as OLD_RATE_COLUMN and NEW_RATE_COLUMN respectively. In this case, the decision as to whether a cell's contents can be modified is based entirely on which column it is in, but you can, if you need to, control editability on a cell-by-cell basis by using the row number as well as the column index.

The setValueAt method is slightly more complex. This method is given the new value (as an object) and the row and column index of the cell to be updated. In terms of the actual implementation of the CurrencyTable- Model, the new value needs to be assigned to the element data [row] [column] of the two-dimensional array that holds the data. There are, however, a few other things that need to be taken into account.

First, setvalueAt checks again that the cell to be changed is in columns 1 or 2. Of course, this repeats the same test made by isCellEditable and it may appear to be redundant. In terms of direct table editing, this check is, indeed, superfluous because the table will never attempt to update a cell for which isCellEditable returns false. However, other software can get direct access to the TableModel by calling the JTable getModel method and attempt to modify parts of the data that should be read-only. This check prevents such unauthorized access.

Next, the actual value needs to be stored in the table model. The value argument to setvalueAt is defined as an Object, so exactly what is its actual data type? As you'll see in the next section, the type of the value passed to setValueAt depends on the editor being used to modify the table. Ideally, because the table model holds Doubles, the editor would supply the new value as a Double (or at least as a Number). However, none of the editors installed in the table by default do this in fact, the editor that would be used for both of the editable columns in this table supplies the modified value as a string.

The implementation of setvalueAt used here accepts either a string or a Number as the new value. If the value is supplied as a Number, it is converted directly to a Double by extracting the double value from the Number and then passing it to the constructor of Double. When a string is supplied, it is passed directly to the Double constructor that accepts a String argument and the result is stored in the data array.

Once the new value has been stored, there is one final item of business to be attended to. The last column of the table always contains the difference between the old and new currency rates, so when either of these values is changed, it is necessary to calculate a new value for this column. The same code is used to perform this calculation as that used in the constructor of CurrencyTableModel shown in Listing 7-1 in the previous chapter. Once the new difference has been stored, the table is self-consistent again.

Once the editor has completed its job and setvalueAt has been called, the table will update the cell with the new value automatically, as you'll see shortly. However, using an editor is not the only way to change the table model. If the setvalueAt method is called from elsewhere, any changes to the model that it makes will not automatically be reflected in the table's on-screen appearance. To make sure that the table updates itself, the fireTableRowsUpdated method of AbstractTableModel is called. This sends a TableModelEvent to any listeners registered to receive events from the model, one of which is the JTable. On receipt of this event, the table repaints itself as necessary.

That's all there is to the implementation of the editable table model. There is, however, one small point that was glossed over in this description. Suppose the user types an illegal value into one of the editable cells. For example, suppose the user tries to store the value ABCDEF as an exchange rate. Obviously, this can't be allowed. The default cell editor won't perform any validity checking on the value that the user types it will just pass it directly to the setValueAt method. The validity checking is, in fact, performed by the constructor of the Double class when the string is given to it for conversion. If the String does not represent a valid Double, this constructor throws a NumberFormatException. As you can see, this exception is caught and the setValueAt method will return without storing a new value. The table editor framework doesn't provide any way to propagate back to the editor that an attempt was made to store an illegal value, so there is no other reasonable course to take. You might wish to display an error message to the user (using the JOptionPane class, for example), but for simplicity we have chosen to ignore illegal values, with the result that the user will simply see the cell revert to its old content on completion of the edit. The same action is taken if the new exchange rate converts to a value Double, but is negative or zero.

Having implemented an editable version of the CurrencyTableModel, it is very simple to change the example programs that we have been using so that they provide an editable table. You can experiment with such a table using the command

 java AdvancedSwing.Chapter7.EditableHighlightCurrencyTable 

This table is the same as the one that was shown in Figure 7-6 and, at first, will appear indistinguishable from it. The only line of code that was changed to make this table editable is this one:

 JTable tbl = new JTable (new EditableCurrencyTableModel()); 

which replaces the original

 JTable tbl = new JTable (new CurrencyTableModel ()); 

The code that modifies the data model is, of course, in the modified data model, while the editors and the editing capability were always available in the table but were deactivated by the isCellEditable method of CurrencyTableModel.

To change the value of a cell, double-click with the mouse in one of the editable columns (the middle two columns). A text editor will appear in the cell and you'll find that you can change the cell's content. Figure 7-1 shows how the table appears when the third column of the top row is being edited. To make the change permanent, click in another cell or press the RETURN key. Notice when you do so that the cell that has been edited is updated and the currency change is also recalculated. Furthermore, you should also see that, if the currency difference becomes negative, the corresponding cell in the third column is highlighted by the renderer installed in that column, demonstrating that the table view is being properly updated (you will, in fact, need to move the row selection away from the row containing the modified cell to see that the difference value has changed color).

Figure 7-1. Editing a table cell.
graphics/07fig01.gif

 

 



Core Swing
Core Swing: Advanced Programming
ISBN: 0130832928
EAN: 2147483647
Year: 1999
Pages: 55
Authors: Kim Topley

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net