From 9c681f38b742b26b841eb42bc19879cb90ac03de Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Mon, 8 Jul 2024 17:30:58 -0400 Subject: 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 --- .../java/net/wotonomy/foundation/NSEither.java | 237 +++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java (limited to 'projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java') 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 The type that could be on the left. + * + * @param The type that could be on the right. + * + */ +public class NSEither { + /** + * Create a new either with the left value occupied. + * + * @param The type of the left value. + * + * @param 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 NSEither left(final LeftType left) { + return new NSEither<>(left, null); + } + + /** + * Create a new either with the right value occupied. + * + * @param The type of the empty left value. + * + * @param 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 NSEither 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 The new left type. + * @param 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 NSEither map(Function leftFunc, + Function rightFunc) { + return isLeft ? left(leftFunc.apply(leftVal)) : right(rightFunc.apply(rightVal)); + } + + /** + * Extract the value from this Either. + * + * @param 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 extract(Function leftHandler, Function 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 leftHandler, Consumer 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 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 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 The new type for the right side + * @return The either with the new type. + */ + @SuppressWarnings("unchecked") + public NSEither newRight() { + if (isLeft) return (NSEither) 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 The new type for the left side + * @return The either with the new type. + */ + @SuppressWarnings("unchecked") + public NSEither newLeft() { + if (isLeft) + throw new NoSuchElementException("Can't replace left type on left Either"); + return (NSEither) this; + } + + /** + * Collapse an Either with the same type on both sides. + * + * @param The type of the either + * @param eth The either to collapse + * + * @return The collapsed either + */ + public static T collapse(NSEither eth) { + Function 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); + } +} -- cgit v1.2.3