package org.codefx.libfx.collection.transform;
import java.util.Comparator;
import java.util.Spliterator;
import java.util.function.Consumer;
/**
* Abstract superclass to {@link Spliterator}s which wrap another spliterator and transform the returned elements from
* their inner type {@code I} to an outer type {@code O}.
* <p>
* Note that this spliterator reports the exact same {@link Spliterator#SORTED SORTED} {@link #characteristics()
* characteristic} as the inner one. It's {@link #getComparator()} transforms the elements it should compare from the
* outer to the inner type and calls the inner spliterator's {@link Spliterator#getComparator() comparator} with it.
* This means that sorting streams is always done by the inner spliterator's logic.
*
* @param <I>
* the inner type, i.e. the type of the elements returned by the wrapped/inner spliterator
* @param <O>
* the outer type, i.e. the type of elements returned by this spliterator
*/
abstract class AbstractTransformingSpliterator<I, O> implements Spliterator<O> {
// #begin IMPLEMENTATION OF 'Spliterator<O>'
@Override
public boolean tryAdvance(Consumer<? super O> action) {
Consumer<I> transformThenAction = transformThen(action);
return getInnerSpliterator().tryAdvance(transformThenAction);
}
@Override
public void forEachRemaining(Consumer<? super O> action) {
Consumer<I> transformThenAction = transformThen(action);
getInnerSpliterator().forEachRemaining(transformThenAction);
}
@Override
public Spliterator<O> trySplit() {
Spliterator<I> newSpliterator = getInnerSpliterator().trySplit();
if (newSpliterator == null)
return null;
else
return wrapNewSpliterator(newSpliterator);
}
@Override
public long estimateSize() {
return getInnerSpliterator().estimateSize();
}
@Override
public long getExactSizeIfKnown() {
return getInnerSpliterator().getExactSizeIfKnown();
}
@Override
public int characteristics() {
return getInnerSpliterator().characteristics();
}
@Override
public boolean hasCharacteristics(int characteristics) {
return getInnerSpliterator().hasCharacteristics(characteristics);
}
@Override
public Comparator<? super O> getComparator() {
Comparator<? super I> innerComparator = getInnerSpliterator().getComparator();
if (innerComparator == null)
return null;
return (leftOuter, rightOuter) -> {
I leftInner = transformToInner(leftOuter);
I rightInner = transformToInner(rightOuter);
return innerComparator.compare(leftInner, rightInner);
};
}
// #end IMPLEMENTATION OF 'Spliterator<O>'
// #begin ABSTRACT METHODS
/**
* @return the wrapped/inner spliterator
*/
protected abstract Spliterator<I> getInnerSpliterator();
/**
* Transforms an element from the inner type {@code I} to the outer type {@code O}.
*
* @param innerElement
* an element returned by the {@link #getInnerSpliterator() innerSpliterator}
* @return an equivalent element of type {@code O}
*/
protected abstract O transformToOuter(I innerElement);
/**
* Transforms an element from the outer type {@code O} to the inner type {@code I}.
*
* @param outerElement
* an element of type {@code O}
* @return an equivalent element of type {@code I}
*/
protected abstract I transformToInner(O outerElement);
/**
* Transforms the specified element of type {@code I} with {@link #transformToOuter(Object) transformToOuter} before
* passing it to the specified consumer.
*
* @param action
* the {@link Consumer} of outer elements to which the transformed element will be passed
* @return a {@link Consumer} of inner elements
*/
private Consumer<I> transformThen(Consumer<? super O> action) {
return innerElement -> {
O asOuterElement = transformToOuter(innerElement);
action.accept(asOuterElement);
};
}
/**
* Wraps the specified spliterator over {@code I} into a spliterator over {@code O}.
* <p>
* This method is called inside {@link #trySplit()}. It is not called with null.
*
* @param newSpliterator
* the newly created inner {@link Spliterator Spliterator<I>}
* @return a {@link Spliterator Spliterator<O>}
*/
protected abstract Spliterator<O> wrapNewSpliterator(Spliterator<I> newSpliterator);
// #end ABSTRACT METHODS
}