ObservableList: how to reliably detect a setAll?
Asked Answered
P

1

18

In some contexts it's necessary to detect - in a ListChangeListener, without control about the list itself - a "all data swapped out", f.i. when we need to clear some state like selection - on completely new data the old state is meaningless.

Completely new data can be reached by

  • list.setAll(...)
  • list.set(otherObservableList) if list is a ListProperty

Thinking about which type of changes could be fired on setAll (c is the change, items is the observed list, "subChangeCount" pseudo-code for counting the subchanges):

// initially empty
assertEquals(0, items.size());
items.setAll(1, 2, 4);
assertEquals(1, c.subChangeCount());
assertTrue(c.wasAdded() && !c.wasReplaced());
assertEquals(0, c.getFrom());
assertEquals(c.getList().size(), c.getAddedSize()); 

// initially not empty
assertTrue(items.size() > 0);
items.setAll(1, 2, 4);
assertEquals(1, c.subChangeCount());
assertTrue(c.wasReplaced());
assertEquals(0, c.getFrom());
assertEquals(c.getList().size(), c.getAddedSize()); 

This seems to allow a utility check like:

boolean wasSetOrClearedAll(Change c) {
   if (c.getList().isEmpty()) return true;
   c.next();
   if (c.getAddedSize() == c.getList().size()) return true; 
   return false; 
}  

In contrast, internal fx code, f.i. in listening to ComboBox' items:

while (c.next()) {
   comboBox.wasSetAllCalled = comboBox.previousItemCount == c.getRemovedSize();
   ... 
}
comboBox.previousItemCount = getItemCount();

stores the old itemCount and compare that against the current removedSize (which I'm uncomfortable with, old state gets stale far too often for my taste), nevertheless there's a good probability that I'm missing something with my approach.

Question is:

in which context would my utility method fail (and core approach would detect the setAll correctly)?

Pampero answered 18/11, 2014 at 12:55 Comment(9)
What is the concrete type of the ObservableList you are using? Looks like ListProperty is an abstract class that does not declare setAll(...)Spirituel
@zeki hmm .. all ObservableLists must have a setAll(T... item) - can be unsupported, of course, so take any that has it implemented (ListProperty is nothing special, it simply routes the setAll of its backing list)Pampero
In that case, could you extend ListProperty and have setAll trigger an event before calling the super method?Spirituel
@Spirituel hmm .. looks like my question isn't overly clear (will try to reword it tomorrow :-) - my perspective if from the listener, just seeing the changes without any control over the sender.Pampero
@downvoter - care to explain?Pampero
@kleopatra:I don't understand your question. Especially assertEquals(0, c.getFrom());? Can you elaborate?Respectability
@kleopatra:Why not clear the delete/list?Respectability
@Phpdna I'm the listener - don't want to change the list itself, just do something if all content was swapped out. As to the c.getFrom(): a setAll fires a replaced starting at 0Pampero
@kleopatra:IMO the core code is safer. It guarantees the list is the same. Or can you use the core code and your solution parallel?Respectability
R
4

Unfortunately there is no reliable way of detecting this on the Listener side.

The struggle starts with the default implemention, which mostly looks like this:

@Override
public boolean setAll(Collection<? extends E> col) {
    beginChange();
    try {
        clear();
        addAll(col);
    } finally {
        endChange();
    }
    return true;
}

If you pass an empty Collection to setAll the result and the event that is fired are both exactly the same as when you would have called clear.

So your method wasSetOrClearedAll returns true when clear has been called, too (as would the core implementation).

So in the end there is no generic detection of setAll, it all depends on your use-case. If you can narrow down what you are trying to detect you may write a filter for that.

Raychel answered 9/12, 2014 at 9:53 Comment(3)
ahhh ... didn't even think at looking into core implementation ;-) Good argument, thanks! clear/setAll being the same is good enough - basically I need some means to reliably throw away all state related to old items (without having references to the items, only to their former positions). hmm ... back to thinkingPampero
And ListChangeListener.Change.getRemoved() / getFrom() / getTo() is not good enough for cleaning up?Raychel
hmm .. not if I follow core implementations of selectionModels. Which are buggy as hell, though. Probably need to think from scratch, maybe setAll is not such a special case, after all :-)Pampero

© 2022 - 2024 — McMap. All rights reserved.