package bjc.utils.data.lazy; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import bjc.utils.data.IHolder; import bjc.utils.funcdata.FunctionalList; /** * Holds a single value of a specific type. This is used for indirect * references to data, and more specifically for accessing non-final * variables from a lambda. AKA the identity monad * * This is a lazy variant of {@link IHolder} * * @author ben * * @param * The type of the data being held */ public class LazyHolder implements IHolder { private final class LazyHolderSupplier implements Supplier { private FunctionalList> pendingActions; private Function pendingTransform; public LazyHolderSupplier(FunctionalList> actons, Function transform) { // Resolve latent bug I just realized. After a map, adding new // actions to the original holder could've resulted in changes // to all unactualized mapped values from that holder pendingActions = actons.clone(); this.pendingTransform = transform; } @Override public NewT get() { if (heldValue == null) { return pendingActions.reduceAux(heldSource.get(), Function::apply, pendingTransform::apply); } else { return pendingActions.reduceAux(heldValue, Function::apply, pendingTransform::apply); } } } /** * List of queued actions to be performed on realized values */ private FunctionalList> actions = new FunctionalList<>(); /** * The value internally held by this lazy holder */ private T heldValue; /** * The source for a value held by this lazy holder */ private Supplier heldSource; /** * Create a new lazy holder with the given supplier * * @param source * The supplier for a value when it is neededs */ public LazyHolder(Supplier source) { heldSource = source; heldValue = null; } /** * Create a new lazy holder with the given value * * @param value * The value held in the holder */ public LazyHolder(T value) { heldValue = value; } @Override public void doWith(Consumer action) { transform((value) -> { // Do the action with the value action.accept(value); // Return the untransformed value return value; }); } @Override public IHolder map(Function transform) { // Don't actually map until we need to return new LazyHolder<>( new LazyHolderSupplier<>(actions, transform)); } @Override public IHolder transform(Function transform) { // Queue the transform until we need to apply it actions.add(transform); return this; } @Override public E unwrap(Function unwrapper) { // Actualize ourselves if (heldValue == null) { heldValue = heldSource.get(); } // Apply all pending transforms actions.forEach((action) -> heldValue = action.apply(heldValue)); return unwrapper.apply(heldValue); } }