package org.codefx.libfx.collection.transform;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
/**
* A {@link List} which decorates another list and transforms the element type from the inner type {@code I} to an outer
* type {@code O}.
* <p>
* See the {@link org.codefx.libfx.collection.transform package} documentation for general comments on transformation.
* <p>
* This implementation mitigates the type safety problems by using a token of the inner and the outer type to check
* instances against them. This solves some of the critical situations but not all of them. In those other cases
* {@link ClassCastException}s might occur when an element can not be transformed by the transformation functions.
* <p>
* Null elements are allowed unless the inner list does not accept them. These are handled explicitly and fixed to the
* transformation {@code null -> null}. The transforming functions specified during construction neither have to handle
* that case nor are they allowed to produce null elements.
* <p>
* If the {@link #stream() stream} returned by this list is told to {@link java.util.stream.Stream#sorted() sort}
* itself, it will do so on the base of the comparator returned by the inner list's spliterator (e.g. based on the
* natural order of {@code O} if it has one).
* <p>
* {@code TransformingList}s are created with a {@link TransformingCollectionBuilder}.
*
* @param <I>
* the inner type, i.e. the type of the elements contained in the wrapped/inner collection
* @param <O>
* the outer type, i.e. the type of elements appearing to be in this collection
*/
public final class TransformingList<I, O> extends AbstractTransformingList<I, O> {
// #begin FIELDS
private final List<I> innerList;
private final Class<? super I> innerTypeToken;
private final Class<? super O> outerTypeToken;
private final Function<? super I, ? extends O> transformToOuter;
private final Function<? super O, ? extends I> transformToInner;
// #end FIELDS
/**
* Creates a new transforming list.
*
* @param innerList
* the wrapped list
* @param innerTypeToken
* the token for the inner type
* @param outerTypeToken
* the token for the outer type
* @param transformToOuter
* transforms an element from an inner to an outer type; will never be called with null argument and must
* not produce null
* @param transformToInner
* transforms an element from an outer to an inner type; will never be called with null argument and must
* not produce null
*/
public TransformingList(
List<I> innerList,
Class<? super I> innerTypeToken, Class<? super O> outerTypeToken,
Function<? super I, ? extends O> transformToOuter, Function<? super O, ? extends I> transformToInner) {
Objects.requireNonNull(innerList, "The argument 'innerList' must not be null.");
Objects.requireNonNull(innerTypeToken, "The argument 'innerTypeToken' must not be null.");
Objects.requireNonNull(outerTypeToken, "The argument 'outerTypeToken' must not be null.");
Objects.requireNonNull(transformToOuter, "The argument 'transformToOuter' must not be null.");
Objects.requireNonNull(transformToInner, "The argument 'transformToInner' must not be null.");
this.innerList = innerList;
this.innerTypeToken = innerTypeToken;
this.outerTypeToken = outerTypeToken;
this.transformToOuter = transformToOuter;
this.transformToInner = transformToInner;
}
// #begin IMPLEMENTATION OF 'AbstractTransformingList'
@Override
protected List<I> getInnerList() {
return innerList;
}
@Override
protected boolean isInnerElement(Object object) {
return object == null || innerTypeToken.isInstance(object);
}
@Override
protected O transformToOuter(I innerElement) {
if (innerElement == null)
return null;
O outerElement = transformToOuter.apply(innerElement);
Objects.requireNonNull(outerElement, "The transformation must not create null instances.");
return outerElement;
}
@Override
protected boolean isOuterElement(Object object) {
return object == null || outerTypeToken.isInstance(object);
}
@Override
protected I transformToInner(O outerElement) {
if (outerElement == null)
return null;
I innerElement = transformToInner.apply(outerElement);
Objects.requireNonNull(innerElement, "The transformation must not create null instances.");
return innerElement;
}
// #end IMPLEMENTATION OF 'AbstractTransformingList'
}