A ListModel implementation that holds a List or ListModel in a ValueModel.
If you hold a List, this class can only report that the List has been
replaced; this is done by firing a PropertyChangeEvent for the
list
property. Also, a
ListDataEvent
is fired that reports
a complete change. In contrast, if you use a ListModel it will report
the same PropertyChangeEvent. But fine grained changes in the ListModel
will be fired by this class to notify observes about changes in the content,
added and removed elements.
If the list content doesn't change at all, or if it always changes
completely, you can work well with both List content and ListModel content.
But if the list structure or content changes, the ListModel reports more
fine grained events to registered ListDataListeners, which in turn allows
list views to chooser better user interface gestures: for example, a table
with scroll pane may retain the current selection and scroll offset.
If you want to combine List operations and the ListModel change reports,
you may consider using an implementation that combines these two interfaces,
for example
ArrayListModel
or
LinkedListModel
.
Important Note: If you change the ListModel instance,
either by calling
#setListModel(ListModel)
or by setting
a new value to the underlying list holder, you must ensure that
the list holder throws a PropertyChangeEvent whenever the instance changes.
This event is used to remove a ListDataListener from the old ListModel
instance and is later used to add it to the new ListModel instance.
It is easy to violate this constraint, just because Java's standard
PropertyChangeSupport helper class that is used by many beans, checks
a changed property value via
#equals
, not
==
.
For example, if you change the IndirectListModel's list model from an empty
list
L1
to another empty list instance
L2
,
the PropertyChangeSupport won't generate a PropertyChangeEvent,
and so, the IndirectListModel won't know about the change, which
may lead to unexpected behavior.
This binding library provides some help for firing PropertyChangeEvents
if the old ListModel and new ListModel are equal but not the same.
Class
ExtendedPropertyChangeSupport
allows to permanently or individually check the identity (using
==
) instead of checking the equity (using
#equals
).
Class
Model
uses this extended
property change support. And class
ValueHolder
uses it too
and can be configured to always test the identity.
This class provides public convenience methods for firing ListDataEvents,
see the methods
#fireContentsChanged
,
#fireIntervalAdded
, and
#fireIntervalRemoved
.
These are automatically invoked if the list holder holds a ListModel
that fires these events. If on the other hand the underlying List or
ListModel does not fire a required ListDataEvent, you can use these
methods to notify presentations about a change. It is recommended
to avoid sending duplicate ListDataEvents; hence check if the underlying
ListModel fires the necessary events or not.
Constraints: The list holder holds instances of
List
or
ListModel
. If the ListModel changes, the underlying ValueModel
must fire a PropertyChangeEvent.
addListDataListener
public final void addListDataListener(ListDataListener l)
Adds a listener to the list that's notified each time a change
to the data model occurs.
l
- the ListDataListener
to be added
createListDataChangeHandler
protected ListDataListener createListDataChangeHandler()
Creates and returns the ListDataListener used to observe
changes in the underlying ListModel. It is re-registered
in #updateListModel
.
- the ListDataListener that handles changes
in the underlying ListModel
fireContentsChanged
public final void fireContentsChanged(int index0,
int index1)
Notifies all registered ListDataListeners that the contents
of one or more list elements has changed.
The changed elements are specified by the closed interval index0, index1
-- the end points are included. Note that index0 need not be less than
or equal to index1.
If the list holder holds a ListModel, this IndirectListModel listens
to ListDataEvents fired by that ListModel, and forwards these events
by invoking the associated
#fireXXX
method, which in turn
notifies all registered ListDataListeners. Therefore if you fire
ListDataEvents in an underlying ListModel, you don't need this method
and should not use it to avoid sending duplicate ListDataEvents.
index0
- one end of the new intervalindex1
- the other end of the new interval
ListModel
, ListDataListener
, ListDataEvent
fireIntervalAdded
public final void fireIntervalAdded(int index0,
int index1)
Notifies all registered ListDataListeners that one or more elements
have been added to this IndirectListModel's List/ListModel.
The new elements are specified by a closed interval index0, index1
-- the end points are included. Note that index0 need not be less than
or equal to index1.
If the list holder holds a ListModel, this IndirectListModel listens
to ListDataEvents fired by that ListModel, and forwards these events
by invoking the associated
#fireXXX
method, which in turn
notifies all registered ListDataListeners. Therefore if you fire
ListDataEvents in an underlying ListModel, you don't need this method
and should not use it to avoid sending duplicate ListDataEvents.
index0
- one end of the new intervalindex1
- the other end of the new interval
ListModel
, ListDataListener
, ListDataEvent
fireIntervalRemoved
public final void fireIntervalRemoved(int index0,
int index1)
Notifies all registered ListDataListeners that one or more elements
have been removed from this IndirectListModel's List/ListModel.
index0
and
index1
are the end points
of the interval that's been removed. Note that
index0
need not be less than or equal to
index1
.
If the list holder holds a ListModel, this IndirectListModel listens
to ListDataEvents fired by that ListModel, and forwards these events
by invoking the associated
#fireXXX
method, which in turn
notifies all registered ListDataListeners. Therefore if you fire
ListDataEvents in an underlying ListModel, you don't need this method
and should not use it to avoid sending duplicate ListDataEvents.
index0
- one end of the removed interval,
including index0
index1
- the other end of the removed interval,
including index1
ListModel
, ListDataListener
, ListDataEvent
fireListChanged
protected final void fireListChanged(int oldLastIndex,
int newLastIndex)
Notifies all registered ListDataListeners that this ListModel
has changed from an old list to a new list content.
If the old and new list size differ, a remove or add event for
the removed or added interval is fired. A content change
is reported for the interval common to the old and new list.
This method is invoked by #updateList during the transition
from an old List(Model) to a new List(Model).
Note:
The order of the events fired ensures that after each event
the size described by the ListDataEvents equals the ListModel size.
oldLastIndex
- the last index of the old listnewLastIndex
- the last index of the new list
getElementAt
public final E getElementAt(int index)
Returns the value at the specified index, null
if the list model is null
.
index
- the requested index
- the value at
index
, null
if the list model is null
getList
public final List getList()
Returns the list holder's List or an empty List, if it
holds null
. Throws an exception if the list holder holds
any other type, including ListModels.
- the List content or an empty List if the content is
null
getListDataListeners
public final ListDataListener[] getListDataListeners()
Returns an array of all the list data listeners
registered on this IndirectListModel
.
- all of this model's
ListDataListener
s,
or an empty array if no list data listeners
are currently registered
getListHolder
public final ValueModel getListHolder()
Returns the model that holds the List/ListModel.
- the model that holds the List/ListModel
getListModel
public final ListModel getListModel()
Returns the list holder's ListModel or an empty ListModel, if it
holds null
. Throws an exception if the list holder holds
any other type, including Lists.
- the ListModel content or an empty ListModel
if the content is
null
getSize
public final int getSize()
Returns the length of the list, 0
if the list model
is null
.
- the size of the list,
0
if the list model is
null
getSize
protected final int getSize(Object aListListModelOrNull)
Returns the length of the given list, 0
if the list model
is null
.
aListListModelOrNull
- a List, ListModel or null
- the size of the given list,
0
if the list model is
null
isEmpty
public final boolean isEmpty()
Checks and answers if the list is empty or null
.
- true if the list is empty or
null
, false otherwise
release
public void release()
Removes the internal listeners from the list holder. If the current list
is a ListModel, the internal ListDataListener is removed from it.
This IndirectListModel must not be used after calling
#release
.
To avoid memory leaks it is recommended to invoke this method,
if the list holder, selection holder, or selection index holder
live much longer than this IndirectListModel.
Instead of releasing the IndirectListModel, you typically make the
list holder obsolete by releasing the PresentationModel or BeanAdapter
that has created them before.
As an alternative you may use ValueModels that in turn use
event listener lists implemented using
WeakReference
.
Basically this release method performs the reverse operation
performed during the IndirectListModel construction.
removeListDataListener
public final void removeListDataListener(ListDataListener l)
Removes a listener from the list that's notified each time a
change to the data model occurs.
l
- the ListDataListener
to be removed
setList
public final void setList(List newList)
Sets the given list as value of the list holder.
Note: Favor
ListModel
over
List
when working with an IndirectListModel.
Why? The IndirectListModel can work with both types. What's the
difference? ListModel provides all list access features
required by the IndirectListModel's. In addition it reports more
fine grained change events, instances of
ListDataEvents
.
In contrast developer often create Lists and operate on them
and the ListModel may be inconvenient for these operations.
A convenient solution for this situation is to use the
ArrayListModel
and
LinkedListModel
classes.
These implement both List and ListModel, offer the standard List
operations and report the fine grained ListDataEvents.
newList
- the list to be set as new list content
setListHolder
public final void setListHolder(ValueModel newListHolder)
Sets a new list holder. Does nothing if old and new holder are equal.
Removes the list change handler from the old holder and adds
it to the new one. In case the list holder contents is a ListModel,
the list data change handler is updated too by invoking
#updateListDataRegistration
in the same way as done in the
list change handler.
TODO: Check and verify whether the list data registration update
can be performed in one step
after the listHolder has been
changed - instead of remove the list data change handler, then
changing the listHolder, and finally adding the list data change handler.
newListHolder
- the list holder to be set
setListModel
public final void setListModel(ListModel newListModel)
Sets the given list model as value of the list holder.
newListModel
- the list model to be set as new list content
updateList
protected void updateList(Object oldList,
int oldSize,
Object newList)
Removes the list data change handler from the old list in case
it is a ListModel
and adds it to new one in case
it is a ListModel
.
It then fires a property change for the list and a contents change event
for the list content.
oldList
- the old list contentoldSize
- the size of the old List contentnewList
- the new list content
javax.swing.JTable.tableChanged(javax.swing.event.TableModelEvent)