Java: Swing: Structure: Observable lists
Usually, the collections in questions are either lists or sets. Otherwise it is not clear which of two equal items is meant. With sets, there can be no multiple equal items; with list, indices can be used instead of the items themselves to distinguish between them.
A list (in this context) is ordered collection of items, indexable by non-negative (or positive) integers.
By this definition, both java.util.SortedSet and java.util.List are lists. The latter allows to introduce any order from the outside, the former implicitly sorts by the given comparison order.
The absolute minimum of inspection methods to provide are get(index) and size() . This is exactly what ListModel provides. It is also possible to only provide an iterator, like SortedSet; but that is inefficient in this context, where fast random access is needed. (It is possible to write tree-based sorted sets that allow indexing).
Lists may change with dynamically with time. How that is done and why isn't the point here, but how listeners are notified of changes in the list.
of item(s) at certain indices by others, while the item count remains the same.
Fake: remove + add
The new item(s) should take the role of the item(s) that were in the same place before; in contrast, (strong) replacement is just an atomic remove+add operation (the old items have nothing to do with the new ones).
The placement of items at certain indices has been permuted, but no items have been added or removed. Together with sending along the old order, this type of event enables choices (selections, expansions of tree nodes) to "survive" the change. If this event type is simulated by replacement or adding/removable, information is lost.
For example, JList stores the indices of the selected items (it is impossible to store the items themselves, since the ListModel may contain multiple equal items, and it is not clear which one would be selected). When the items are reordered (for example sorted), there is no way to signify to the JList to keep the logically same indices selected.
Fake: replacement for bounds; or remove + add
Fake: remove + add
In theory, the observer could also register with the individual items to observe property changes instead of the list also handling these. Reasons against it are:
In Swing, there is no generic interface for observable objects, and if there were, it would probably be too generic to be useful.
The list (model) itself may (probably will) know better which observable properties are considered important in the contexts it is used.
There is no lossless notification, so the observer cannot unregister with removed items (unless holding a copy of the list).
(De-)registering one listener per item has overhead.
So the list also sents out events when "relevant" property changes have ocurred. Unfortunately, some event types don't distinguish between replacements and mere property changes (with lossless notification, this could always be done by comparing the old and new items).
This is especially awkward with ComboBoxModel's selection part (which is, if at all, a degenerate zero-or-one-element list). If a property of the selection changed, the ComboBoxModel must fire the same event as if there were a new selection. JComboBox has some special logic when receiving these events, probably to avoid firing extraneous Item- and ActionEvents if only a property of the selection item changed.
Model | Adding | Replacement | Removal | Moving | Complete changes | Property changesProperty changes |
|---|---|---|---|---|---|---|
ListModel | range | range (without old items) | range (without old items) | no | yes (without old items) | = replacement |
TableModel | range | range (without old items) | range (without old items) | no | yes (without old items) | = replacement |
TableColumnModel | single | no | single (without old item) | single | no | width and preferred width (by columnMarginChanged) items are observable |
TreeModel (children lists) | any | no | any (with old items) | no | yes (without old items) | any |
Document (list of characters) | range | no | range | no | no | range |
Document (element children lists) | range | range | range | no | except root? | with character events |
No model supports exchanging or reordering (ListDataEvent and TableModelEvent can be extended to do it, with ignorant listeners treating the event as normal replacement/property change).
complete changes only vaguely documented about the actual contents of the event.
nothing special.
No replacement is bad because by faking it, the selection state is inconsistent for a short time. To observe the items (which wouldn't be a problem since you know they are TableColumns), you must still have a copy of the model because you aren't given the TableColumn to disattach the property change listener from when it is removed (only the old index; DefaultTableColumnModel even gave a wrong index until 1.4.)
With the standard interface, you can only add columns at the end. If you want to have them elsewhere, you have to move them afterwards.
The fact that the old items are sent with removal events still is not enough. First, still a structure changed event for the parent could arrive, and that carries no information about the lost children. Second, changes up in the tree hierarchy only (if at all) sent the children along and not all descendants; and as they are removed, you cannot use the TreeModel methods anymore to get information about them. In fact, TreeModel is the model where it makes the least sense to sent them along, and to have changes at any indices instead of ranges.
Change update is only for attribute changes.
The documentation is rather vague about how elements can be added and removed at once, or how it works at all. It appears they must be consecutive.
(C) 2001-2009 Christian Kaufhold (swing@chka.de)