BaseObservableList

Martin Sladecek martin.sladecek at oracle.com
Wed Dec 14 02:43:20 PST 2011


Hello,
I'm working on http://javafx-jira.kenai.com/browse/RT-15029 and as part 
of this effort, I'm doing a refactoring of ObservableListWrapper.

So I've created an abstract class BaseObservableList (extends 
AbstractList) that could serve as a base class for all ObservableLists 
implementations.
For a simple (modifiable) implementation, one would need to implement 
following methods:

     public int size() {
     }

     public E get(int index) {
     }

     protected void doAdd(int index, E element) {
     }

     protected E doRemove(int index) {
     }

     protected E doSet(int index, E element) {
     }



So something like ObservableListWrapper would look like this:

      private final List<E>  backingList;

     protected List<E>  getBackingList() {
         return backingList;
     }

     public ObservableListWrapper(List<E>  backingList) {
         this.backingList = backingList;
     }

     @Override
     public int size() {
         return backingList.size();
     }

     @Override
     public E get(int index) {
         return backingList.get(index);
     }

     @Override
     protected void doAdd(int index, E element) {
         backingList.add(index, element);
     }

     @Override
     protected E doRemove(int index) {
         return backingList.remove(index);
     }

     @Override
     protected E doSet(int index, E element) {
         return backingList.set(index, element);
     }


The methods from List interface like add, set, remove, etc.. then 
delegate calls to do* methods and report changes to ListChangeListeners, 
so you don't need to worry about this.


Now when somebody would want to override something else or extend the 
implementation, an IterableChangeBuilder, that's used in 
BaseObservableList, needs to be used for this purpose.

So when creating something by delegating to the methods from the List, 
you just need to wrap the block in
changeBuilder.beginComplexChange(); and changeBuilder.endComplexChange();
This builds up the change in the calls in between instead of calling the 
observers multiple times.

E.g. setAll method works like this

     public boolean setAll(Collection<? extends E>  col) {
         changeBuilder.beginComplexChange();
         clear(); //does not call ListChangeListeners
         addAll(col); //neither does this
         changeBuilder.endComplexChange(); //this calls the ListChangeListeners using just one Change object
         return true;
     }


On the other hand, if you want to override something simple like 
add/remove/set or use just doSet, doAdd and doRemove methods or you 
directly manipulate with your data structure, you have to use a 
different pattern and use methods from the IterableChangeBuilder (there 
are methods like nextAdd, nextRemove, nextSet). And then end it with 
commit() call.

So, e.g. you want to implement some fast clear() (the original clear in 
AbstractList iterates over the elements and remove them one by one, 
building up the resulting change on the way), you would do:

public void clear() {
     //do the fast clear
     changeBuilder.nextRemove(0, listOfRemovedItems);
     changeBuilder.commit(); //Calls the ListChangeListeners, but only if not in ComplexChange block
}


Of course, these 2 patterns could be combined and you can do something 
like this:

changeBuilder.beginComplexChange();
clear(); //no call to observers, as we are in ComplexChange block.
// do some stuff using doAdd or by directly changing my data structures
chnageBuilder.nextAdd(0, x); //I've added something so I need to report this
changeBuilder.endComplexChange(); //calls the ListChangeListeners


What do you think about this API?

Thanks,
-Martin


More information about the openjfx-dev mailing list