ListChangeListener
Gaja Sutra
gajasutra at gmail.com
Fri Dec 9 06:28:55 PST 2011
> Unfortunately we will not be able to implement your proposal for some time. ObservableList is an interface and we will not be able to make changes until JDK 8. Even with virtual extension methods in place, we will probably not be able to add the addListener method to ObservableList, because there is no default implementation as far as I can tell.
Yes, this is clearly not possible now with good compatibility, but I
have not found problem before (sorry for not trying to write an
ObservableList before). In Java8, the default implementation can be:
wrap interface in an adaptor for current listener, like Daniel
(Zwolenski) say.
> The other issue is, that you will have a hard time to convince people in the JavaFX team. One of the high level goals for the JavaFX API is to use SAMs as much as possible to benefit from lambda expressions later. You may have noticed that all listeners, event handlers, callbacks etc. in the JavaFX library are SAMs. Convincing people to break this pattern with the ListChangeHandler will be tough. :-)
Yes, but others listeners are on simple properties, not on collections
(MapChangeListener seem to have the same problem than ListChangeListener
for me, even if I doesn't have tried to write one). This is not a simple
event handler, because you iterate on each operation of the change
(because it listen a collection).
> Now what we can do now and what we should do is improve the documentation. From my experience most ListChangeListeners are actually quite simple, because usually you do not have to test the operation type. For example if you loop over all removed elements, this will also work for an add-operation, because the list of removed elements will be empty. But AFAIK this is not documented so far, I can imagine we need a tutorial or somebody should blog about some common cases and how you can implement them with ListChangeListeners.
After thinking, I think there is a bigger problem with current design:
impossibility of evolving Change class with new change type from
improving performance when many listeners uses this change type and can
have specialized code for this change type.
Contrary to current Change object, the two stream API will be easier to
extends with new change type (IterableChangeBuilder need only adding a
method, ListChangeHandler will only need another method (if it is an
abstract class it will need to have a default implementation splitting
it in existing operations, or if it is an interface to have a Java8
extension method with default pointing to same code than abstract class
default implementation).
By example adding a new change type, like swap between two items without
change on items between them, will be impossible on Change (need code
change in all listeners to add a new test wasSwap(): then swap will
always be broken using current simpler operations (like add+remove or
big permute concerning items between the two) by IterableChangeBuilder
constructing Change and knowledge of swap will be lost in Change
instance. It will be impossible to have a specialized handler for swap
(like one doing less work if size of list is unchanged (*), or if some
items in the middle are unchanged (#)). After some thinking, I think
only the streams API can be evolved by adding new change operations in
future (but only with abstract class currently or interface using Java8
virtual extension methods).
(*) Like a ReadOnlyIntegerProperty with the size of ObservableList
(possible useful addition), if swap is expanded in add+remove.
(#) Like a FilteredList observing items in the middle of swap but not
the two swapped items, if swap is expanded in a big permute.
> Maybe you bumped into some use cases that we are not aware of. Could you please give us some examples? That would be very helpful to understand the difficulties you have encountered.
Yes. my biggest problem is with ConcatenatedList (an ObservableList of
ObservableList offering a living read-only view of concatenated lists).
I have copied code (without listeners, for readability and shortness).
As an use-case, I am interested in using it to populate a TableView with
items coming from multiples sources (changing sources, changing items
from each source).|
||
public class ConcatenatedList<E> extends
ObservableListWrapper<ObservableList<? extends E>> {
private final class ReadOnlyView extends
ReadOnlyUnbackedObservableList<E> {
@Override
public final E get(final int index) {
final int search = ConcatenatedList.this.readPartIndex(index);
final ObservableList<? extends E> part =
ConcatenatedList.this.get(search);
return part.get(index - ConcatenatedList.this.ends[search]);
}
@Override
public final int size() {
return
ConcatenatedList.this.ends[ConcatenatedList.this.ends.length - 1];
}
}
private final ReadOnlyView concatenated = new ReadOnlyView();
/**
* Boundaries between parts with (this.size()+1) items. first item
is 0, last item is size of concatenated list.
*/
private final int[] ends;
/* this listener transform change by changing list (from part list
to global view)
/* and shifting indexes and resend it on concatenated view
listeners. */
private final ListChangeListener<E> listener;
@SafeVarargs
ConcatenatedList(final Class<E> type, final ObservableList<?
extends E>... parts) {
super(Arrays.asList(parts));
final int size = this.size();
this.ends = new int[size + 1];
this.ends[0] = 0;
for (int i = 0; i < size; i++) {
final ObservableList<? extends E> part = this.get(i);
this.ends[i + 1] = this.ends[i] + part.size();
part.addListener(this.listener);
}
/* the following listener add and remove listeners on parts
added or removed.
* It need to creating change by adding, removing and permuting
blocks of items corresponding to each part list. */
this.addListener(/* */);
}
@Override
public final ObservableList<E> getConcatenated() {
return this.concatenated;
}
/**
*
* @param index
* @return part corresponding to this index
*/
private final int readPartIndex(final int index) {
int search = Arrays.binarySearch(this.ends, index);
if (search < 0) {
search = -search - 1;
}
return search;
}
}|
Thank you,
Daniel.
More information about the openjfx-dev
mailing list