org.reactfx.util.ListHelper 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.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.IntFunction;
public abstract class ListHelper {
public static T get(ListHelper listHelper, int index) {
Lists.checkIndex(index, size(listHelper)); // always throws for listHelper == null
return listHelper.get(index);
}
public static ListHelper add(ListHelper listHelper, T elem) {
if(listHelper == null) {
return new SingleElemHelper<>(elem);
} else {
return listHelper.add(elem);
}
}
public static ListHelper remove(ListHelper listHelper, T elem) {
if(listHelper == null) {
return listHelper;
} else {
return listHelper.remove(elem);
}
}
public static void forEach(ListHelper listHelper, Consumer f) {
if(listHelper != null) {
listHelper.forEach(f);
}
}
public static void forEachBetween(
ListHelper listHelper, int from, int to, Consumer f) {
Lists.checkRange(from, to, size(listHelper));
if(from < to) {
listHelper.forEachBetween(from, to, f);
}
}
public static Iterator iterator(ListHelper listHelper) {
if(listHelper != null) {
return listHelper.iterator();
} else {
return Collections.emptyIterator();
}
}
public static Iterator iterator(ListHelper listHelper, int from, int to) {
Lists.checkRange(from, to, size(listHelper));
if(from < to) {
return listHelper.iterator(from, to);
} else {
return Collections.emptyIterator();
}
}
public static Optional reduce(ListHelper listHelper, BinaryOperator f) {
if(listHelper == null) {
return Optional.empty();
} else {
return listHelper.reduce(f);
}
}
public static U reduce(ListHelper listHelper, U unit, BiFunction f) {
if(listHelper == null) {
return unit;
} else {
return listHelper.reduce(unit, f);
}
}
public static T[] toArray(ListHelper listHelper, IntFunction allocator) {
if(listHelper == null) {
return allocator.apply(0);
} else {
return listHelper.toArray(allocator);
}
}
public static boolean isEmpty(ListHelper listHelper) {
return listHelper == null;
}
public static int size(ListHelper listHelper) {
if(listHelper == null) {
return 0;
} else {
return listHelper.size();
}
}
private ListHelper() {
// private constructor to prevent subclassing
};
abstract T get(int index);
abstract ListHelper add(T elem);
abstract ListHelper remove(T elem);
abstract void forEach(Consumer f);
abstract void forEachBetween(int from, int to, Consumer f);
abstract Iterator iterator();
abstract Iterator iterator(int from, int to);
abstract Optional reduce(BinaryOperator f);
abstract U reduce(U unit, BiFunction f);
abstract T[] toArray(IntFunction allocator);
abstract int size();
private static class SingleElemHelper extends ListHelper {
private final T elem;
SingleElemHelper(T elem) {
this.elem = elem;
}
@Override
T get(int index) {
assert index == 0;
return elem;
}
@Override
ListHelper add(T elem) {
return new MultiElemHelper<>(this.elem, elem);
}
@Override
ListHelper remove(T elem) {
if(Objects.equals(this.elem, elem)) {
return null;
} else {
return this;
}
}
@Override
void forEach(Consumer f) {
f.accept(elem);
}
@Override
void forEachBetween(int from, int to, Consumer f) {
assert from == 0 && to == 1;
f.accept(elem);
}
@Override
Iterator iterator() {
return new Iterator() {
boolean hasNext = true;
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public T next() {
if(hasNext) {
hasNext = false;
return elem;
} else {
throw new NoSuchElementException();
}
}
};
}
@Override
Iterator iterator(int from, int to) {
assert from == 0 && to == 1;
return iterator();
}
@Override
Optional reduce(BinaryOperator f) {
return Optional.of(elem);
}
@Override
U reduce(U unit, BiFunction f) {
return f.apply(unit, elem);
}
@Override
T[] toArray(IntFunction allocator) {
T[] res = allocator.apply(1);
res[0] = elem;
return res;
}
@Override
int size() {
return 1;
}
}
private static class MultiElemHelper extends ListHelper {
private final List elems;
// when > 0, this ListHelper must be immutable,
// i.e. use copy-on-write for mutating operations
private int iterating = 0;
@SafeVarargs
MultiElemHelper(T... elems) {
this(Arrays.asList(elems));
}
private MultiElemHelper(List elems) {
this.elems = new ArrayList<>(elems);
}
private MultiElemHelper copy() {
return new MultiElemHelper<>(elems);
}
@Override
T get(int index) {
return elems.get(index);
}
@Override
ListHelper add(T elem) {
if(iterating > 0) {
return copy().add(elem);
} else {
elems.add(elem);
return this;
}
}
@Override
ListHelper remove(T elem) {
int idx = elems.indexOf(elem);
if(idx == -1) {
return this;
} else {
switch(elems.size()) {
case 0: // fall through
case 1: throw new AssertionError();
case 2: return new SingleElemHelper<>(elems.get(1-idx));
default:
if(iterating > 0) {
return copy().remove(elem);
} else {
elems.remove(elem);
return this;
}
}
}
}
@Override
void forEach(Consumer f) {
++iterating;
try {
elems.forEach(f);
} finally {
--iterating;
}
}
@Override
void forEachBetween(int from, int to, Consumer f) {
++iterating;
try {
elems.subList(from, to).forEach(f);
} finally {
--iterating;
}
}
@Override
Iterator iterator() {
return iterator(0, elems.size());
}
@Override
Iterator iterator(int from, int to) {
assert from < to;
++iterating;
return new Iterator() {
int next = from;
@Override
public boolean hasNext() {
return next < to;
}
@Override
public T next() {
if(next < to) {
T res = elems.get(next);
++next;
if(next == to) {
--iterating;
}
return res;
} else {
throw new NoSuchElementException();
}
}
};
}
@Override
Optional reduce(BinaryOperator f) {
return elems.stream().reduce(f);
}
@Override
U reduce(U unit, BiFunction f) {
U u = unit;
for(T elem: elems) {
u = f.apply(u, elem);
}
return u;
}
@Override
T[] toArray(IntFunction allocator) {
return elems.toArray(allocator.apply(size()));
}
@Override
int size() {
return elems.size();
}
}
}