Class ListEvent<E>

  • All Implemented Interfaces:
    java.io.Serializable

    public abstract class ListEvent<E>
    extends java.util.EventObject
    A ListEvent models a sequence of changes to an EventList. A ListEventListener can be registered on an EventList to observe and handle these ListEvents.

    A simple change is characterized by its type and the corresponding list index. The type indicates the INSERT, UPDATE or DELETE operation that took place at the specified index.

    Here are some examples:

    • eventList.add(0, value) produces an INSERT at index 0 ("I0")
    • eventList.add(value) on a list with size 5 produces an INSERT at index 5 ("I5")
    • eventList.remove(3) on a list with appropriate size produces a DELETE at index 3 ("D3")
    • eventList.remove(value) on a list which contains the value at index 2 produces a DELETE at index 2 ("D2")
    • eventList.clear() on a list with size 1 produces a DELETE at index 0 ("D0")
    • eventList.set(1, value) on a list with appropriate size produces an UPDATE at index 1 ("U1")

    A ListEvent is capable of representing a sequence of such changes. Consider this example:

     EventList<String> list = GlazedLists.eventListOf("A", "D", "E");
     list.addAll(1, GlazedLists.eventListOf("B", "C"));
     
    This operation inserts element "B" at index 1 ("I1") and element "C" at index 2 ("I2"). These two changes are not represented as two separate ListEvents but as a sequence of changes ("I1", "I2") within one ListEvent. Because of this, the ListEvent is accessed like an iterator with the user iterating over the contained sequence of changes, see below. Here is another example:
     EventList<String> list = GlazedLists.eventListOf("A", "B", "C", "B");
     list.removeAll(GlazedLists.eventListOf("A", "C"));
     
    This will remove element "A" at index 0 and element "C" at original index 2. But the corresponding ListEvent looks like ("D0","D1"). The list index of the second deletion is automatically adjusted, taking the first deletion into account.

    Note, that the sequence of changes is ordered by ascending list indexes.

    The typical pattern to iterate over a ListEvent is:

     ListEvent listChanges = ...
     while (listChanges.next()) {
         final int type = listChanges.getType());
         final int index = listChanges.getIndex();
         // handle insert, update or delete at index
         ...
     }
     
    If you need to iterate over a ListEvent again, that's of course possible, just reset the ListEvent and iterate again.

    In addition to simple changes, ListEvent supports the view on list changes as blocks. This basically means, that simple changes of one particular type that build a continuous range of indexes are grouped together. Consider this example:

     EventList<String> list = GlazedLists.eventListOf("A", "A", "B", "B", "C", "C");
     list.removeAll(GlazedLists.eventListOf("A", "C"));
     
    This deletes all occurences of elements "A" and "C" from the list. So there is a deletion from index 0 to index 1 (inclusive) and another deletion from index 2 to 3. So, instead of modeling each change one by one like ("D0", "D0", "D2", "D2"), you can view the changes in blocks "D0-1" and "D2-3". This view is exactly what ListEvent offers by iterating the changes in blocks:
     ListEvent listChanges = ...
     while (listChanges.nextBlock()) {
         final int type = listChanges.getType());
         final int startIndex = listChanges.getBlockStartIndex();
         final int endIndex = listChanges.getBlockEndIndex();
         // handle insert, update or delete from startIndex to endIndex
         ...
     }
     
    In the above example you would have two blocks of changes of type DELETE instead of four simple changes. It is up to you, which iteration style you choose. Handling blocks of changes might yield a better performance.

    While it is possible to change the style during one iteration, it is not recommended, because you have to be careful to not miss some changes. Refering to the last example above, the following code would skip the change "D1":

     ListEvent listChanges = ...// ("D0", "D0", "D2", "D2") vs. ("D0-1", "D2-3")
     if (listChanges.next()) {
         final int type = listChanges.getType();
         final int index = listChanges.getIndex();
         // handle delete at index 0
         // ...
         while (listChanges.nextBlock()) {// move to next block starting at index 2, skipping change at index 1, which is part of the first block !
             final int type2 = listChanges.getType();
             final int startIndex = listChanges.getBlockStartIndex();
             final int endIndex = listChanges.getBlockEndIndex();
             // handle deletion from startIndex 2 to endIndex 3
             ...
         }
     }
     
    This kind of code is unusual, error-prone and should be avoided.

    There is a special kind of change, a reordering ListEvent. It indicates a reordering of the list elements, for example triggered by setting a new comparator on a SortedList.

    A reorder event cannot contain other regular changes in the current implementation. Instead it provides a reorder map, which maps the new indexes of the list elements to the old indexes. For details of the mapping see the javadoc of method getReorderMap().

    ListEventListeners, that don't explicitly check for a reorder event, will observe a deletion of all list elements with a subsequent re-insertion instead.

    In the future, ListEvent will provide even more information about the list changes to be more self-contained:

    The methods are currently marked as deprecated and should not be used yet, because the implementation is a work in progress.

    Note, that providing the old and new elements has an impact on the granularity of blocks of changes. For example, consider the clear operation on a list:

     EventList<String> list = GlazedLists.eventListOf("A", "B", "C");
     list.clear();
     
    Without considering the old and new elements, the ListEvent would consist of one block: a deletion from index 0 to 2 ("D0-2"). With the feature of providing the deleted elements, the ListEvent cannot consist of one block anymore, because of the additional requirement, that the old element must have the same value in one block:
     ListEvent listChanges = ...
     while (listChanges.nextBlock()) {
         final int type = listChanges.getType());
         final int startIndex = listChanges.getBlockStartIndex();
         final int endIndex = listChanges.getBlockEndIndex();
         final Object oldValue = listChanges.getOldValue();
         final Object newValue = listChanges.getNewValue();
         // handle insert, update or delete from startIndex to endIndex
         ...
     }
     
    So, a sequence of simple changes can only be grouped as block, when the type, as well as the old and new value are equal.
    Author:
    Jesse Wilson
    See Also:
    Serialized Form
    • Field Summary

      Fields 
      Modifier and Type Field Description
      static int DELETE
      different types of changes
      static int INSERT  
      protected EventList<E> sourceList
      the list that has changed
      static java.lang.Object UNKNOWN_VALUE
      indicates a removed element whose value is unknown
      static int UPDATE  
      • Fields inherited from class java.util.EventObject

        source
    • Method Summary

      All Methods Static Methods Instance Methods Abstract Methods Concrete Methods Deprecated Methods 
      Modifier and Type Method Description
      abstract ListEvent<E> copy()
      Create a bitwise copy of this ListEvent.
      abstract int getBlockEndIndex()
      Gets the last row of the current block of changes.
      abstract int getBlocksRemaining()
      Deprecated.
      this method depends on a particular implementation of how list events are stored internally, and this implementation has since changed.
      abstract int getBlockStartIndex()
      Gets the first row of the current block of changes.
      abstract int getIndex()
      Gets the current row index.
      abstract E getNewValue()
      Deprecated.
      this is a developer preview API that is not yet fit for human consumption.
      abstract E getOldValue()
      Deprecated.
      this is a developer preview API that is not yet fit for human consumption.
      abstract int[] getReorderMap()
      Gets the reorder map of this list.
      EventList<E> getSourceList()
      Gets the List where this event originally occured.
      abstract int getType()
      Gets the type of the current change, which should be one of ListEvent.INSERT, UPDATE, or DELETE.
      abstract boolean hasNext()
      Without incrementing the implicit iterator, this tests if there is another change to view.
      abstract boolean isReordering()
      Tests if this change is a complete reordering of the list.
      abstract boolean next()
      Increments the change sequence to view the next change.
      abstract boolean nextBlock()
      Increments the change sequence to view the next change block.
      abstract void reset()
      Resets this event's position to the previously-marked position.
      abstract java.lang.String toString()
      Gets this event as a String.
      static <E> E unknownValue()
      Returns a value indicating a removed element whose value is unknown.
      • Methods inherited from class java.util.EventObject

        getSource
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    • Field Detail

      • UNKNOWN_VALUE

        public static final java.lang.Object UNKNOWN_VALUE
        indicates a removed element whose value is unknown
      • sourceList

        protected EventList<E> sourceList
        the list that has changed
    • Method Detail

      • unknownValue

        public static final <E> E unknownValue()
        Returns a value indicating a removed element whose value is unknown.
      • reset

        public abstract void reset()
        Resets this event's position to the previously-marked position. This should be used for TransformedLists that require multiple-passes of the ListEvent in order to process it.
      • next

        public abstract boolean next()
        Increments the change sequence to view the next change. This will return true if such a change exists and false when there is no change to view.
      • hasNext

        public abstract boolean hasNext()
        Without incrementing the implicit iterator, this tests if there is another change to view. The user will still need to call next() to view such a change.
      • nextBlock

        public abstract boolean nextBlock()
        Increments the change sequence to view the next change block.
      • isReordering

        public abstract boolean isReordering()
        Tests if this change is a complete reordering of the list.

        If it's a reordering, you can determine the changed element positions with the help of the reorder map.

        See Also:
        getReorderMap()
      • getReorderMap

        public abstract int[] getReorderMap()
        Gets the reorder map of this list. Before calling this method, you should check that isReordering() returns true.

        The size of the returned array is equal to the list size. The array value at index i is the previous index (before the reordering) of the list element at index i (after the reordering).

        list before the reordering: "A", "B", "C"

        list after the reordering: "C", "B", "A"

        The reorder map of the corresponding ListEvent would look like: map[0]=2; map[1]=1; map[2]=0

        Returns:
        an array of integers where the previous index of a value is stored at the current index of that value.
        Throws:
        java.lang.IllegalStateException - if this is not a reordering event
        See Also:
        isReordering()
      • getIndex

        public abstract int getIndex()
        Gets the current row index. If the block type is delete, this will always return the startIndex of the current list change.
      • getBlockStartIndex

        public abstract int getBlockStartIndex()
        Gets the first row of the current block of changes. Inclusive.
      • getBlockEndIndex

        public abstract int getBlockEndIndex()
        Gets the last row of the current block of changes. Inclusive.
      • getType

        public abstract int getType()
        Gets the type of the current change, which should be one of ListEvent.INSERT, UPDATE, or DELETE.
      • getOldValue

        public abstract E getOldValue()
        Deprecated.
        this is a developer preview API that is not yet fit for human consumption. Hopefully the full implementation is complete for Glazed Lists 2.0.
        Gets the previous value for a deleted or updated element. If that data is not available, this will return UNKNOWN_VALUE.
      • getNewValue

        public abstract E getNewValue()
        Deprecated.
        this is a developer preview API that is not yet fit for human consumption. Hopefully the full implementation is complete for Glazed Lists 2.0.
        Gets the current value for an inserted or updated element. If that data is not available, this will return UNKNOWN_VALUE.
      • getBlocksRemaining

        public abstract int getBlocksRemaining()
        Deprecated.
        this method depends on a particular implementation of how list events are stored internally, and this implementation has since changed.
        Gets the number of blocks currently remaining in this atomic change.
      • getSourceList

        public EventList<E> getSourceList()
        Gets the List where this event originally occured.
      • toString

        public abstract java.lang.String toString()
        Gets this event as a String. This simply iterates through all blocks and concatenates them.
        Overrides:
        toString in class java.util.EventObject