February 25, 2021
Hot Topics:

Creating Interactive GUIs with Swing's MVC Architecture Part 2: Dynamic View for a Static Model

  • By Vlad Kofman
  • Send Email »
  • More Articles »

The filtering models do not hold data directly, but hold a reference to it, and they extend AbstractTableModel to make sure they are compliant with JTable's public model signature. Also, both of them implement the TableModelListener interface to deal with actual events raised from the users. The sorting Model was adapted from the Sun Java tutorial and modified to work with my application. See the Reference section.

When JTable is created, it already contains a default model, but I'm installing my own custom one, and will actually chain my filters on top of it. For the purposes of the simple application, I created a Moon class, See Listing 1, which contains data corresponding to one row of the table. In the initialization code, I created an array of Moon objects and populated it with data, and then set it to be the main data of my model.

static Moon[] moons =
   new Moon[] {
      new Moon("Moon", planet[1], "None", 0),
      new Moon("Metis", planet[2], "Ardon", 134000),
... ... ... ...
... ... ... ...

The data in an enterprise application would most likely come from some relational data source or a dynamic data store, but in my case I simply populate the table from an array. The setModelData method is not part of the Table Model and is custom, piggybacking on the addRow(object[] array) method from DefaultTableModel.

See Listing 2 for the method's code.

In my custom model, I also have set header names and overwrote some methods; for instance, isCellEditable is set to always return false, and getColumnNane(int c) is set to use an array of column header names.

Then, the custom model could be assigned to the JTable, for example.

PlanetTableModel model = new PlanetTableModel();
JTable planetList      = new JTable(model);

This ensures that the displayed JTable uses the custom model and all calls from the view are directed to it. The table is then added to the Frame and validated. As you can see in Figure 1, Moon information is displayed, the header is correct, and all cells are not editable.

Model Chaining

Installing the custom model allows for much greater control over how data is handled when the user starts to interact with the presentation; however, chaining models (or installing filters) will allow for the manipulation of presenting without changing the data itself. As I've mentioned, the technique for doing so, relies on inheritance, passing objects by reference, and event/listener mechanisms. Inheritance is used when custom models sub-class default Model objects and override or extend default methods. But, the other two need more explaining.

The filter models have constructors that take a reference to the table model and do not actually hold any data. However, they do override indexing methods of the table model interface such as getValueAt(row, col) and hold an internal index of the data. This allows creating custom model, passing it by reference to the filter model and then installing filter model into the JTable. Therefore, the table uses indexing and locating methods of the filter to represent actual data, stored in a separate custom model object. And there could by more filters chained on top of each other.

model           = new PlanetTableModel();                      // model
exclusionFilter = new PlanetTableExclutionFilter(model);       // filter 1
sortFilter      = new PlanetTableSortFilter(exclusionFilter);  // filter 2
planetList      = new JTable(sortFilter);                      // table

Event Model and Dynamic Model Presentation

The event/listener mechanism is involved in propagating events to all interested models (because they implement TableModelListener), making sure proper action, such as recalculation of the index, is taken on user request.

public void tableChanged(TableModelEvent e) {    // called in table
                                                 // change
      reallocateIndexes();                       // do custom action
      fireTableChanged(e);                       // forward event

See Listing 3 for the relocateIndexes() code.

For instance, the exclusion filter model has a custom method that takes a "filter" string; this method is initiated when the user selects an item from the drop-down list. Once the call is made, the filter recalculates the index to include only rows containing a matching string and generates an event that table has been changed. See Listing 4. Not coincidently, it has identical code to recalculate index as a reallocateIndexes() method.

This updates the presentation and the underlying data; the real model is not changed.

Figure 2

Clicking on the column header will call a sorting algorithm from the sorting model that also recalculates the index of presented (filtered) data and also generates an event that the table has been changed. This demonstrates an enormous power of chaining models as the presentation is now filtered and sorted, in real time, without additional object creation or repopulation of any model.

Figure 3

Sorting, of course, works on the complete, unfiltered data set; see Figure 4.

Figure 4

Page 2 of 3

This article was originally published on June 3, 2004

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date