org.reactfx.collection.MemoizationList 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
package org.reactfx.collection;
import java.util.List;
import java.util.Optional;
import javafx.collections.ObservableList;
import javafx.scene.control.IndexRange;
import org.reactfx.Subscription;
import org.reactfx.util.Lists;
import org.reactfx.util.SparseList;
public interface MemoizationList extends LiveList {
LiveList memoizedItems();
boolean isMemoized(int index);
Optional getIfMemoized(int index);
int getMemoizedCount();
int getMemoizedCountBefore(int position);
int getMemoizedCountAfter(int position);
void forget(int from, int to);
int indexOfMemoizedItem(int index);
IndexRange getMemoizedItemsRange();
void force(int from, int to);
}
class MemoizationListImpl
extends LiveListBase
implements MemoizationList, ReadOnlyLiveListImpl {
private class MemoizedView
extends LiveListBase
implements ReadOnlyLiveListImpl {
@Override
protected Subscription observeInputs() {
return MemoizationListImpl.this.pin();
}
@Override
public E get(int index) {
return sparseList.getPresent(index);
}
@Override
public int size() {
return sparseList.getPresentCount();
}
private void prepareNotifications(QuasiListChange extends E> change) {
enqueueNotifications(change);
}
private void publishNotifications() {
notifyObservers();
}
}
private final SparseList sparseList = new SparseList<>();
private final MemoizedView memoizedItems = new MemoizedView();
private final ObservableList source;
MemoizationListImpl(ObservableList source) {
this.source = source;
}
@Override
protected Subscription observeInputs() {
sparseList.insertVoid(0, source.size());
return LiveList.observeQuasiChanges(source, this::sourceChanged)
.and(sparseList::clear);
}
private void sourceChanged(QuasiListChange extends E> qc) {
ListChangeAccumulator acc = new ListChangeAccumulator<>();
for(QuasiListModification extends E> mod: qc) {
int from = mod.getFrom();
int removedSize = mod.getRemovedSize();
int memoFrom = sparseList.getPresentCountBefore(from);
List memoRemoved = sparseList.collect(from, from + removedSize);
sparseList.spliceByVoid(from, from + removedSize, mod.getAddedSize());
acc.add(new QuasiListModificationImpl<>(memoFrom, memoRemoved, 0));
}
memoizedItems.prepareNotifications(acc.fetch());
notifyObservers(qc);
memoizedItems.publishNotifications();
}
@Override
public E get(int index) {
if(!isObservingInputs()) { // memoization is off
return source.get(index);
} else if(sparseList.isPresent(index)) {
return sparseList.getOrThrow(index);
} else {
E elem = source.get(index); // may cause recursive get(), so we
// need to check again for absence
if(sparseList.setIfAbsent(index, elem)) {
memoizedItems.fireElemInsertion(
sparseList.getPresentCountBefore(index));
}
return elem;
}
}
@Override
public void force(int from, int to) {
if(!isObservingInputs()) { // memoization is off
throw new IllegalStateException(
"Cannot force items when memoization is off."
+ " To turn memoization on, you have to be observing this"
+ " list or its memoizedItems.");
}
Lists.checkRange(from, to, size());
int presentBefore = sparseList.getPresentCountBefore(from);
ListChangeAccumulator mods = new ListChangeAccumulator<>();
for(int i = from; i < to; ++i) {
if(!sparseList.isPresent(i)) {
E elem = source.get(i);
if(sparseList.setIfAbsent(i, elem)) {
mods.add(LiveListHelpers.elemInsertion(presentBefore + (i - from)));
}
}
}
if(!mods.isEmpty()) {
memoizedItems.notifyObservers(mods.fetch());
}
}
@Override
public int size() {
return source.size();
}
@Override
public LiveList memoizedItems() {
return memoizedItems;
}
@Override
public boolean isMemoized(int index) {
return isObservingInputs() && sparseList.isPresent(index);
}
@Override
public Optional getIfMemoized(int index) {
Lists.checkIndex(index, size());
return isObservingInputs()
? sparseList.get(index)
: Optional.empty();
}
@Override
public int getMemoizedCountBefore(int position) {
Lists.checkPosition(position, size());
return isObservingInputs()
? sparseList.getPresentCountBefore(position)
: 0;
}
@Override
public int getMemoizedCountAfter(int position) {
Lists.checkPosition(position, size());
return isObservingInputs()
? sparseList.getPresentCountAfter(position)
: 0;
}
@Override
public int getMemoizedCount() {
return memoizedItems.size();
}
@Override
public void forget(int from, int to) {
if(!isObservingInputs()) { // memoization is off
throw new IllegalStateException(
"There is nothing to forget, because memoization is off."
+ " To turn memoization on, you have to be observing this"
+ " list or its memoizedItems.");
}
Lists.checkRange(from, to, size());
int memoChangeFrom = sparseList.getPresentCountBefore(from);
List memoRemoved = sparseList.collect(from, to);
sparseList.spliceByVoid(from, to, to - from);
memoizedItems.fireRemoveRange(memoChangeFrom, memoRemoved);
}
@Override
public int indexOfMemoizedItem(int index) {
return sparseList.indexOfPresentItem(index);
}
@Override
public IndexRange getMemoizedItemsRange() {
return sparseList.getPresentItemsRange();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy