com.jgoodies.binding.list

Class IndirectListModel<E>

Implemented Interfaces:
ListModel, Observable, Serializable
Known Direct Subclasses:
SelectionInList<E>

public class IndirectListModel<E>
extends Model
implements ListModel

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.

Version:
$Revision: 1.7 $
Author:
Karsten Lentzsch
Since:
2.0
See Also:
List, ListModel, SelectionInList, ValueModel, ComboBoxAdapter, AbstractTableAdapter, ExtendedPropertyChangeSupport, Model, ValueHolder

Field Summary

static String
PROPERTYNAME_LIST
The name of the bound write-only list property.
static String
PROPERTYNAME_LIST_HOLDER
The name of the bound read-write listHolder property.

Constructor Summary

IndirectListModel()
Constructs an IndirectListModel with an empty initial ArrayListModel.
IndirectListModel(E[] listItems)
Constructs an IndirectListModel on the given item array.
IndirectListModel(List list)
Constructs an IndirectListModel on the given list.
IndirectListModel(ListModel listModel)
Constructs an IndirectListModel on the given list model using a default list holder.
IndirectListModel(ValueModel listHolder)
Constructs an IndirectListModel on the given list holder.

Method Summary

void
addListDataListener(ListDataListener l)
Adds a listener to the list that's notified each time a change to the data model occurs.
protected ListDataListener
createListDataChangeHandler()
Creates and returns the ListDataListener used to observe changes in the underlying ListModel.
void
fireContentsChanged(int index0, int index1)
Notifies all registered ListDataListeners that the contents of one or more list elements has changed.
void
fireIntervalAdded(int index0, int index1)
Notifies all registered ListDataListeners that one or more elements have been added to this IndirectListModel's List/ListModel.
void
fireIntervalRemoved(int index0, int index1)
Notifies all registered ListDataListeners that one or more elements have been removed from this IndirectListModel's List/ListModel.
protected void
fireListChanged(int oldLastIndex, int newLastIndex)
Notifies all registered ListDataListeners that this ListModel has changed from an old list to a new list content.
E
getElementAt(int index)
Returns the value at the specified index, null if the list model is null.
List
getList()
Returns the list holder's List or an empty List, if it holds null.
ListDataListener[]
getListDataListeners()
Returns an array of all the list data listeners registered on this IndirectListModel.
ValueModel
getListHolder()
Returns the model that holds the List/ListModel.
ListModel
getListModel()
Returns the list holder's ListModel or an empty ListModel, if it holds null.
int
getSize()
Returns the length of the list, 0 if the list model is null.
protected int
getSize(Object aListListModelOrNull)
Returns the length of the given list, 0 if the list model is null.
boolean
isEmpty()
Checks and answers if the list is empty or null.
void
release()
Removes the internal listeners from the list holder.
void
removeListDataListener(ListDataListener l)
Removes a listener from the list that's notified each time a change to the data model occurs.
void
setList(List newList)
Sets the given list as value of the list holder.
void
setListHolder(ValueModel newListHolder)
Sets a new list holder.
void
setListModel(ListModel newListModel)
Sets the given list model as value of the list holder.
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.

Methods inherited from class com.jgoodies.binding.beans.Model

addPropertyChangeListener, addPropertyChangeListener, addVetoableChangeListener, addVetoableChangeListener, equals, fireIndexedPropertyChange, fireIndexedPropertyChange, fireIndexedPropertyChange, fireMultiplePropertiesChanged, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, firePropertyChange, fireVetoableChange, fireVetoableChange, fireVetoableChange, fireVetoableChange, fireVetoableChange, fireVetoableChange, fireVetoableChange, getPropertyChangeListeners, getPropertyChangeListeners, getVetoableChangeListeners, getVetoableChangeListeners, removePropertyChangeListener, removePropertyChangeListener, removeVetoableChangeListener, removeVetoableChangeListener

Field Details

PROPERTYNAME_LIST

public static final String PROPERTYNAME_LIST
The name of the bound write-only list property.

PROPERTYNAME_LIST_HOLDER

public static final String PROPERTYNAME_LIST_HOLDER
The name of the bound read-write listHolder property.

Constructor Details

IndirectListModel

public IndirectListModel()
Constructs an IndirectListModel with an empty initial ArrayListModel.

IndirectListModel

public IndirectListModel(E[] listItems)
Constructs an IndirectListModel on the given item array. The specified array will be converted to a List.

Changes to the list "write through" to the array, and changes to the array contents will be reflected in the list.

Parameters:
listItems - the array of initial items

IndirectListModel

public IndirectListModel(List list)
Constructs an IndirectListModel on the given list.

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.

Parameters:
list - the initial list

IndirectListModel

public IndirectListModel(ListModel listModel)
Constructs an IndirectListModel on the given list model using a default list holder.
Parameters:
listModel - the initial list model

IndirectListModel

public IndirectListModel(ValueModel listHolder)
Constructs an IndirectListModel on the given list holder.

Constraints: 1) The listHolder must hold instances of List or ListModel and 2) must report a value change whenever the value's identity changes. Note that many bean properties don't fire a PropertyChangeEvent if the old and new value are equal - and so would break this constraint. If you provide a ValueHolder, enable its identityCheck feature during construction. If you provide an adapted bean property from a bean that extends the JGoodies Model class, you can enable the identity check feature in the methods #firePropertyChange by setting the trailing boolean parameter to true.

Parameters:
listHolder - holds the list or list model

Method Details

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.
Parameters:
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.
Returns:
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.

Parameters:
index0 - one end of the new interval
index1 - the other end of the new interval
Since:
1.0.2
See Also:
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.

Parameters:
index0 - one end of the new interval
index1 - the other end of the new interval
Since:
1.0.2
See Also:
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.

Parameters:
index0 - one end of the removed interval, including index0
index1 - the other end of the removed interval, including index1
Since:
1.0.2
See Also:
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.

Parameters:
oldLastIndex - the last index of the old list
newLastIndex - 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.
Parameters:
index - the requested index
Returns:
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.
Returns:
the List content or an empty List if the content is null
Since:
2.0

getListDataListeners

public final ListDataListener[] getListDataListeners()
Returns an array of all the list data listeners registered on this IndirectListModel.
Returns:
all of this model's ListDataListeners, 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.
Returns:
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.
Returns:
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.
Returns:
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.
Parameters:
aListListModelOrNull - a List, ListModel or null
Returns:
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.
Returns:
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.

Since:
1.2

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.
Parameters:
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.

Parameters:
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.

Parameters:
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.
Parameters:
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.
Parameters:
oldList - the old list content
oldSize - the size of the old List content
newList - the new list content
See Also:
javax.swing.JTable.tableChanged(javax.swing.event.TableModelEvent)

Copyright © 2002-2007 JGoodies Karsten Lentzsch. All Rights Reserved.