package lighthouse.utils;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.TransformationList;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Maps elements of type F to E with change listeners working as expected.
*/
public class MappedList<E, F> extends TransformationList<E, F> {
private final Function<F, E> mapper;
private final ArrayList<E> mapped;
/**
* Creates a new MappedList list wrapped around the source list.
* Each element will have the given function applied to it, such that the list is cast through the mapper.
*/
public MappedList(ObservableList<? extends F> source, Function<F, E> mapper) {
super(source);
this.mapper = mapper;
this.mapped = new ArrayList<>(source.size());
mapAll();
}
private void mapAll() {
mapped.clear();
for (F val : getSource())
mapped.add(mapper.apply(val));
}
@Override
protected void sourceChanged(ListChangeListener.Change<? extends F> c) {
// Is all this stuff right for every case? Probably it doesn't matter for this app.
beginChange();
while (c.next()) {
if (c.wasPermutated()) {
int[] perm = new int[c.getTo() - c.getFrom()];
for (int i = c.getFrom(); i < c.getTo(); i++)
perm[i - c.getFrom()] = c.getPermutation(i);
nextPermutation(c.getFrom(), c.getTo(), perm);
} else if (c.wasUpdated()) {
for (int i = c.getFrom(); i < c.getTo(); i++) {
remapIndex(i);
nextUpdate(i);
}
} else {
if (c.wasRemoved()) {
// Removed should come first to properly handle replacements, then add.
List<E> removed = mapped.subList(c.getFrom(), c.getFrom() + c.getRemovedSize());
ArrayList<E> duped = new ArrayList<>(removed);
removed.clear();
nextRemove(c.getFrom(), duped);
}
if (c.wasAdded()) {
for (int i = c.getFrom(); i < c.getTo(); i++) {
mapped.addAll(c.getFrom(), c.getAddedSubList().stream().map(mapper).collect(Collectors.toList()));
remapIndex(i);
}
nextAdd(c.getFrom(), c.getTo());
}
}
}
endChange();
}
private void remapIndex(int i) {
if (i >= mapped.size()) {
for (int j = mapped.size(); j <= i; j++) {
mapped.add(mapper.apply(getSource().get(j)));
}
}
mapped.set(i, mapper.apply(getSource().get(i)));
}
@Override
public int getSourceIndex(int index) {
return index;
}
@Override
public E get(int index) {
return mapped.get(index);
}
@Override
public int size() {
return mapped.size();
}
}