diff options
| author | Benjamin Culkin <scorpress@gmail.com> | 2024-07-08 17:30:58 -0400 |
|---|---|---|
| committer | Benjamin Culkin <scorpress@gmail.com> | 2024-07-08 17:30:58 -0400 |
| commit | 9c681f38b742b26b841eb42bc19879cb90ac03de (patch) | |
| tree | 6c1b9eb1971629cc3c42bae3bff9fd930d4af11c /projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java | |
| parent | 6d46c473d41c6c47e6b8bd8c676d925e544bd378 (diff) | |
Add XML property lists
Implement support for the XML property lists that are the newer version
of the ASCII ones. There are a few things that still need to be done,
but all of the basics are there
Next things
- Allow collapsing a property list into a series of objects
- Serialize both the property list and flattened objects to XML
Diffstat (limited to 'projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java')
| -rw-r--r-- | projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java new file mode 100644 index 0000000..69714c2 --- /dev/null +++ b/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java @@ -0,0 +1,237 @@ +package net.wotonomy.foundation; + +import java.util.*; +import java.util.function.*; + +/** + * Represents a choice between objects of two types + * + * @author bjculkin + * + * @param <LeftType> The type that could be on the left. + * + * @param <RightType> The type that could be on the right. + * + */ +public class NSEither<LeftType, RightType> { + /** + * Create a new either with the left value occupied. + * + * @param <LeftType> The type of the left value. + * + * @param <RightType> The type of the empty right value. + * + * @param left The value to put on the left. + * + * @return An either with the left side occupied. + */ + public static <LeftType, RightType> NSEither<LeftType, RightType> left(final LeftType left) { + return new NSEither<>(left, null); + } + + /** + * Create a new either with the right value occupied. + * + * @param <LeftType> The type of the empty left value. + * + * @param <RightType> The type of the right value. + * + * @param right The value to put on the right. + * + * @return An either with the right side occupied. + */ + public static <LeftType, RightType> NSEither<LeftType, RightType> right(final RightType right) { + return new NSEither<>(null, right); + } + + /* The left value of the either. */ + private LeftType leftVal; + /* The right value of the either. */ + private RightType rightVal; + /* Whether the left value is the one filled out. */ + private boolean isLeft; + + /* Create a new either with specifed values. */ + private NSEither(final LeftType left, final RightType right) { + if (left == null) { + rightVal = right; + } else { + leftVal = left; + + isLeft = true; + } + } + + /** + * Perform a mapping over this either. + * + * @param <NewLeft> The new left type. + * @param <NewRight> The new right type. + * + * @param leftFunc The function to apply if this is a left either. + * @param rightFunc The function to apply if this is a right either. + * + * @return A new either, containing a value transformed by the appropriate + * function. + */ + public <NewLeft, NewRight> NSEither<NewLeft, NewRight> map(Function<LeftType, NewLeft> leftFunc, + Function<RightType, NewRight> rightFunc) { + return isLeft ? left(leftFunc.apply(leftVal)) : right(rightFunc.apply(rightVal)); + } + + /** + * Extract the value from this Either. + * + * @param <Common> The common type to extract. + * + * @param leftHandler The function to handle left-values. + * @param rightHandler The function to handle right-values. + * + * @return The result of applying the proper function. + */ + public <Common> Common extract(Function<LeftType, Common> leftHandler, Function<RightType, Common> rightHandler) { + return isLeft ? leftHandler.apply(leftVal) : rightHandler.apply(rightVal); + } + + /** + * Perform an action on this either. + * + * @param leftHandler The handler of left values. + * @param rightHandler The handler of right values. + */ + public void pick(Consumer<LeftType> leftHandler, Consumer<RightType> rightHandler) { + if (isLeft) + leftHandler.accept(leftVal); + else + rightHandler.accept(rightVal); + } + + /** + * Check if this either is left-aligned (has the left value filled, not the + * right value). + * + * @return Whether this either is left-aligned. + */ + public boolean isLeft() { + return isLeft; + } + + /** + * Get the left value of this either if there is one. + * + * @return An optional containing the left value, if there is one. + */ + public Optional<LeftType> getLeft() { + return Optional.ofNullable(leftVal); + } + + /** + * Get the left value of this either, or get a {@link NoSuchElementException} if + * there isn't one. + * + * @return The left value of this either. + * + * @throws NoSuchElementException If this either doesn't have a left value. + */ + public LeftType forceLeft() { + if (isLeft) { + return leftVal; + } + + throw new NoSuchElementException("Either has no left value, is right value"); + } + + /** + * Get the right value of this either if there is one. + * + * @return An optional containing the right value, if there is one. + */ + public Optional<RightType> getRight() { + return Optional.ofNullable(rightVal); + } + + /** + * Get the right value of this either, or get a {@link NoSuchElementException} + * if there isn't one. + * + * @return The right value of this either. + * + * @throws NoSuchElementException If this either doesn't have a right value. + */ + public RightType forceRight() { + if (isLeft) { + throw new NoSuchElementException("Either has no right value, has left value"); + } + + return rightVal; + } + + /** + * Change the type of the right-side of this either. + * + * Works only for left Eithers. + * + * @param <T> The new type for the right side + * @return The either with the new type. + */ + @SuppressWarnings("unchecked") + public <T> NSEither<LeftType, T> newRight() { + if (isLeft) return (NSEither<LeftType, T>) this; + + throw new NoSuchElementException("Can't replace right type on right Either"); + } + + /** + * Change the type of the left-side of this either. + * + * Works only for right Eithers. + * + * @param <T> The new type for the left side + * @return The either with the new type. + */ + @SuppressWarnings("unchecked") + public <T> NSEither<T, RightType> newLeft() { + if (isLeft) + throw new NoSuchElementException("Can't replace left type on left Either"); + return (NSEither<T, RightType>) this; + } + + /** + * Collapse an Either with the same type on both sides. + * + * @param <T> The type of the either + * @param eth The either to collapse + * + * @return The collapsed either + */ + public static <T> T collapse(NSEither<T, T> eth) { + Function<T, T> id = (x) -> x; + return eth.extract(id, id); + } + // Misc. overrides + + @Override + public int hashCode() { + return Objects.hash(isLeft, leftVal, rightVal); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + + NSEither<?, ?> other = (NSEither<?, ?>) obj; + + return isLeft == other.isLeft && Objects.equals(leftVal, other.leftVal) + && Objects.equals(rightVal, other.rightVal); + } + + @Override + public String toString() { + return String.format("Either [leftVal='%s', rightVal='%s', isLeft=%s]", leftVal, rightVal, isLeft); + } +} |
