summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java
diff options
context:
space:
mode:
authorBenjamin Culkin <scorpress@gmail.com>2024-07-08 17:30:58 -0400
committerBenjamin Culkin <scorpress@gmail.com>2024-07-08 17:30:58 -0400
commit9c681f38b742b26b841eb42bc19879cb90ac03de (patch)
tree6c1b9eb1971629cc3c42bae3bff9fd930d4af11c /projects/net.wotonomy.foundation/src/main/java/net/wotonomy/foundation/NSEither.java
parent6d46c473d41c6c47e6b8bd8c676d925e544bd378 (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.java237
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);
+ }
+}