BaseObservableList
Martin Sladecek
martin.sladecek at oracle.com
Wed Jan 4 06:21:48 PST 2012
OK, so for the 2.1 we will introduce only this:
FXCollections.observableList(List<E> list, Callback<E, Observable[]>
extractor);
FXCollections.observableArrayList(Callback<E, Observable[]> extractor);
These lists will fire update notifications if some of the Observables
extracted from the elements will change.
For this, we will need a new method in ListChangeListener.Change:
public boolean wasUpdated()
returning false by default (cannot make it abstract now).
BaseObservableList + invalidation() methods will be defered to some
later release.
Any comments? Rich, are you OK with this?
-Martin
On 12/19/2011 11:31 AM, Michael Heinrichs wrote:
> Hi Martin,
>
> as I said earlier, I do not like this approach, and I am still not
> persuaded otherwise. :-)
>
> I think it is a rather complex implementation. (Ok, I am in an unfair
> advantage here, because I have seen the implementation already.) My
> feeling is, that you cannot do anything out of ordinary without
> knowing the source code of BaseObservableList. And this is always a
> bad sign. For example there are two patterns how to generate the
> Change object, one for simple operations and one for complex. What is
> a simple operation and what is a complex operation? Can an extending
> class change the pattern that is used by default for a certain
> operation? Why do I have to call commit() after generating a simple
> Change and when should I call it?
>
> There is a lot of functionality in this class that is only needed for
> writable lists. I expect almost all extensions of BaseObservableList
> will be read-only. It seems to be a waste to have all of that
> functionality in a class, if it is most of the time not needed. IMO
> BaseObservableList should be optimized for the read-only case and be
> extendible for the few writable cases. (Or we introduce a two classes
> for both the two cases.)
>
> If you define a public class that is supposed to be extended, you
> commit not only on the public API, but also on the implementation. My
> feeling is, we are not at a point yet, where we can commit on the
> implementation.
>
> Overall, I do not think we need this for any of the features that is
> scheduled for the 2.1 release. We already discussed a good solution
> for http://javafx-jira.kenai.com/browse/RT-15029 internally that does
> not require the introduction of BaseObservableList. I would suggest,
> we focus on the smaller solution for now, discuss that on the public
> alias, and keep experimenting a little more with other options for
> BaseObservableList after the 2.1 release.
>
> - Michael
>
>
>
> On 17.12.2011, at 08:45, Martin Sladecek wrote:
>
>> On 12/17/2011 01:22 AM, Richard Bair wrote:
>>> Hi Martin,
>>>
>>>> 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.
>>> OK, the "Base" naming convention is one that I really regret, I wish
>>> we had just done AbstractFoo. Oh well. The things you end up
>>> regretting are often not the things you thought you might :-) But
>>> the "Base" convention we're using (hopefully everywhere...) is
>>> FooBase rather than BaseFoo, so I guess this should be
>>> ObservableListBase.
>>
>> OK.
>>>
>>>> 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:
>>> Is the proposal also to make ObservableListWrapper public?
>>
>> Yes. I've added invalidate() methods, we were discussing before, into
>> BaseObservableList. So making ObservableListWrapper public would also
>> allow calling invalidate(from, to) or invalidate(index) on it.
>>>
>>>> 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?
>>> Why is it called an IterableChangeBuilder? Is there a complete
>>> description of the API I can see (maybe attach to the issue?).
>> We had a NonIterableChange in com.sun.javafx.collections, which did
>> not allow iterating using next(), so this one was for creating
>> IterableChanges. But it doesn't make much sense in javafx.collections
>> package to call it like this, so maybe we could call it
>> ListChangeBuilder?
>>
>> I don't have any javadoc for that yet, but I'll try to describe it
>> briefly:
>> (package-private) IterableChangeBuilder(BaseObservableList<E> list);
>> // Create a new builder associated with list. This is called by
>> BaseObservableList in it's constructor
>>
>> public void beginComplexChange(); // Begins a complex change,
>> building up a change until endComplexChangeIsCalled(). Nested complex
>> changes are allowed.
>>
>> public void endComplexChange(); // If this is a topmost complex
>> change, create a Change object a call callObservers on the list.
>>
>> public void commit(); // If we are not in a complexChange, create an
>> Change object and call callObservers
>>
>> public boolean isEmpty(); // if there's some change stored in the builder
>>
>> public void nextAdd(int from, int to); // added something on interval
>> [from, to)
>>
>> public void nextPermutation(int from, int to, int[] perm);
>> //permutation perm on interval [from, to)
>>
>> public void nextRemove(int idx, E removed); //single element removed
>> from idx
>>
>> public void nextRemove(int idx, List<E> removed); //multiple elements
>> removed from idx.
>>
>> public void nextReplace(int from, int to, List<E> removed); replaced
>> removed with elements on interval [from, to)
>>
>> public void nextSet(int idx, E old); // changed element "old" on
>> index "idx"
>>
>> public void nextUpdate(int pos); // updated element on position pos
>>
>> public void reset(); //builder reset
>>
>>
>> Right now, it's tightly coupled with BaseObservableList, calling
>> directly it's callObservers method, but I'm going to separate them,
>> so it can be used in any implementation, not just the one based on
>> BaseObservableList.
>> Right now, the only way to use it is to use protected field in
>> BaseObservableList.
>>
>> -Martin
>>
>>>
>>> Thanks!
>>> Richard
>>
>
More information about the openjfx-dev
mailing list