package mhfc.net.common.util.parsing;
import java.util.Objects;
import java.util.function.BooleanSupplier;
import java.util.function.DoubleSupplier;
import java.util.function.Function;
import java.util.function.IntSupplier;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
import mhfc.net.MHFCMain;
import mhfc.net.common.util.ExceptionLessFunctions;
import mhfc.net.common.util.ExceptionLessFunctions.ThrowingSupplier;
import mhfc.net.common.util.function.ByteSupplier;
import mhfc.net.common.util.function.CharSupplier;
import mhfc.net.common.util.function.FloatSupplier;
import mhfc.net.common.util.function.ShortSupplier;
import mhfc.net.common.util.parsing.valueholders.Any;
/**
* A Holder is the <b>immutable</b> version of an Any and the general basecase.
*
* @author WorldSEnder
*/
public final class Holder implements IValueHolder {
private static class FailedComputationTag {}
private static class ClassTag {}
private static FailedComputationTag failedTag = new FailedComputationTag();
private static ClassTag classTag = new ClassTag();
private static interface Wrapper {
/**
*
* @return <code>null</code> if not convertible, else a supplier
*/
BooleanSupplier asBool();
CharSupplier asChar();
ByteSupplier asByte();
ShortSupplier asShort();
IntSupplier asInt();
LongSupplier asLong();
FloatSupplier asFloat();
DoubleSupplier asDouble();
<T> Supplier<T> asObject(Class<T> clazz);
Class<?> getWrappedClass();
Throwable checkError();
}
private static abstract class BasicWrapper implements Wrapper {
@SuppressWarnings("unchecked")
public <T> Supplier<T> asBoxedPrimitive(Class<T> clazz) {
if (!clazz.isPrimitive()) {
throw new IllegalArgumentException("clazz must be primitive");
}
if (boolean.class.equals(clazz)) {
BooleanSupplier supp = asBool();
return supp == null ? null : () -> (T) Boolean.valueOf(supp.getAsBoolean());
}
if (char.class.equals(clazz)) {
CharSupplier supp = asChar();
return supp == null ? null : () -> (T) Character.valueOf(supp.getAsChar());
}
if (byte.class.equals(clazz)) {
ByteSupplier supp = asByte();
return supp == null ? null : () -> (T) Byte.valueOf(supp.getAsByte());
}
if (short.class.equals(clazz)) {
ShortSupplier supp = asShort();
return supp == null ? null : () -> (T) Short.valueOf(supp.getAsShort());
}
if (int.class.equals(clazz)) {
IntSupplier supp = asInt();
return supp == null ? null : () -> (T) Integer.valueOf(supp.getAsInt());
}
if (long.class.equals(clazz)) {
LongSupplier supp = asLong();
return supp == null ? null : () -> (T) Long.valueOf(supp.getAsLong());
}
if (float.class.equals(clazz)) {
FloatSupplier supp = asFloat();
return supp == null ? null : () -> (T) Float.valueOf(supp.getAsFloat());
}
if (double.class.equals(clazz)) {
DoubleSupplier supp = asDouble();
return supp == null ? null : () -> (T) Double.valueOf(supp.getAsDouble());
}
throw new IllegalArgumentException("Unreachable. Did java add another primitive type?");
}
}
private static abstract class PrimitiveWrapper<B> extends BasicWrapper {
private Class<B> boxedClazz;
public PrimitiveWrapper() {
boxedClazz = Objects.requireNonNull(getBoxedClass());
}
protected abstract B boxed();
protected abstract Class<B> getBoxedClass();
@Override
public BooleanSupplier asBool() {
return null;
}
@Override
public CharSupplier asChar() {
return null;
}
@Override
public ByteSupplier asByte() {
return null;
}
@Override
public ShortSupplier asShort() {
return null;
}
@Override
public IntSupplier asInt() {
return null;
}
@Override
public LongSupplier asLong() {
return null;
}
@Override
public FloatSupplier asFloat() {
return null;
}
@Override
public DoubleSupplier asDouble() {
return null;
}
@SuppressWarnings("unchecked")
@Override
public <T> Supplier<T> asObject(Class<T> clazz) {
if (clazz.isPrimitive()) {
return asBoxedPrimitive(clazz);
}
// 5.2
// A boxing conversion, optionally followed by a widening reference conversion
if (clazz.isAssignableFrom(boxedClazz)) {
return () -> (T) boxed();
}
// TODO: maybe extend the cases of 5.2 to
// A widening primitive conversion, then a boxing conversion (no widening reference conversion)
return null;
}
@Override
public Throwable checkError() {
return null;
}
}
private static class BooleanWrapper extends PrimitiveWrapper<Boolean> {
private final boolean bool;
public BooleanWrapper(boolean bool) {
this.bool = bool;
}
@Override
public BooleanSupplier asBool() {
return () -> bool;
}
@Override
protected Boolean boxed() {
return Boolean.valueOf(bool);
}
@Override
protected Class<Boolean> getBoxedClass() {
return Boolean.class;
}
@Override
public Class<?> getWrappedClass() {
return boolean.class;
}
}
private static class CharWrapper extends PrimitiveWrapper<Character> {
private final char ch;
public CharWrapper(char ch) {
this.ch = ch;
}
@Override
protected Character boxed() {
return Character.valueOf(ch);
}
@Override
protected Class<Character> getBoxedClass() {
return Character.class;
}
@Override
public Class<?> getWrappedClass() {
return char.class;
}
}
private static class ByteWrapper extends PrimitiveWrapper<Byte> {
private final byte b;
public ByteWrapper(byte b) {
this.b = b;
}
@Override
public ByteSupplier asByte() {
return () -> this.b;
}
@Override
public ShortSupplier asShort() {
return () -> this.b;
}
@Override
public IntSupplier asInt() {
return () -> this.b;
}
@Override
public LongSupplier asLong() {
return () -> this.b;
}
@Override
public FloatSupplier asFloat() {
return () -> this.b;
}
@Override
public DoubleSupplier asDouble() {
return () -> this.b;
}
@Override
protected Byte boxed() {
return Byte.valueOf(b);
}
@Override
protected Class<Byte> getBoxedClass() {
return Byte.class;
}
@Override
public Class<?> getWrappedClass() {
return byte.class;
}
}
private static class ShortWrapper extends PrimitiveWrapper<Short> {
private final short sh;
public ShortWrapper(short sh) {
this.sh = sh;
}
@Override
public ShortSupplier asShort() {
return () -> this.sh;
}
@Override
public IntSupplier asInt() {
return () -> this.sh;
}
@Override
public LongSupplier asLong() {
return () -> this.sh;
}
@Override
public FloatSupplier asFloat() {
return () -> this.sh;
}
@Override
public DoubleSupplier asDouble() {
return () -> this.sh;
}
@Override
protected Short boxed() {
return Short.valueOf(sh);
}
@Override
protected Class<Short> getBoxedClass() {
return Short.class;
}
@Override
public Class<?> getWrappedClass() {
return short.class;
}
}
private static class IntWrapper extends PrimitiveWrapper<Integer> {
private final int i;
public IntWrapper(int i) {
this.i = i;
}
@Override
public IntSupplier asInt() {
return () -> this.i;
}
@Override
public LongSupplier asLong() {
return () -> this.i;
}
@Override
public FloatSupplier asFloat() {
return () -> this.i;
}
@Override
public DoubleSupplier asDouble() {
return () -> this.i;
}
@Override
protected Integer boxed() {
return Integer.valueOf(i);
}
@Override
protected Class<Integer> getBoxedClass() {
return Integer.class;
}
@Override
public Class<?> getWrappedClass() {
return int.class;
}
}
private static class LongWrapper extends PrimitiveWrapper<Long> {
private final long l;
public LongWrapper(long l) {
this.l = l;
}
@Override
public LongSupplier asLong() {
return () -> this.l;
}
@Override
public FloatSupplier asFloat() {
return () -> this.l;
}
@Override
public DoubleSupplier asDouble() {
return () -> this.l;
}
@Override
protected Long boxed() {
return Long.valueOf(l);
}
@Override
protected Class<Long> getBoxedClass() {
return Long.class;
}
@Override
public Class<?> getWrappedClass() {
return void.class;
}
}
private static class FloatWrapper extends PrimitiveWrapper<Float> {
private float f;
public FloatWrapper(float f) {
this.f = f;
}
@Override
public FloatSupplier asFloat() {
return () -> this.f;
}
@Override
public DoubleSupplier asDouble() {
return () -> this.f;
}
@Override
protected Float boxed() {
return Float.valueOf(f);
}
@Override
protected Class<Float> getBoxedClass() {
return Float.class;
}
@Override
public Class<?> getWrappedClass() {
return float.class;
}
}
private static class DoubleWrapper extends PrimitiveWrapper<Double> {
private final double d;
public DoubleWrapper(double d) {
this.d = d;
}
@Override
public DoubleSupplier asDouble() {
return () -> this.d;
}
@Override
protected Double boxed() {
return Double.valueOf(d);
}
@Override
protected Class<Double> getBoxedClass() {
return Double.class;
}
@Override
public Class<?> getWrappedClass() {
return double.class;
}
}
private static class VoidWrapper extends PrimitiveWrapper<Void> {
@Override
public <T> Supplier<T> asObject(Class<T> clazz) {
return null;
}
@Override
protected Void boxed() {
return null;
}
@Override
protected Class<Void> getBoxedClass() {
return Void.class;
}
@Override
public Class<?> getWrappedClass() {
return EMPTY_CLASS;
}
}
private static final Wrapper VOID_WRAPPER = new VoidWrapper();
private static class ThrowableWrapper extends VoidWrapper {
private final Throwable caught;
public ThrowableWrapper(Throwable thr) {
this.caught = thr;
}
@Override
public Throwable checkError() {
return this.caught;
}
}
private static class ObjectWrapper extends BasicWrapper {
private final Object obj;
private final Class<?> clazz;
private BooleanSupplier boolS;
private CharSupplier charS;
private ByteSupplier byteS;
private ShortSupplier shortS;
private IntSupplier intS;
private LongSupplier longS;
private FloatSupplier floatS;
private DoubleSupplier doubleS;
public ObjectWrapper(Object nonNull) {
this(nonNull, nonNull.getClass());
}
public ObjectWrapper(Class<?> of, ClassTag overload) {
this(null, of);
if (of.isPrimitive()) {
throw new IllegalArgumentException("of can't be primitive for null-values");
}
}
private ObjectWrapper(Object obj, Class<?> clazz) {
this.obj = clazz.cast(obj);
this.clazz = Objects.requireNonNull(clazz);
updateSuppliers();
}
@SuppressWarnings("unchecked")
private <T> T retrieveUnsafe() {
return (T) obj;
}
private void updateSuppliers() {
// 5.2 Allow unboxing, optionally followed by widening primitive conversion
if (Boolean.class.isAssignableFrom(clazz)) {
this.boolS = () -> this.<Boolean>retrieveUnsafe().booleanValue();
} else if (Character.class.isAssignableFrom(clazz)) {
this.charS = () -> this.<Character>retrieveUnsafe().charValue();
} else if (Byte.class.isAssignableFrom(clazz)) {
this.byteS = () -> this.<Byte>retrieveUnsafe().byteValue();
this.shortS = () -> this.<Byte>retrieveUnsafe().shortValue();
this.intS = () -> this.<Byte>retrieveUnsafe().intValue();
this.longS = () -> this.<Byte>retrieveUnsafe().longValue();
this.floatS = () -> this.<Byte>retrieveUnsafe().floatValue();
this.doubleS = () -> this.<Byte>retrieveUnsafe().doubleValue();
} else if (Short.class.isAssignableFrom(clazz)) {
this.shortS = () -> this.<Short>retrieveUnsafe().shortValue();
this.intS = () -> this.<Short>retrieveUnsafe().intValue();
this.longS = () -> this.<Short>retrieveUnsafe().longValue();
this.floatS = () -> this.<Short>retrieveUnsafe().floatValue();
this.doubleS = () -> this.<Short>retrieveUnsafe().doubleValue();
} else if (Integer.class.isAssignableFrom(clazz)) {
this.intS = () -> this.<Integer>retrieveUnsafe().intValue();
this.longS = () -> this.<Integer>retrieveUnsafe().longValue();
this.floatS = () -> this.<Integer>retrieveUnsafe().floatValue();
this.doubleS = () -> this.<Integer>retrieveUnsafe().doubleValue();
} else if (Long.class.isAssignableFrom(clazz)) {
this.longS = () -> this.<Long>retrieveUnsafe().longValue();
this.floatS = () -> this.<Long>retrieveUnsafe().floatValue();
this.doubleS = () -> this.<Long>retrieveUnsafe().longValue();
} else if (Float.class.isAssignableFrom(clazz)) {
this.floatS = () -> this.<Float>retrieveUnsafe().floatValue();
this.doubleS = () -> this.<Float>retrieveUnsafe().doubleValue();
} else if (Double.class.isAssignableFrom(clazz)) {
this.doubleS = () -> this.<Double>retrieveUnsafe().doubleValue();
}
}
@Override
public BooleanSupplier asBool() {
return boolS;
}
@Override
public CharSupplier asChar() {
return charS;
}
@Override
public ByteSupplier asByte() {
return byteS;
}
@Override
public ShortSupplier asShort() {
return shortS;
}
@Override
public IntSupplier asInt() {
return intS;
}
@Override
public LongSupplier asLong() {
return longS;
}
@Override
public FloatSupplier asFloat() {
return floatS;
}
@Override
public DoubleSupplier asDouble() {
return doubleS;
}
@Override
public <T> Supplier<T> asObject(Class<T> clazz) {
if (clazz.isPrimitive()) {
return asBoxedPrimitive(clazz);
}
if (clazz.isAssignableFrom(this.clazz)) {
return () -> this.retrieveUnsafe();
}
return null;
}
@Override
public Throwable checkError() {
return null;
}
@Override
public Class<?> getWrappedClass() {
return clazz;
}
}
public static final Holder EMPTY = new Holder();
public static Holder empty() {
return EMPTY;
}
public static final Holder FALSE = new Holder(false);
public static final Holder TRUE = new Holder(true);
public static Holder valueOf(boolean bool) {
return bool ? TRUE : FALSE;
}
public static Holder valueOf(byte b) {
return new Holder(b);
}
public static Holder valueOf(char c) {
return new Holder(c);
}
public static Holder valueOf(short s) {
return new Holder(s);
}
public static Holder valueOf(int i) {
return new Holder(i);
}
public static Holder valueOf(long l) {
return new Holder(l);
}
public static Holder valueOf(float f) {
return new Holder(f);
}
public static Holder valueOf(double d) {
return new Holder(d);
}
public static Holder valueOf(Object nonNull) {
return new Holder(nonNull);
}
/**
* Constructs a Holder of the object if the object is not null, else an empty Holder.
*
* @param o
* @return
*/
public static Holder valueOrEmpty(Object o) {
return o == null ? empty() : new Holder(o);
}
/**
* If clazz represents a primitive class, unboxes o, otherwise wraps o into a Holder. Note that o shall always
* represent the boxed form of clazz, but may be null if clazz is not primitve.<br>
* If unboxing fails, returns a Holder with the conversion exception.<br>
* This is primarily aimed at the unboxing of value retrieved by reflection, in which case you can write
* #makeUnboxer(fieldType).apply(fieldValue) to retrieve the correct Holder wrapping the field.
*
* @param clazz
* @return
*/
public static <T> Function<Object, Holder> makeUnboxer(Class<T> clazz) {
if (void.class.isAssignableFrom(clazz)) {
return t -> Holder.empty();
}
if (boolean.class.isAssignableFrom(clazz)) {
return t -> Holder.valueOf(Boolean.class.cast(t).booleanValue());
}
if (char.class.isAssignableFrom(clazz)) {
return t -> Holder.valueOf(Character.class.cast(t).charValue());
}
if (byte.class.isAssignableFrom(clazz)) {
return t -> Holder.valueOf(Byte.class.cast(t).byteValue());
}
if (short.class.isAssignableFrom(clazz)) {
return t -> Holder.valueOf(Short.class.cast(t).shortValue());
}
if (int.class.isAssignableFrom(clazz)) {
return t -> Holder.valueOf(Integer.class.cast(t).intValue());
}
if (long.class.isAssignableFrom(clazz)) {
return t -> Holder.valueOf(Long.class.cast(t).longValue());
}
if (float.class.isAssignableFrom(clazz)) {
return t -> Holder.valueOf(Float.class.cast(t).floatValue());
}
if (double.class.isAssignableFrom(clazz)) {
return t -> Holder.valueOf(Double.class.cast(t).doubleValue());
}
return t -> Holder.valueOf(clazz.cast(t), clazz);
}
public static Holder typedNull(Class<?> clazz) {
return new Holder(clazz, classTag);
}
/**
* Constructs a Holder of the value, or if null, one for the given class.
*
* @param object
* the object to hold, possibly null
* @param clazz
* the exact class to hold if object is null
* @return an engaged Holder that holds an object of the given Class.
*/
public static <F> Holder valueOf(F object, Class<? super F> clazz) {
return object == null ? new Holder(clazz, classTag) : new Holder(object);
}
/**
* Executes the supplier, if it throws an exception of type excClazz, it is caught and returned in a Holder, else
* the computed Holder is returned.<br>
* Usage:
*
* <pre>
* <code>
* return Holder.catching(NullPointerException.class, () -> {
* return possiblyNull.compute();
* });
* </code>
* </pre>
*
* Keep in mind that all exceptions <b>but E</b> are still thrown.
*
* @param excClazz
* @param supplier
* @return
*/
public static <E extends Throwable> Holder catching(
Class<? extends E> excClazz,
ThrowingSupplier<Holder, E> supplier) {
try {
return supplier.get();
} catch (Throwable thr) {
MHFCMain.logger().catching(thr);
Objects.requireNonNull(excClazz);
if (excClazz.isInstance(thr)) {
return Holder.catching(thr);
}
return ExceptionLessFunctions.throwUnchecked(thr);
}
}
/**
* The same as {@link #catching(Class, ThrowingSupplier)} but for supplier that only throw
* {@link RuntimeException}s. Most of the time, instead of using this directly, try
* {@link #snapshotSafely(IValueHolder)}.
*
* @param supplier
* @return
*/
public static <E extends Throwable> Holder catching(ThrowingSupplier<Holder, E> supplier) {
return Holder.catching(RuntimeException.class, supplier::get);
}
public static Holder catching(Throwable cause) {
return new Holder(cause, failedTag);
}
public static Holder snapshotSafely(IValueHolder holder) {
return Holder.catching(Throwable.class, holder::snapshot);
}
private final Wrapper wrap;
/**
* Any empty Any
*/
private Holder() {
this.wrap = VOID_WRAPPER;
}
private Holder(boolean bool) {
this(new BooleanWrapper(bool));
}
private Holder(byte b) {
this(new ByteWrapper(b));
}
private Holder(char c) {
this(new CharWrapper(c));
}
private Holder(short s) {
this(new ShortWrapper(s));
}
private Holder(int i) {
this(new IntWrapper(i));
}
private Holder(long l) {
this(new LongWrapper(l));
}
private Holder(float f) {
this(new FloatWrapper(f));
}
private Holder(double d) {
this(new DoubleWrapper(d));
}
/**
* An empty Any that is empty for a reason
*
* @param tag
* ignored tag for overload
* @param cause
* the cause why this {@link Holder} is empty
*/
private Holder(Throwable cause, FailedComputationTag tag) {
this(new ThrowableWrapper(cause));
}
/**
* Initializes the Any with an Object. If the value is <code>null</code>, this does the same as {@link #Holder()}.
* If you want to enforce a type with the value, use {@link #Holder(Object, Class)}.
*
* @param value
* the value to assign.
* @see #valueOf(Object, Class)
*/
private Holder(Object value) {
this.wrap = value == null ? VOID_WRAPPER : new ObjectWrapper(value);
}
private <F> Holder(Class<?> clazz, ClassTag tag) {
this.wrap = new ObjectWrapper(clazz, tag);
}
private Holder(Wrapper wrap) {
this.wrap = wrap;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Any)) {
return false;
}
Holder o = (Holder) obj;
// return this.contained.equals(other.contained);
if (!this.isValid() || !o.isValid()) {
return false;
}
Object thisBoxed = this.boxed();
Object otherBoxed = o.boxed();
return thisBoxed == null ? otherBoxed == null : thisBoxed.equals(otherBoxed);
}
public Object boxed() {
return getAs(Object.class);
}
public Object boxedOrNull() {
return getAs(Object.class, null);
}
/**
* Check if an error is stored, if yes, throw, if no, return
*/
private void throwIfError() {
Throwable th = this.wrap.checkError();
if (th == null) {
return;
}
throw new IllegalStateException(th.toString(), th);
}
private ClassCastException failedConversion(Class<?> to) {
return new ClassCastException("Can't convert from " + wrap.getWrappedClass() + " to " + to);
}
public boolean asBool() {
throwIfError();
BooleanSupplier supply = this.wrap.asBool();
if (supply == null) {
throw failedConversion(boolean.class);
}
return supply.getAsBoolean();
}
public byte asByte() {
throwIfError();
ByteSupplier supply = this.wrap.asByte();
if (supply == null) {
throw failedConversion(byte.class);
}
return supply.getAsByte();
}
public char asChar() {
throwIfError();
CharSupplier supply = this.wrap.asChar();
if (supply == null) {
throw failedConversion(char.class);
}
return supply.getAsChar();
}
public short asShort() {
throwIfError();
ShortSupplier supply = this.wrap.asShort();
if (supply == null) {
throw failedConversion(short.class);
}
return supply.getAsShort();
}
public int asInt() {
throwIfError();
IntSupplier supply = this.wrap.asInt();
if (supply == null) {
throw failedConversion(int.class);
}
return supply.getAsInt();
}
public long asLong() {
throwIfError();
LongSupplier supply = this.wrap.asLong();
if (supply == null) {
throw failedConversion(long.class);
}
return supply.getAsLong();
}
public float asFloat() {
throwIfError();
FloatSupplier supply = this.wrap.asFloat();
if (supply == null) {
throw failedConversion(float.class);
}
return supply.getAsFloat();
}
public double asDouble() {
throwIfError();
DoubleSupplier supply = this.wrap.asDouble();
if (supply == null) {
throw failedConversion(double.class);
}
return supply.getAsDouble();
}
/**
* Returns the object stored in this any if it could be assigned to an object of the given class in a simple
* expression <code>F object = getAs<F>(F.class)</code>. This includes auto-boxing. For example, when this class
* holds an int, it is possible to say getAs(Integer.class) because <code>Integer a = 5;</code> is a legal
* expression.
*
* @param fClazz
* the class to cast to. It is legal to give primitive classes - although that won't get you far because
* the value is boxed and unboxed anyway because with type erasure this method returns an Object
* @return
* @throws Throwable
*/
public <F> F getAs(Class<F> fClazz) {
throwIfError();
Supplier<F> supply = this.wrap.asObject(fClazz);
if (supply == null) {
throw failedConversion(fClazz);
}
return supply.get();
}
public <F> F getAs(Class<F> fClazz, F otherwise) {
throwIfError();
Supplier<F> supply = this.wrap.asObject(fClazz);
if (supply == null) {
return otherwise;
}
return supply.get();
}
/**
* This Holder is said to be "valid" when it doesn't hold an error.
*
* @return
*/
public boolean isValid() {
return this.wrap.checkError() == null;
}
/**
* This Holder is said to be "engaged" when it isn't empty and doesn't hold an error.
*
* @return
*/
public boolean isEngaged() {
return !EMPTY_CLASS.isAssignableFrom(wrap.getClass());
}
/**
* Returns true if this holder's value can be converted to the class clazz. <b>This does not mean that no exceptions
* may be thrown during conversion</b>. For example, if this holder holds null of type {@link Integer}, this would
* return <code>true</code> but during execution an {@link NullPointerException} will be thrown.
*
* @param clazz
* @return
*/
public boolean holdsClass(Class<?> clazz) {
return this.wrap.asObject(clazz) != null;
}
public Class<?> getType() {
throwIfError();
return this.wrap.getWrappedClass();
}
/**
*
* {@inheritDoc}
*
* @return
*/
@Override
public Holder snapshot() {
return this;
}
public Throwable getFailCause() {
if (!isValid()) {
return this.wrap.checkError();
}
return null;
}
/**
* "{error: <errorstring>}" if this holds an error.<br>
* "void" if this is empty.<br>
* otherwise the (boxed) held object.toString()<br>
*/
@Override
public String toString() {
if (!isValid()) {
return "{error: " + this.wrap.checkError().toString() + "}";
}
Object hold = boxedOrNull();
return hold == null ? "void" : hold.toString();
}
/**
* If this holds an exception, return this, otherwise call the function with this and return the result.
*
* @param supplier
* @return
*/
public Holder ifValid(Function<Holder, Holder> mapping) {
if (!this.isValid()) {
return this;
}
return mapping.apply(this);
}
/**
* Flat maps the function application. If the function returns null, an empty Holder is returned instead.
*
* @param classT
* @param func
* @return
*/
public <T> Holder ifValidMap(Class<T> classT, Function<T, Holder> func) {
if (!this.isValid()) {
return this;
}
Supplier<T> supp = this.wrap.asObject(classT);
if (supp == null) {
throw failedConversion(classT);
}
Holder ret = func.apply(supp.get());
return ret == null ? Holder.empty() : ret;
}
/**
* Utility method to supply a back-up if normal calculations should fail.<br>
* If {@link #isValid()}, return this, otherwise return {@link Supplier#get()} of the supplier given.
*
* @param supplier
* @return
* @see {@link #ifNotEngaged(Supplier)}: uses {@link #isEngaged()} to determine validity of itself
*/
public Holder ifInvalid(Supplier<Holder> supplier) {
if (this.isValid()) {
return this;
}
return supplier.get();
}
/**
* Utility method to supply a back-up if normal calculations returns no result.<br>
* If {@link #isEngaged()}, return this, otherwise return {@link Supplier#get()} of the supplier given.
*
* @param supplier
* @return
* @see {@link #ifInvalid(Supplier)}: uses {@link #isValid()} to determine validity of itself
*/
public Holder ifNotEngaged(Supplier<Holder> supplier) {
if (this.isEngaged()) {
return this;
}
return supplier.get();
}
/**
* If this Holder represents an exception, return one that holds that exception as value, else return an empty
* Holder.
*
* @return
*/
public Holder exceptionAsValue() {
return Holder.valueOrEmpty(this.wrap.checkError());
}
}