diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/java/bjc/data/Option.java | 19 | ||||
| -rw-r--r-- | src/main/java/bjc/functypes/ThrowFunction.java | 97 | ||||
| -rw-r--r-- | src/test/java/bjc/data/EitherTest.java | 40 |
3 files changed, 104 insertions, 52 deletions
diff --git a/src/main/java/bjc/data/Option.java b/src/main/java/bjc/data/Option.java index 6c76d59..6ca8b13 100644 --- a/src/main/java/bjc/data/Option.java +++ b/src/main/java/bjc/data/Option.java @@ -13,7 +13,15 @@ import java.util.function.UnaryOperator; */ public class Option<ContainedType> implements Holder<ContainedType> { private ContainedType held; - + private boolean isHolding; + + /** + * Create a new empty optional. + */ + public Option() { + isHolding = false; + } + /** * Create a new optional, using the given initial value. * @@ -22,12 +30,13 @@ public class Option<ContainedType> implements Holder<ContainedType> { */ public Option(final ContainedType seed) { held = seed; + isHolding = true; } @Override public <BoundType> Holder<BoundType> bind(final Function<ContainedType, Holder<BoundType>> binder) { - if (held == null) return new Option<>(null); + if (isHolding) return new Option<>(); return binder.apply(held); } @@ -41,7 +50,7 @@ public class Option<ContainedType> implements Holder<ContainedType> { @Override public <MappedType> Holder<MappedType> map(final Function<ContainedType, MappedType> mapper) { - if (held == null) return new Option<>(null); + if (isHolding) return new Option<>(); return new Option<>(mapper.apply(held)); } @@ -49,7 +58,7 @@ public class Option<ContainedType> implements Holder<ContainedType> { @Override public Holder<ContainedType> transform(final UnaryOperator<ContainedType> transformer) { - if (held != null) held = transformer.apply(held); + if (isHolding) held = transformer.apply(held); return this; } @@ -57,7 +66,7 @@ public class Option<ContainedType> implements Holder<ContainedType> { @Override public <UnwrappedType> UnwrappedType unwrap(final Function<ContainedType, UnwrappedType> unwrapper) { - if (held == null) return null; + if (isHolding) return null; return unwrapper.apply(held); } diff --git a/src/main/java/bjc/functypes/ThrowFunction.java b/src/main/java/bjc/functypes/ThrowFunction.java index 132f55c..3714cad 100644 --- a/src/main/java/bjc/functypes/ThrowFunction.java +++ b/src/main/java/bjc/functypes/ThrowFunction.java @@ -1,5 +1,6 @@ package bjc.functypes; +import java.util.*; import java.util.function.*; /** @@ -25,6 +26,46 @@ public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable> public ReturnType apply(InputType arg) throws ExType; /** + * Converts this into a {@link Function} by handling any thrown exceptions, + * then mapping the return type to get a consistent return type. + * + * @param <NewReturn> The new return type. + * + * @param clasz The class of the handled exception. This needs to be provided + * because you can't catch a generic exception, and we want to + * make sure that we aren't catching any types of exception that + * we aren't supposed to. + * @param mapper The function which maps the normal return. + * @param handler The handler to use. + * + * @return A function which will either return its proper value, or the result + * of invoking the handler. + */ + @SuppressWarnings("unchecked") + default <NewReturn> Function<InputType, NewReturn> swallowMap( + Class<ExType> clasz, + Function<ReturnType, NewReturn> mapper, + Function<ExType, NewReturn> handler) + { + return (inp) -> { + try { + return mapper.apply(this.apply(inp)); + } catch (Throwable ex) { + if (clasz.isInstance(ex)) { + // Swallow this + return handler.apply((ExType) ex); + } else { + String msg = "Exception of incorrect type to be handled, only " + + clasz.getName() + + " are handled"; + + throw new RuntimeException(msg, ex); + } + } + }; + } + + /** * Converts this into a {@link Function} by handling any thrown exceptions. * * @param clasz The class of the handled exception. This needs to be provided @@ -37,30 +78,10 @@ public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable> * @return A function which will either return its proper value, or the result * of invoking the handler. */ - @SuppressWarnings("unchecked") default Function<InputType, ReturnType> swallow( Class<ExType> clasz, Function<ExType, ReturnType> handler) { - return (inp) -> - { - try - { - return this.apply(inp); - } catch (Throwable ex) { - if (clasz.isInstance(ex)) - { - // Swallow this - return handler.apply((ExType) ex); - } else - { - String msg = "Exception of incorrect type to be handled, only " - + clasz.getName() - + " are handled"; - - throw new RuntimeException(msg, ex); - } - } - }; + return swallowMap(clasz, (arg) -> arg, handler); } /** @@ -73,20 +94,16 @@ public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable> */ @SuppressWarnings("unchecked") default Function<InputType, ReturnType> recover( - Class<ExType> clasz, BiFunction<InputType, ExType, InputType> rescue) { - return Fixpoints.fix((arg, self) -> - { - try - { + Class<ExType> clasz, BiFunction<InputType, ExType, InputType> rescue) + { + return Fixpoints.fix((arg, self) -> { + try { return this.apply(arg); - } catch (Throwable ex) - { - if (clasz.isInstance(ex)) - { + } catch (Throwable ex) { + if (clasz.isInstance(ex)) { // Swallow this return self.apply(rescue.apply(arg, (ExType) ex)); - } else - { + } else { String msg = "Exception of incorrect type to be handled, only " + clasz.getName() + " are handled"; @@ -125,6 +142,22 @@ public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable> return (arg) -> func.apply(this.apply(arg)); } + /** Convert this function into one which will return an empty optional if an + * exception is thrown, returning an optional containing the value otherwise. + * + * Note that if this function returns a null value by itself, that will also + * yield an empty nullable. + * + * @param clasz The class of the exception. Needed because of type erasure, + * to ensure that we are catching the proper class. + * + * @return A function which returns an optional instead. + */ + default Function<InputType, Optional<ReturnType>> + makeTotal(Class<ExType> clasz) + { + return swallowMap(clasz, Optional::ofNullable, (ignored) -> Optional.empty()); + } /** * ThrowFunctions and functions which return a {@link Thrower} are isomorphic. * diff --git a/src/test/java/bjc/data/EitherTest.java b/src/test/java/bjc/data/EitherTest.java index bbf94b7..ef2d12b 100644 --- a/src/test/java/bjc/data/EitherTest.java +++ b/src/test/java/bjc/data/EitherTest.java @@ -2,24 +2,34 @@ package bjc.data; import static org.junit.Assert.*; -import org.junit.Test; +import java.util.*; -/** - * Test Either - * @author Ben Culkin - * - */ -public class EitherTest { +import org.junit.*; - /** - * Do a test of Either. - */ +@SuppressWarnings("javadoc") +public class EitherTest +{ + private Either<String, String> leftEither; + private Either<String, String> rightEither; + + @Before + public void setUp() throws Exception { + leftEither = Either.left("left"); + rightEither = Either.right("right"); + } + + @Test + public void testIsLeft() { + assertTrue("isLeft properly marks left eithers", leftEither.isLeft()); + assertFalse("isLeft properly marks right eithers", rightEither.isLeft()); + } + @Test - public void test() { - Either<String, String> left = Either.left("left"); - Either<String, String> right = Either.right("right"); - - assertNotEquals(left, right); + public void testGetLeft() { + assertEquals("getLeft treats left eithers properly", + Optional.of("left"), leftEither.getLeft()); + assertEquals("getLeft treats right eithers properly", + Optional.empty(), rightEither.getLeft()); } } |
