package org.codefx.libfx.collection.transform;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
/**
* A {@link Map} which decorates another map and transforms the key and value types from the inner types {@code IK},
* {@code IV} to outer types {@code OK}, {@code OV}.
* <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 tokens of the inner and the outer types 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 keys and values are allowed unless the inner map 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 java.util.stream.Stream streams} returned by this map's views are told to
* {@link java.util.stream.Stream#sorted() sort} themself, they will do so on the base of the comparator returned by the
* inner map view's spliterator (e.g. based on the natural order of {@code IK} or {@code IV} if it has one).
* <p>
* {@code TransformingMap}s are created with a {@link TransformingMapBuilder}.
*
* @param <IK>
* the inner key type, i.e. the type of the keys contained in the wrapped/inner map
* @param <OK>
* the outer key type, i.e. the type of keys appearing to be in this map
* @param <IV>
* the inner value type, i.e. the type of the values contained in the wrapped/inner map
* @param <OV>
* the outer value type, i.e. the type of values appearing to be in this map
*/
public final class TransformingMap<IK, OK, IV, OV> extends AbstractTransformingMap<IK, OK, IV, OV> {
// #begin FIELDS
private final Map<IK, IV> innerMap;
private final Class<? super OK> outerKeyTypeToken;
private final Class<? super IK> innerKeyTypeToken;
private final Function<? super IK, ? extends OK> transformToOuterKey;
private final Function<? super OK, ? extends IK> transformToInnerKey;
private final Class<? super OV> outerValueTypeToken;
private final Class<? super IV> innerValueTypeToken;
private final Function<? super IV, ? extends OV> transformToOuterValue;
private final Function<? super OV, ? extends IV> transformToInnerValue;
// #end FIELDS
// #begin CONSTRUCTION
/**
* Creates a new transforming map.
*
* @param innerMap
* the wrapped map
* @param innerKeyTypeToken
* the token for the inner key type
* @param outerKeyTypeToken
* the token for the outer key type
* @param transformToOuterKey
* transforms a key from an inner to an outer key type; will never be called with null argument and must
* not produce null
* @param transformToInnerKey
* transforms a key from an outer to an inner key type; will never be called with null argument and must
* not produce null
* @param innerValueTypeToken
* the token for the inner value type
* @param outerValueTypeToken
* the token for the outer value type
* @param transformToOuterValue
* transforms a value from an inner to an outer value type; will never be called with null argument and
* must not produce null
* @param transformToInnerValue
* transforms a value from an outer to an inner value type; will never be called with null argument and
* must not produce null
*/
TransformingMap(
Map<IK, IV> innerMap,
Class<? super IK> innerKeyTypeToken, Class<? super OK> outerKeyTypeToken,
Function<? super IK, ? extends OK> transformToOuterKey,
Function<? super OK, ? extends IK> transformToInnerKey,
Class<? super IV> innerValueTypeToken, Class<? super OV> outerValueTypeToken,
Function<? super IV, ? extends OV> transformToOuterValue,
Function<? super OV, ? extends IV> transformToInnerValue) {
Objects.requireNonNull(innerMap, "The argument 'innerMap' must not be null.");
Objects.requireNonNull(innerKeyTypeToken, "The argument 'innerKeyTypeToken' must not be null.");
Objects.requireNonNull(outerKeyTypeToken, "The argument 'outerKeyTypeToken' must not be null.");
Objects.requireNonNull(transformToOuterKey, "The argument 'transformToOuterKey' must not be null.");
Objects.requireNonNull(transformToInnerKey, "The argument 'transformToInnerKey' must not be null.");
Objects.requireNonNull(innerValueTypeToken, "The argument 'innerValueTypeToken' must not be null.");
Objects.requireNonNull(outerValueTypeToken, "The argument 'outerValueTypeToken' must not be null.");
Objects.requireNonNull(transformToOuterValue, "The argument 'transformToOuterValue' must not be null.");
Objects.requireNonNull(transformToInnerValue, "The argument 'transformToInnerValue' must not be null.");
this.innerMap = innerMap;
this.outerKeyTypeToken = outerKeyTypeToken;
this.innerKeyTypeToken = innerKeyTypeToken;
this.transformToOuterKey = transformToOuterKey;
this.transformToInnerKey = transformToInnerKey;
this.outerValueTypeToken = outerValueTypeToken;
this.innerValueTypeToken = innerValueTypeToken;
this.transformToOuterValue = transformToOuterValue;
this.transformToInnerValue = transformToInnerValue;
}
// #end CONSTRUCTION
// #begin IMPLEMENTATION OF 'AbstractTransformingMap'
@Override
protected Map<IK, IV> getInnerMap() {
return innerMap;
}
@Override
protected boolean isInnerKey(Object object) {
return object == null || innerKeyTypeToken.isInstance(object);
}
@Override
protected OK transformToOuterKey(IK innerKey) {
if (innerKey == null)
return null;
OK outerKey = transformToOuterKey.apply(innerKey);
Objects.requireNonNull(outerKey, "The transformation must not create null instances.");
return outerKey;
}
@Override
protected boolean isOuterKey(Object object) {
return object == null || outerKeyTypeToken.isInstance(object);
}
@Override
protected IK transformToInnerKey(OK outerKey) {
if (outerKey == null)
return null;
IK innerKey = transformToInnerKey.apply(outerKey);
Objects.requireNonNull(innerKey, "The transformation must not create null instances.");
return innerKey;
}
@Override
protected boolean isInnerValue(Object object) {
return object == null || innerValueTypeToken.isInstance(object);
}
@Override
protected OV transformToOuterValue(IV innerValue) {
if (innerValue == null)
return null;
OV outerValue = transformToOuterValue.apply(innerValue);
Objects.requireNonNull(outerValue, "The transformation must not create null instances.");
return outerValue;
}
@Override
protected boolean isOuterValue(Object object) {
return object == null || outerValueTypeToken.isInstance(object);
}
@Override
protected IV transformToInnerValue(OV outerValue) {
if (outerValue == null)
return null;
IV innerValue = transformToInnerValue.apply(outerValue);
Objects.requireNonNull(innerValue, "The transformation must not create null instances.");
return innerValue;
}
// #end IMPLEMENTATION OF 'AbstractTransformingMap'
}