package xapi.fu;
import xapi.fu.Log.DefaultLog;
import xapi.fu.Log.LogLevel;
/**
* @author James X. Nelson (james@wetheinter.net)
* Created on 1/3/16.
*/
public interface Pointer <T> extends In1<T>, Out1<T>, In1Out1<T, T> {
Out2<In1<T>, Out1<T>> accessors();
@Override
default void in(T in) {
accessors().out1().in(in);
}
@Override
default T out1() {
return accessors().out2().out1();
}
@Override
default T io(T in) {
return getThenSet(in);
}
default T getThenSet(T in) {
T was = out1();
in(in);
return was;
}
default T getThenApply(In1Out1<T, T> io) {
T was = out1();
T next = io.io(was);
in(next);
return was;
}
default T setThenGet(T in) {
io(in);
return out1();
}
static <T> Pointer <T> pointer() {
T[] value = X_Fu.array((T)null);
final In1<T> in = In1.from1(X_Fu::setZeroeth, value);
final Out1<T> out = Out1.out1Deferred(X_Fu::getZeroeth, value);
final Out2<In1<T>, Out1<T>> pair = Out2.out2Immutable(in, out);
return ()-> pair;
}
static <T> Pointer <T> pointerTo(T value) {
final Pointer<T> pointer = pointer();
pointer.in(value);
return pointer;
}
static <T> Pointer <T> pointerJoin(In1<T> input, Out1<T> output) {
return new PointerJoin<>(input, output);
}
static <T> Pointer <T> pointerDeferred(Out1<T> value) {
return new PointerDeferred<>(value);
}
static <T> PointerImmutable <T> pointerImmutable(T value) {
return new PointerImmutable<>(value);
}
static <T> PointerLazy <T> pointerLazy(Out1<T> value) {
return new PointerLazy<>(value);
}
interface PointerComparable<T extends Comparable<T>> extends Pointer<T>, Lambda {
}
final class PointerJoin <T> implements Pointer<T>, DefaultLog {
@Override
public Out2<In1<T>, Out1<T>> accessors() {
return Out2.out2Immutable(in, out);
}
private final In1<T> in;
private final Out1<T> out;
PointerJoin(In1<T> in, Out1<T> out) {
this.in = in;
this.out = out;
}
@Override
public void in(T i) {
in.in(i);
}
@Override
public T out1() {
return out.out1();
}
}
class PointerDeferred <T> implements Pointer<T>, DefaultLog {
protected volatile Out1<T> value;
public PointerDeferred(Out1<T> value) {
this.value = value;
}
public PointerDeferred() {
value = this::warnUninitialized;
}
protected T warnUninitialized() {
log(getClass(), "Attempting to get the value from a PointerDeferred, ", this, ", before its value has been initialized");
return null;
}
@Override
public Out2<In1<T>, Out1<T>> accessors() {
return Out2.out2Immutable(this, this);
}
@Override
public void in(T in) {
this.value = Immutable.immutable1(in);
}
public void supply(Out1<T> deferred) {
this.value = deferred;
}
@Override
public T out1() {
return value.out1();
}
}
class PointerLazy <T> extends PointerDeferred <T> implements IsLazy {
PointerLazy() {}
PointerLazy(Out1<T> of) {value = of;}
@Override
public T out1() {
if (value instanceof Immutable) {
return value.out1();
}
T result = value.out1();
value = Immutable.immutable1(result);
return result;
}
}
class PointerImmutable <T> implements Pointer<T> {
private final T value;
public PointerImmutable(T value) {
this.value = value;
}
@Override
public void in(T in) {
warnAttemptToSet(in);
}
protected void warnAttemptToSet(T in) {
if ("true".equals(System.getProperty("xapi.strict.immutable"))) {
final Out1<String> message = Immutable.immutable1("Attempting to assign value [" + in + "] to an immutable pointer: " + getClass() + " : " + this);
Log.firstLog(in, this).log(LogLevel.DEBUG, "Attempt to set an immutable");
assert false : message;
}
}
@Override
public Out2<In1<T>, Out1<T>> accessors() {
return Out2.out2Immutable(this, this);
}
@Override
public T out1() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof PointerImmutable))
return false;
final PointerImmutable<?> that = (PointerImmutable<?>) o;
return X_Fu.equal(value, that.value);
}
@Override
public int hashCode() {
return value != null ? value.hashCode() : 0;
}
}
}