| Proxies a standard TreeModel and TableModel, translating events between
the two. Note that the constructor is not public; the TableModel that is
proxied is the OutlineModel's own. To make use of this class, implement
RowModel - that is a mini-table model in which the TreeModel is responsible
for defining the set of rows; it is passed an object from the tree, which
it may use to generate values for the other columns. Pass that and the
TreeModel you want to use to createOutlineModel .
A note on TableModelEvents produced by this model: There is a slight
impedance mismatch between TableModelEvent and TreeModelEvent. When the
tree changes, it is necessary to fire TableModelEvents to update the display.
However, TreeModelEvents support changes to discontiguous segments of the
model (i.e. "child nodes 3, 4 and 9 were deleted"). TableModelEvents
have no such concept - they operate on contiguous ranges of rows. Therefore,
one incoming TreeModelEvent may result in more than one TableModelEvent being
fired. Discontiguous TreeModelEvents will be broken into their contiguous
segments, which will be fired sequentially (in the case of removals, in
reverse order). So, the example above would generate two TableModelEvents,
the first indicating that row 9 was removed, and the second indicating that
rows 3 and 4 were removed.
Clients which need to know whether the TableModelEvent they have just
received is one of a group (perhaps they update some data structure, and
should not do so until the table's state is fully synchronized with that
of the tree model) may call areMoreEventsPending() .
In the case of TreeModelEvents which add items to an unexpanded tree node,
a simple value change TableModelEvent will be fired for the row in question
on the tree column index.
Note also that if the model is large-model, removal events may only indicate
those indices which were visible at the time of removal, because less data
is retained about the position of nodes which are not displayed. In this
case, the only issue is the accuracy of the scrollbar in the model; in
practice this is a non-issue, since it is based on the Outline's row count,
which will be accurate.
A note to subclassers, if we even leave this class non-final: If you do
not use ProxyTableModel and RowMapper (which probably means you are doing
something wrong), do not fire structural changes from the TableModel.
This class is designed such that the TreeModel is entirely in control of the
count and contents of the rows of the table. It and only it may fire
structural changes.
Note that this class enforces access only on the event dispatch thread
with assertions. All events fired by the underlying table and tree model
must be fired on the event dispatch thread.
author: Tim Boudreau |