package bjc.data.internals; import java.util.function.Function; import java.util.function.Supplier; import java.util.function.UnaryOperator; import bjc.data.Holder; import bjc.data.Lazy; import bjc.funcdata.FunctionalList; import bjc.funcdata.ListEx; /** * Implements a lazy holder that has been bound. * * @author Ben Culkin * @param * The old type of the value. * @param * The type of the new bound value. */ public class BoundLazy implements Holder { /* The old value. */ private final Supplier> oldSupplier; /* The function to use to transform the old value into a new value. */ private final Function> binder; /* The bound value being held. */ private Holder boundHolder; /* Whether the bound value has been actualized or not. */ private boolean holderBound; /* Transformations currently pending on the bound value. */ private final ListEx> actions = new FunctionalList<>(); /** * Create a new bound lazy value. * * @param supp * The supplier of the old value. * * @param binder * The function to use to bind the old value to the new one. */ public BoundLazy(final Supplier> supp, final Function> binder) { oldSupplier = supp; this.binder = binder; } @Override public Holder bind(final Function> bindr) { if (bindr == null) throw new NullPointerException("Binder must not be null"); /* Prepare a list of pending actions. */ final ListEx> pendingActions = new FunctionalList<>(); for (UnaryOperator pendAct : actions) { pendingActions.add(pendAct); } /* Create the new supplier of a value. */ final Supplier> typeSupplier = () -> { Holder oldHolder = boundHolder; /* Bind the value if it hasn't been bound before. */ if (!holderBound) { oldHolder = oldSupplier.get().unwrap(binder); } /* Apply all the pending actions. */ return pendingActions.reduceAux(oldHolder, (action, state) -> state.transform(action), value -> value); }; return new BoundLazy<>(typeSupplier, bindr); } @Override public Function> lift(final Function func) { if (func == null) throw new NullPointerException("Function to lift must not be null"); return val -> { return new Lazy<>(func.apply(val)); }; } @Override public Holder map(final Function mapper) { if (mapper == null) throw new NullPointerException("Mapper must not be null"); /* Prepare a list of pending actions. */ final ListEx> pendingActions = new FunctionalList<>(); for (UnaryOperator pendAct : actions) { pendingActions.add(pendAct); } /* Prepare the new supplier. */ final Supplier typeSupplier = () -> { Holder oldHolder = boundHolder; /* Bound the value if it hasn't been bound. */ if (!holderBound) { oldHolder = oldSupplier.get().unwrap(binder); } /* Apply pending actions. */ return pendingActions.reduceAux(oldHolder.getValue(), (action, state) -> action.apply(state), value -> mapper.apply(value)); }; return new Lazy<>(typeSupplier); } @Override public String toString() { if (holderBound) return boundHolder.toString(); return "(unmaterialized)"; } @Override public Holder transform(final UnaryOperator transformer) { if (transformer == null) throw new NullPointerException("Transformer must not be null"); actions.add(transformer); return this; } @Override public UnwrappedType unwrap(final Function unwrapper) { if (unwrapper == null) throw new NullPointerException("Unwrapper must not be null"); if (!holderBound) { boundHolder = oldSupplier.get().unwrap(binder::apply); } return boundHolder.unwrap(unwrapper); } }