package bjc.utils.data; import bjc.utils.data.internals.BoundLazyPair; import bjc.utils.data.internals.HalfBoundLazyPair; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; /** * A lazy implementation of a pair * * @author ben * * @param * The type on the left side of the pair * @param * The type on the right side of the pair * */ public class LazyPair implements IPair { private LeftType leftValue; private RightType rightValue; private Supplier leftSupplier; private Supplier rightSupplier; private boolean leftMaterialized; private boolean rightMaterialized; /** * Create a new lazy pair, using the set values * * @param leftVal * The value for the left side of the pair * @param rightVal * The value for the right side of the pair */ public LazyPair(LeftType leftVal, RightType rightVal) { leftValue = leftVal; rightValue = rightVal; leftMaterialized = true; rightMaterialized = true; } /** * Create a new lazy pair from the given value sources * * @param leftSupp * The source for a value on the left side of the pair * @param rightSupp * The source for a value on the right side of the pair */ public LazyPair(Supplier leftSupp, Supplier rightSupp) { // Use single suppliers to catch double-instantiation bugs leftSupplier = new SingleSupplier<>(leftSupp); rightSupplier = new SingleSupplier<>(rightSupp); leftMaterialized = false; rightMaterialized = false; } @Override public IPair bind( BiFunction> binder) { return new BoundLazyPair<>(leftSupplier, rightSupplier, binder); } @Override public IPair bindLeft( Function> leftBinder) { Supplier leftSupp = () -> { if(leftMaterialized) return leftValue; return leftSupplier.get(); }; return new HalfBoundLazyPair<>(leftSupp, leftBinder); } @Override public IPair bindRight( Function> rightBinder) { Supplier rightSupp = () -> { if(rightMaterialized) return rightValue; return rightSupplier.get(); }; return new HalfBoundLazyPair<>(rightSupp, rightBinder); } @Override public IPair combine( IPair otherPair, BiFunction leftCombiner, BiFunction rightCombiner) { return otherPair.bind((otherLeft, otherRight) -> { return bind((leftVal, rightVal) -> { return new LazyPair<>(leftCombiner.apply(leftVal, otherLeft), rightCombiner.apply(rightVal, otherRight)); }); }); } @Override public LeftType getLeft() { if(!leftMaterialized) { leftValue = leftSupplier.get(); leftMaterialized = true; } return leftValue; } @Override public RightType getRight() { if(!rightMaterialized) { rightValue = rightSupplier.get(); rightMaterialized = true; } return rightValue; } @Override public IPair mapLeft(Function mapper) { Supplier leftSupp = () -> { if(leftMaterialized) return mapper.apply(leftValue); return mapper.apply(leftSupplier.get()); }; Supplier rightSupp = () -> { if(rightMaterialized) return rightValue; return rightSupplier.get(); }; return new LazyPair<>(leftSupp, rightSupp); } @Override public IPair mapRight(Function mapper) { Supplier leftSupp = () -> { if(leftMaterialized) return leftValue; return leftSupplier.get(); }; Supplier rightSupp = () -> { if(rightMaterialized) return mapper.apply(rightValue); return mapper.apply(rightSupplier.get()); }; return new LazyPair<>(leftSupp, rightSupp); } @Override public MergedType merge(BiFunction merger) { if(!leftMaterialized) { leftValue = leftSupplier.get(); leftMaterialized = true; } if(!rightMaterialized) { rightValue = rightSupplier.get(); rightMaterialized = true; } return merger.apply(leftValue, rightValue); } @Override public String toString() { StringBuilder sb = new StringBuilder("pair[l="); if(leftMaterialized) { sb.append(leftValue.toString()); } else { sb.append("(un-materialized)"); } sb.append(", r="); if(rightMaterialized) { sb.append(rightValue.toString()); } else { sb.append("(un-materialized)"); } sb.append("]"); return sb.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (leftMaterialized ? 1231 : 1237); result = prime * result + ((leftValue == null) ? 0 : leftValue.hashCode()); result = prime * result + (rightMaterialized ? 1231 : 1237); result = prime * result + ((rightValue == null) ? 0 : rightValue.hashCode()); return result; } @Override public boolean equals(Object obj) { if(this == obj) return true; if(obj == null) return false; if(getClass() != obj.getClass()) return false; LazyPair other = (LazyPair) obj; if(leftMaterialized != other.leftMaterialized) return false; if(leftValue == null) { if(other.leftValue != null) return false; } else if(!leftValue.equals(other.leftValue)) return false; if(rightMaterialized != other.rightMaterialized) return false; if(rightValue == null) { if(other.rightValue != null) return false; } else if(!rightValue.equals(other.rightValue)) return false; return true; } }