package xapi.fu;
import xapi.fu.In1Out1.In1Out1Unsafe;
import xapi.fu.Log.LogLevel;
import static xapi.fu.Filter.alwaysTrue;
import static xapi.fu.Immutable.immutable1;
import javax.inject.Provider;
import java.util.function.Supplier;
/**
* @author James X. Nelson (james@wetheinter.net)
* Created on 07/11/15.
*/
public interface Out1<O> extends Rethrowable, Lambda {
O out1();
Out1 NULL = immutable1(null);
static <T> Out1 <T> null1() {
return NULL;
}
Out1<String> EMPTY_STRING = immutable1("");
Out1<String> NEW_LINE = immutable1("\n");
Out1<String> SPACE = immutable1(" ");
Out1<Boolean> FALSE = immutable1(false);
Out1<Integer> ZERO = immutable1(0);
Out1<Boolean> TRUE = immutable1(true);
Out1<Integer> ONE = immutable1(1);
Out1<Integer> NEGATIVE_ONE = immutable1(-1);
default boolean isImmutable() {
Object o = this;
return o instanceof Immutable || o instanceof IsImmutable;
}
default Out1<O> use(In1<O> callback) {
callback.in(out1());
return this;
}
/**
* @return an immutable copy of this provider.
*/
default <F extends Out1<O> & Frozen> F freeze() {
if (this instanceof Frozen) {
return (F) this;
}
final O o = out1();
F f = (F)(Out1<O> & Frozen)()->o;
return f;
}
default Out1<O> self() {
return this;
}
default Supplier<O> toSupplier() {
return this::out1;
}
default Provider<O> toProvider() {
return this::out1;
}
static <O> Out1<O> out1Supplier(Supplier<O> of) {
return of::get;
}
static <O> Out1<O> out1(Out1<O> of) {
return of;
}
static <O> Out1<O> out1Provider(Provider<O> of) {
return of::get;
}
static <I, O> Out1<O> out1Immediate(In1Out1<I, O> mapper, I input) {
return mapper.supply(input);
}
static <I, O> Out1<O> out1Immediate(In1Out1<I, O> mapper, Out1<I> input) {
I value = input.out1();
return ()->mapper.io(value);
}
static <I, O> Out1<O> out1Deferred(In1Out1<I, O> mapper, Out1<I> input) {
return ()->mapper.io(input.out1());
}
static <I, O> Out1<O> out1Deferred(In1Out1<I, O> mapper, I input) {
return ()->mapper.io(input);
}
/**
* This method just exists to give you somewhere to create a lambda that will rethrow exceptions,
* but exposes an exceptionless api. If you don't have to call code with checked exceptions,
* prefer the standard {@link #out1(Out1)}, as try/catch can disable / weaken some JIT compilers.
*/
static <O> Out1<O> out1Unsafe(Out1Unsafe<O> of) {
return of;
}
interface Out1Unsafe <O> extends Out1<O>, Rethrowable{
O outUnsafe() throws Throwable;
default O out1() {
try {
return outUnsafe();
} catch (Throwable e) {
throw rethrow(e);
}
}
default O eatExceptions() {
return eatExceptions(
Log.firstLog(this), LogLevel.WARN, alwaysTrue());
}
@SuppressWarnings("unchecked")
default O eatExceptions(Log log) {
Filter<Throwable> filter = Filter.TRUE;
return eatExceptions(log, LogLevel.WARN, filter);
}
default O eatExceptions(Log log, Filter<Throwable> filter) {
return eatExceptions(log, LogLevel.WARN, filter);
}
@SuppressWarnings("unchecked")
default O eatExceptions(Log log, LogLevel level) {
return eatExceptions(log, level, alwaysTrue());
}
default O eatExceptions(Log log, LogLevel level, Filter<Throwable> filter) {
try {
return outUnsafe();
} catch (Throwable e) {
log = Log.normalize(log);
if (log.isLoggable(level)) {
try {
log.log(getClass(), e);
} catch (Throwable loggingError) {
log.log(LogLevel.ERROR, "Error logging error! Errorception: ");
log.log(LogLevel.ERROR, "Error while logging: ", loggingError);
log.log(LogLevel.ERROR, "Original error: ", e);
}
}
throw rethrow(e);
}
}
}
default <To> Out1<To> map(In1Out1<O, To> factory) {
return factory.supplyDeferred(this);
}
default Out1<O> ifNull(In1Out1<O, O> mapper) {
return mapIf(X_Fu::isNull, mapper);
}
default Out1<O> mapIf(In1Out1<O, Boolean> filter, In1Out1<O, O> mapper) {
return ()->{
O o = out1();
if (filter.io(o)) {
o = mapper.io(o);
}
return o;
};
}
default <To> Out1<To> mapIf(In1Out1<O, Boolean> filter,
In1Out1<O, To> pass,
In1Out1<O, To> fail) {
return ()->{
O o = out1();
if (filter.io(o)) {
return pass.io(o);
}
return fail.io(o);
};
}
default <In, To> Out1<To> map(In2Out1<O, In, To> factory, In in1) {
return factory.supply(out1(), in1);
}
default <In, To> Out1<To> mapDeferred(In2Out1<O, In, To> factory, Out1<In> in1) {
return factory.supply1Deferred(this).supplyDeferred(in1);
}
default <In, To> Out1<To> mapImmediate(In2Out1<O, In, To> factory, Out1<In> in1) {
return factory.supply1(out1()).supplyDeferred(in1);
}
default <To> Out1<To> mapUnsafe(In1Out1Unsafe<O, To> factory) {
return factory.supplyDeferred(this);
}
}