org.reactfx.collection.LiveList Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of reactfx Show documentation
Show all versions of reactfx Show documentation
Reactive event streams for JavaFX
The newest version!
package org.reactfx.collection;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import javafx.beans.InvalidationListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.control.IndexRange;
import org.reactfx.EventStream;
import org.reactfx.EventStreamBase;
import org.reactfx.Observable;
import org.reactfx.Subscription;
import org.reactfx.collection.LiveList.QuasiChangeObserver;
import org.reactfx.collection.LiveList.QuasiModificationObserver;
import org.reactfx.util.AccumulatorSize;
import org.reactfx.util.Experimental;
import org.reactfx.util.WrapperBase;
import org.reactfx.value.Val;
/**
* Adds additional methods to {@link ObservableList}.
*
* @param type of list elements
*/
public interface LiveList
extends ObservableList, Observable> {
/* ************ *
* Nested Types *
* ************ */
public interface Observer {
AccumulatorSize sizeOf(ListModificationSequence extends E> mods);
O headOf(ListModificationSequence extends E> mods);
ListModificationSequence tailOf(ListModificationSequence mods);
void onChange(O change);
}
@FunctionalInterface
public interface QuasiChangeObserver
extends Observer> {
@Override
default AccumulatorSize sizeOf(
ListModificationSequence extends E> mods) {
return AccumulatorSize.ONE;
}
@Override
default QuasiListChange extends E> headOf(
ListModificationSequence extends E> mods) {
return mods.asListChange();
}
@Override
default ListModificationSequence tailOf(
ListModificationSequence mods) {
throw new NoSuchElementException();
}
}
@FunctionalInterface
public interface QuasiModificationObserver
extends Observer> {
@Override
default AccumulatorSize sizeOf(
ListModificationSequence extends E> mods) {
return AccumulatorSize.fromInt(mods.getModificationCount());
}
@Override
default QuasiListModification extends E> headOf(
ListModificationSequence extends E> mods) {
return mods.getModifications().get(0);
}
@Override
default ListModificationSequence tailOf(
ListModificationSequence mods) {
return mods.asListChangeAccumulator().drop(1);
}
}
/* *************** *
* Default Methods *
* *************** */
default void addQuasiChangeObserver(QuasiChangeObserver super E> observer) {
addObserver(observer);
}
default void removeQuasiChangeObserver(QuasiChangeObserver super E> observer) {
removeObserver(observer);
}
default void addQuasiModificationObserver(QuasiModificationObserver super E> observer) {
addObserver(observer);
}
default void removeQuasiModificationObserver(QuasiModificationObserver super E> observer) {
removeObserver(observer);
}
default void addChangeObserver(Consumer super ListChange extends E>> observer) {
addQuasiChangeObserver(new ChangeObserverWrapper<>(this, observer));
}
default void removeChangeObserver(Consumer super ListChange extends E>> observer) {
removeQuasiChangeObserver(new ChangeObserverWrapper<>(this, observer));
}
default void addModificationObserver(Consumer super ListModification extends E>> observer) {
addQuasiModificationObserver(new ModificationObserverWrapper<>(this, observer));
}
default void removeModificationObserver(Consumer super ListModification extends E>> observer) {
removeQuasiModificationObserver(new ModificationObserverWrapper<>(this, observer));
}
default Subscription observeQuasiChanges(QuasiChangeObserver super E> observer) {
addQuasiChangeObserver(observer);
return () -> removeQuasiChangeObserver(observer);
}
default Subscription observeQuasiModifications(QuasiModificationObserver super E> observer) {
addQuasiModificationObserver(observer);
return () -> removeQuasiModificationObserver(observer);
}
default Subscription observeChanges(Consumer super ListChange extends E>> observer) {
addChangeObserver(observer);
return () -> removeChangeObserver(observer);
}
default Subscription observeModifications(Consumer super ListModification extends E>> observer) {
addModificationObserver(observer);
return () -> removeModificationObserver(observer);
}
@Override
default void addListener(ListChangeListener super E> listener) {
addQuasiChangeObserver(new ChangeListenerWrapper<>(this, listener));
}
@Override
default void removeListener(ListChangeListener super E> listener) {
removeQuasiChangeObserver(new ChangeListenerWrapper<>(this, listener));
}
@Override
default void addListener(InvalidationListener listener) {
addQuasiChangeObserver(new InvalidationListenerWrapper<>(this, listener));
}
@Override
default void removeListener(InvalidationListener listener) {
removeQuasiChangeObserver(new InvalidationListenerWrapper<>(this, listener));
}
default Subscription pin() {
return observeQuasiChanges(qc -> {});
}
default Val sizeProperty() {
return sizeOf(this);
}
default LiveList map(Function super E, ? extends F> f) {
return map(this, f);
}
default LiveList mapDynamic(
ObservableValue extends Function super E, ? extends F>> f) {
return mapDynamic(this, f);
}
default SuspendableList suspendable() {
return suspendable(this);
}
default MemoizationList memoize() {
return memoize(this);
}
default Val reduce(BinaryOperator reduction) {
return reduce(this, reduction);
}
@Experimental
default Val reduceRange(
ObservableValue range, BinaryOperator reduction) {
return reduceRange(this, range, reduction);
}
@Experimental
default Val collapse(Function super List, ? extends T> f) {
return collapse(this, f);
}
@Experimental
default Val collapseDynamic(
ObservableValue extends Function super List, ? extends T>> f) {
return collapseDynamic(this, f);
}
default EventStream> quasiChanges() {
return new EventStreamBase>() {
@Override
protected Subscription observeInputs() {
return observeQuasiChanges(this::emit);
}
};
}
default EventStream> changes() {
return quasiChanges().map(qc -> QuasiListChange.instantiate(qc, this));
}
default EventStream> quasiModifications() {
return new EventStreamBase>() {
@Override
protected Subscription observeInputs() {
return observeQuasiModifications(this::emit);
}
};
}
default EventStream> modifications() {
return quasiModifications().map(qm -> QuasiListModification.instantiate(qm, this));
}
/* ************** *
* Static Methods *
* ************** */
static Subscription observeQuasiChanges(
ObservableList extends E> list,
QuasiChangeObserver super E> observer) {
if(list instanceof LiveList) {
LiveList extends E> lst = (LiveList extends E>) list;
return lst.observeQuasiChanges(observer);
} else {
ListChangeListener listener = ch -> {
QuasiListChange extends E> change = QuasiListChange.from(ch);
observer.onChange(change);
};
list.addListener(listener);
return () -> list.removeListener(listener);
}
}
static Subscription observeChanges(
ObservableList list,
Consumer super ListChange extends E>> observer) {
return observeQuasiChanges(
list, qc -> observer.accept(QuasiListChange.instantiate(qc, list)));
}
static EventStream> quasiChangesOf(
ObservableList list) {
if(list instanceof LiveList) {
LiveList lst = (LiveList) list;
return lst.quasiChanges();
} else {
return new EventStreamBase>() {
@Override
protected Subscription observeInputs() {
return LiveList.observeQuasiChanges(list, this::emit);
}
};
}
}
static EventStream> changesOf(ObservableList list) {
return quasiChangesOf(list).map(qc -> QuasiListChange.instantiate(qc, list));
}
static Val sizeOf(ObservableList> list) {
return Val.create(() -> list.size(), list);
}
static LiveList map(
ObservableList extends E> list,
Function super E, ? extends F> f) {
return new MappedList<>(list, f);
}
static LiveList mapDynamic(
ObservableList extends E> list,
ObservableValue extends Function super E, ? extends F>> f) {
return new DynamicallyMappedList<>(list, f);
}
static SuspendableList suspendable(ObservableList list) {
if(list instanceof SuspendableList) {
return (SuspendableList) list;
} else {
return new SuspendableListWrapper<>(list);
}
}
static MemoizationList memoize(ObservableList list) {
if(list instanceof MemoizationList) {
return (MemoizationList) list;
} else {
return new MemoizationListImpl<>(list);
}
}
static Val reduce(
ObservableList list, BinaryOperator reduction) {
return new ListReduction<>(list, reduction);
}
@Experimental
static Val reduceRange(
ObservableList list,
ObservableValue range,
BinaryOperator reduction) {
return new ListRangeReduction<>(list, range, reduction);
}
@Experimental
static Val collapse(
ObservableList extends E> list,
Function super List, ? extends T> f) {
return Val.create(() -> f.apply(Collections.unmodifiableList(list)), list);
}
@Experimental
static Val collapseDynamic(
ObservableList extends E> list,
ObservableValue extends Function super List, ? extends T>> f) {
return Val.create(
() -> f.getValue().apply(Collections.unmodifiableList(list)),
list, f);
}
/**
* Returns a {@linkplain LiveList} view of the given
* {@linkplain ObservableValue} {@code obs}. The returned list will have
* size 1 when the given observable value is not {@code null} and size 0
* otherwise.
*/
static LiveList wrapVal(ObservableValue obs) {
return new ValAsList<>(obs);
}
}
class ChangeObserverWrapper
extends WrapperBase>>
implements QuasiChangeObserver {
private final ObservableList list;
ChangeObserverWrapper(
ObservableList list,
Consumer super ListChange extends T>> delegate) {
super(delegate);
this.list = list;
}
@Override
public void onChange(QuasiListChange extends T> change) {
getWrappedValue().accept(QuasiListChange.instantiate(change, list));
}
}
class ModificationObserverWrapper
extends WrapperBase>>
implements QuasiModificationObserver {
private final ObservableList list;
ModificationObserverWrapper(
ObservableList list,
Consumer super ListModification extends T>> delegate) {
super(delegate);
this.list = list;
}
@Override
public void onChange(QuasiListModification extends T> change) {
getWrappedValue().accept(QuasiListModification.instantiate(change, list));
}
}
class InvalidationListenerWrapper
extends WrapperBase
implements QuasiChangeObserver {
private final ObservableList list;
public InvalidationListenerWrapper(
ObservableList list,
InvalidationListener listener) {
super(listener);
this.list = list;
}
@Override
public void onChange(QuasiListChange extends T> change) {
getWrappedValue().invalidated(list);
}
}
class ChangeListenerWrapper
extends WrapperBase>
implements QuasiChangeObserver {
private final ObservableList list;
public ChangeListenerWrapper(
ObservableList list,
ListChangeListener super T> listener) {
super(listener);
this.list = list;
}
@Override
public void onChange(QuasiListChange extends T> change) {
List extends QuasiListModification extends T>> modifications =
change.getModifications();
if(modifications.isEmpty()) {
return;
}
getWrappedValue().onChanged(new ListChangeListener.Change(list) {
private int current = -1;
@Override
public int getFrom() {
return modifications.get(current).getFrom();
}
@Override
protected int[] getPermutation() {
return new int[0]; // not a permutation
}
/* Can change to List extends E> and remove unsafe cast when
* https://javafx-jira.kenai.com/browse/RT-39683 is resolved. */
@Override
@SuppressWarnings("unchecked")
public List getRemoved() {
// cast is safe, because the list is unmodifiable
return (List) modifications.get(current).getRemoved();
}
@Override
public int getTo() {
return modifications.get(current).getTo();
}
@Override
public boolean next() {
if(current + 1 < modifications.size()) {
++current;
return true;
} else {
return false;
}
}
@Override
public void reset() {
current = -1;
}
});
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy