com.jgoodies.binding.list
Class IndirectListModel<E>

java.lang.Object
  extended by com.jgoodies.binding.beans.Model
      extended by com.jgoodies.binding.list.IndirectListModel<E>
Type Parameters:
E - the type of the list elements
All Implemented Interfaces:
Observable, java.io.Serializable, javax.swing.ListModel
Direct Known Subclasses:
SelectionInList

public class IndirectListModel<E>
extends Model
implements javax.swing.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.

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

Field Summary
static java.lang.String PROPERTYNAME_LIST
          The name of the bound write-only list property.
static java.lang.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(java.util.List<E> list)
          Constructs an IndirectListModel on the given list.
IndirectListModel(javax.swing.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(javax.swing.event.ListDataListener l)
          Adds a listener to the list that's notified each time a change to the data model occurs.
protected  javax.swing.event.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.
 java.util.List<E> getList()
          Returns the list holder's List or an empty List, if it holds null.
 javax.swing.event.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.
 javax.swing.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(java.lang.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(javax.swing.event.ListDataListener l)
          Removes a listener from the list that's notified each time a change to the data model occurs.
 void setList(java.util.List<E> newList)
          Sets the given list as value of the list holder.
 void setListHolder(ValueModel newListHolder)
          Sets a new list holder.
 void setListModel(javax.swing.ListModel newListModel)
          Sets the given list model as value of the list holder.
protected  void updateList(java.lang.Object oldList, int oldSize, java.lang.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
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

PROPERTYNAME_LIST

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

See Also:
Constant Field Values

PROPERTYNAME_LIST_HOLDER

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

See Also:
Constant Field Values
Constructor Detail

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
Throws:
java.lang.NullPointerException - if listItems is null

IndirectListModel

public IndirectListModel(java.util.List<E> 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(javax.swing.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
Throws:
java.lang.NullPointerException - if listHolder is null
Method Detail

getList

public final java.util.List<E> 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
Throws:
java.lang.ClassCastException - if the list holder is neither null nor a List
Since:
2.0
See Also:
setList(List), getListModel(), setListModel(ListModel)

setList

public final void setList(java.util.List<E> 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
See Also:
getList(), getListModel(), setListModel(ListModel)

getListModel

public final javax.swing.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
Throws:
java.lang.ClassCastException - if the list holder is neither null nor a ListModel
See Also:
setListModel(ListModel), setList(List)

setListModel

public final void setListModel(javax.swing.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
See Also:
getListModel(), setList(List)

getListHolder

public final ValueModel getListHolder()
Returns the model that holds the List/ListModel.

Returns:
the model that holds the List/ListModel

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
Throws:
java.lang.NullPointerException - if the new list holder is null
java.lang.IllegalArgumentException - if the listHolder is a ValueHolder that doesn't check the identity when changing its value

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

getSize

public final int getSize()
Returns the length of the list, 0 if the list model is null.

Specified by:
getSize in interface javax.swing.ListModel
Returns:
the size of the list, 0 if the list model is null

getElementAt

public final E getElementAt(int index)
Returns the value at the specified index, null if the list model is null.

Specified by:
getElementAt in interface javax.swing.ListModel
Parameters:
index - the requested index
Returns:
the value at index, null if the list model is null
Throws:
java.lang.NullPointerException - if the list holder's content is null

addListDataListener

public final void addListDataListener(javax.swing.event.ListDataListener l)
Adds a listener to the list that's notified each time a change to the data model occurs.

Specified by:
addListDataListener in interface javax.swing.ListModel
Parameters:
l - the ListDataListener to be added

removeListDataListener

public final void removeListDataListener(javax.swing.event.ListDataListener l)
Removes a listener from the list that's notified each time a change to the data model occurs.

Specified by:
removeListDataListener in interface javax.swing.ListModel
Parameters:
l - the ListDataListener to be removed

getListDataListeners

public final javax.swing.event.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
See Also:
addListDataListener(ListDataListener), removeListDataListener(ListDataListener)

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

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
See Also:
PresentationModel.release(), BeanAdapter.release(), WeakReference

createListDataChangeHandler

protected javax.swing.event.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

updateList

protected void updateList(java.lang.Object oldList,
                          int oldSize,
                          java.lang.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:
JTable.tableChanged(javax.swing.event.TableModelEvent)

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

getSize

protected final int getSize(java.lang.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


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