├── .gitignore ├── LICENSE.txt ├── README.md ├── pom.xml └── src ├── main └── java │ └── uk │ └── co │ └── benjiweber │ └── expressions │ ├── Action.java │ ├── ActionWithOneParam.java │ ├── Block.java │ ├── ChainableVoid.java │ ├── Coalesce.java │ ├── Curry.java │ ├── EqualsHashcode.java │ ├── InstanceOf.java │ ├── InstanceOfMethodReference.java │ ├── NullSafe.java │ ├── Print.java │ ├── Times.java │ ├── ToString.java │ ├── Try.java │ ├── Using.java │ ├── Value.java │ ├── caseclass │ ├── Case.java │ ├── Case2.java │ ├── Case3.java │ ├── MatchExample.java │ ├── MatchesAny.java │ ├── ShapeExample.java │ └── constructor │ ├── collections │ ├── EnhancedList.java │ ├── ForwardingList.java │ └── WithIndex.java │ ├── exceptions │ ├── Exceptions.java │ ├── OrElse.java │ ├── Result.java │ └── WrapBuilder.java │ ├── functions │ ├── ExceptionalBiConsumer.java │ ├── ExceptionalBiFunction.java │ ├── ExceptionalConsumer.java │ ├── ExceptionalFunction.java │ ├── ExceptionalQuadConsumer.java │ ├── ExceptionalQuadFunction.java │ ├── ExceptionalQuinConsumer.java │ ├── ExceptionalQuinFunction.java │ ├── ExceptionalSexConsumer.java │ ├── ExceptionalSexFunction.java │ ├── ExceptionalSupplier.java │ ├── ExceptionalTriConsumer.java │ ├── ExceptionalTriFunction.java │ ├── ExceptionalVoid.java │ ├── QuadConsumer.java │ ├── QuadFunction.java │ ├── QuinConsumer.java │ ├── QuinFunction.java │ ├── SexConsumer.java │ ├── SexFunction.java │ ├── TriConsumer.java │ └── TriFunction.java │ ├── properties │ ├── ExplicitName.java │ ├── GuessesName.java │ ├── Named.java │ ├── Property.java │ ├── Readonly.java │ └── Writeonly.java │ └── tuples │ ├── BiTuple.java │ ├── QuadTuple.java │ ├── QuinTuple.java │ ├── SexTuple.java │ ├── TriTuple.java │ ├── Tuple.java │ └── UniTuple.java └── test └── java └── uk └── co └── benjiweber └── expressions ├── ChainableVoidTest.java ├── CoalesceTest.java ├── CurryTest.java ├── InstanceOfTest.java ├── NullSafeTest.java ├── OptionalFromExceptionalTest.java ├── Paint.java ├── PropertyTest.java ├── TimesTest.java ├── TryAsExpressionTest.java ├── UsingTest.java ├── caseclass ├── CaseConstructorTest.java ├── CaseListTest.java ├── CaseParseArgumentsTest.java ├── CaseSomeNoneTest.java ├── CaseTest.java ├── CaseTreeTest.java ├── DecompositionConstructorReferenceTest.java ├── DecompositionTest.java ├── NestedDecompositionTest.java └── UnreadableDecompositionTest.java ├── collections └── IndexedListTest.java ├── exceptions ├── Example.java └── ExceptionsTest.java ├── functions └── ExceptionalFunctionTest.java └── tuples └── TupleTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.class 3 | *.iml 4 | .idea/ 5 | 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Benjamin Weber 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | expressions 2 | =========== 3 | 4 | Playing with Java8. Here's Try as an Expression. [More Examples](https://github.com/benjiman/expressions/blob/master/src/test/java/uk/co/benjiweber/expressions/TryAsExpressionTest.java) 5 | ```java 6 | @Test public void should_return_try_value() { 7 | String result = Try(() -> { 8 | return "try"; 9 | }).Catch(NullPointerException.class, e -> { 10 | return "catch"; 11 | }).apply(); 12 | 13 | assertEquals("try", result); 14 | } 15 | ``` 16 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | uk.co.benjiweber 8 | expressions 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | org.apache.maven.plugins 15 | maven-compiler-plugin 16 | 3.1 17 | 18 | 1.8 19 | 1.8 20 | 21 | 22 | 23 | 24 | 25 | 26 | junit 27 | junit 28 | 4.11 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/Action.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | public interface Action { 4 | public T apply() throws Exception; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/ActionWithOneParam.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | public interface ActionWithOneParam { 4 | T apply(U param) throws Exception; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/Block.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | public interface Block { 4 | public void apply() throws Exception; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/ChainableVoid.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import static uk.co.benjiweber.expressions.exceptions.Exceptions.unchecked; 4 | 5 | public class ChainableVoid { 6 | private final T obj; 7 | 8 | public ChainableVoid(T obj) { 9 | this.obj = obj; 10 | } 11 | 12 | public static ChainableVoid chain(T instance) { 13 | return new ChainableVoid<>(instance); 14 | } 15 | 16 | public ChainableVoid invoke(VoidMethodOn voidMethod) { 17 | unchecked(() -> voidMethod.invoke(obj)); 18 | return this; 19 | } 20 | 21 | public ChainableVoid invoke(VoidMethodOneArgOn method, U value) { 22 | unchecked(() -> method.invoke(obj, value)); 23 | return this; 24 | } 25 | 26 | public ChainableVoid invoke(VoidMethodTwoArgOn method, U value1, V value2) { 27 | unchecked(() -> method.invoke(obj, value1, value2)); 28 | return this; 29 | } 30 | 31 | public interface VoidMethodOn { 32 | void invoke(T instance) throws Exception; 33 | } 34 | 35 | public interface VoidMethodOneArgOn { 36 | void invoke(T instance, U value) throws Exception; 37 | } 38 | 39 | public interface VoidMethodTwoArgOn { 40 | void invoke(T instance, U value1, V value2) throws Exception; 41 | } 42 | 43 | public T unwrap() { 44 | return obj; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/Coalesce.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import java.util.Optional; 4 | import java.util.function.Supplier; 5 | 6 | import static java.util.Arrays.asList; 7 | 8 | public class Coalesce { 9 | 10 | public static T coalesce(Supplier... ts) { 11 | return asList(ts) 12 | .stream() 13 | .map(t -> t.get()) 14 | .filter(t -> t != null) 15 | .findFirst() 16 | .orElse(null); 17 | } 18 | 19 | public interface AnotherSupplier extends Supplier {} 20 | 21 | public static Optional coalesce(AnotherSupplier>... ts) { 22 | return asList(ts) 23 | .stream() 24 | .map(t -> t.get()) 25 | .filter(t -> t.isPresent()) 26 | .findFirst() 27 | .orElse(Optional.empty()); 28 | } 29 | 30 | public static Optional coalesce(Optional... ts) { 31 | return asList(ts) 32 | .stream() 33 | .filter(t -> t.isPresent()) 34 | .findFirst() 35 | .orElse(Optional.empty()); 36 | } 37 | 38 | public static T coalesce(T... ts) { 39 | return asList(ts) 40 | .stream() 41 | .filter(t -> t != null) 42 | .findFirst() 43 | .orElse(null); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/Curry.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import java.util.function.BiFunction; 4 | import java.util.function.Function; 5 | 6 | public class Curry { 7 | 8 | public static Function> curry(BiFunction fun) { 9 | return t -> u -> fun.apply(t,u); 10 | } 11 | 12 | public interface TriFunction { 13 | R apply(T t, U u, V v); 14 | } 15 | 16 | public static Function>> curry(TriFunction fun) { 17 | return t -> u -> v -> fun.apply(t,u,v); 18 | } 19 | 20 | public interface QuadFunction { 21 | R apply(T t, U u, V v, W w); 22 | } 23 | 24 | public static Function>>> curry(QuadFunction fun) { 25 | return t -> u -> v -> w -> fun.apply(t,u,v,w); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/EqualsHashcode.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | import java.util.function.Consumer; 6 | import java.util.function.Function; 7 | 8 | public interface EqualsHashcode { 9 | default boolean autoEquals(Object o) { 10 | if (this == o) return true; 11 | if (o == null || getClass() != o.getClass()) return false; 12 | final T value = (T)o; 13 | return props().stream() 14 | .allMatch(prop -> Objects.equals(prop.apply((T) this), prop.apply(value))); 15 | } 16 | 17 | default int autoHashCode() { 18 | return props().stream() 19 | .map(prop -> (Object)prop.apply((T)this)) 20 | .collect(ResultCalculator::new, ResultCalculator::accept, ResultCalculator::combine) 21 | .result; 22 | } 23 | 24 | 25 | static class ResultCalculator implements Consumer { 26 | private int result = 0; 27 | public void accept(Object value) { 28 | result = 31 * result + (value != null ? value.hashCode() : 0); 29 | } 30 | public void combine(ResultCalculator other) { 31 | result += other.result; 32 | } 33 | } 34 | 35 | List> props(); 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/InstanceOf.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import java.util.Optional; 4 | 5 | public class InstanceOf { 6 | 7 | public static InstanceOfBuilder when(final Object obj) { 8 | return new InstanceOfBuilder() { 9 | public ThenBuilder instanceOf(final Class cls) { 10 | return new ThenBuilder() { 11 | public ElseBuilder then(ActionWithOneParam ifAction) { 12 | return new ElseBuilder(){ 13 | public U otherwise(U value) { 14 | try { 15 | return cls.isInstance(obj) ? ifAction.apply((T)obj) : value; 16 | } catch (RuntimeException | Error e) { 17 | throw e; 18 | } catch (Exception e) { 19 | throw new RuntimeException( e); 20 | } 21 | } 22 | 23 | public Optional optional() { 24 | try { 25 | return cls.isInstance(obj) ? Optional.of(ifAction.apply((T) obj)) : Optional.empty(); 26 | } catch (RuntimeException | Error e) { 27 | throw e; 28 | } catch (Exception e) { 29 | throw new RuntimeException(e); 30 | } 31 | } 32 | }; 33 | } 34 | }; 35 | } 36 | }; 37 | } 38 | 39 | public interface InstanceOfBuilder { 40 | ThenBuilder instanceOf(Class cls); 41 | } 42 | 43 | public interface ThenBuilder { 44 | ElseBuilder then(ActionWithOneParam action); 45 | } 46 | 47 | public interface ElseBuilder { 48 | U otherwise(U value); 49 | Optional optional(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/InstanceOfMethodReference.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | public class InstanceOfMethodReference { 4 | 5 | public static void main(String... args) throws Exception { 6 | Object foo = "curry_and_partially_apply"; 7 | Object bar = 5; 8 | Integer result = 9 | when(foo) 10 | .is(String.class) 11 | .then(String::compareTo).apply("other"); 12 | 13 | System.out.println(result); 14 | } 15 | 16 | interface NoArg { 17 | U apply(T instance); 18 | } 19 | 20 | interface OneArg { 21 | U apply(T instance, ARG1 arg1); 22 | } 23 | 24 | static class OneArgApplicator { 25 | private final OneArg action; 26 | private final I instance; 27 | 28 | OneArgApplicator(OneArg action, I instance) { 29 | this.action = action; 30 | this.instance = instance; 31 | } 32 | 33 | public R apply(ARG1 arg1) { 34 | return action.apply(instance, arg1); 35 | } 36 | } 37 | 38 | static interface When { 39 | public Is is(Class cls); 40 | } 41 | 42 | static class Is { 43 | 44 | private T instance; 45 | 46 | public Is(T instance) { 47 | this.instance = instance; 48 | } 49 | 50 | OneArgApplicator then(OneArg action) throws Exception { 51 | return new OneArgApplicator(action, instance); 52 | } 53 | 54 | } 55 | 56 | public static When when(final Object input) { 57 | return new When() { 58 | public Is is(Class cls) { 59 | return new Is((T)input); 60 | } 61 | }; 62 | } 63 | 64 | public static OneArgApplicator then(T instance, OneArg action) throws Exception { 65 | return new OneArgApplicator(action, instance); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/NullSafe.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import java.util.Optional; 4 | import java.util.function.Supplier; 5 | 6 | public class NullSafe { 7 | public static Optional nullSafe(Supplier supplier) { 8 | try { 9 | return Optional.of(supplier.get()); 10 | } catch (NullPointerException e) { 11 | return Optional.empty(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/Print.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | public class Print { 4 | public static T println(T t) { 5 | System.out.println(t); 6 | return t; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/Times.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | public class Times { 4 | public interface Action { void apply(); } 5 | public interface TimesBuilder { void invoke(Action action); } 6 | 7 | public static TimesBuilder times(int times) { 8 | return action -> { 9 | for (int i = 0; i < times; i++) { 10 | action.apply(); 11 | } 12 | }; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/ToString.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import java.util.List; 4 | import java.util.function.Function; 5 | import java.util.stream.Collectors; 6 | 7 | public interface ToString { 8 | default String autoToString() { 9 | return "{" + 10 | props().stream() 11 | .map(prop -> (Object)prop.apply((T)this)) 12 | .map(prop -> prop == null ? "_" : prop.toString()) 13 | .collect(Collectors.joining(", ")) + 14 | "}"; 15 | } 16 | 17 | List> props(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/Try.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | public class Try { 7 | 8 | public static TryBuilder Try(Action action) { 9 | return new TryBuilder<>(action); 10 | } 11 | 12 | static interface Catch { 13 | TryBuilder Catch(Class cls, ActionWithOneParam action); 14 | } 15 | static interface Finally { 16 | TryBuilder Finally(Block action); 17 | } 18 | public static class TryBuilder implements Catch, Finally { 19 | 20 | private Action mainAction; 21 | 22 | Map, ActionWithOneParam> catches = new LinkedHashMap<>(); 23 | public Block finalAction; 24 | 25 | public TryBuilder(Action mainAction) { 26 | 27 | this.mainAction = mainAction; 28 | } 29 | public T apply() { 30 | try { 31 | return mainAction.apply(); 32 | } catch (RuntimeException e) { 33 | return handle(e, ex -> {throw ex;}); 34 | } catch (Error e) { 35 | return handle(e, ex -> {throw ex;}); 36 | } catch (Exception e) { 37 | return handle(e, ex -> {throw new RuntimeException(ex);}); 38 | } finally { 39 | if (finalAction != null) { 40 | try { 41 | finalAction.apply(); 42 | } catch (RuntimeException e) { 43 | return handle(e, ex -> {throw ex;}); 44 | } catch (Error e) { 45 | return handle(e, ex -> {throw ex;}); 46 | } catch (Exception e) { 47 | return handle(e, ex -> {throw new RuntimeException(ex);}); 48 | } 49 | } 50 | } 51 | } 52 | 53 | interface Thrower { 54 | T doThrow(E e) throws W; 55 | } 56 | 57 | private T handle(E e, Thrower thrower) throws W { 58 | for (Class cls : catches.keySet()) { 59 | if (cls.isAssignableFrom(e.getClass())) { 60 | return runCatchBlock(catches.get(cls),e); 61 | } 62 | } 63 | return thrower.doThrow(e); 64 | } 65 | 66 | @SuppressWarnings("unchecked") 67 | public T runCatchBlock(ActionWithOneParam catchBlock, Throwable throwable) { 68 | try { 69 | return catchBlock.apply((U)throwable); 70 | } catch (Exception e) { 71 | throw new RuntimeException(e); 72 | } 73 | } 74 | 75 | public TryBuilder Finally(Block action) { 76 | this.finalAction = action; 77 | return this; 78 | } 79 | 80 | @Override 81 | public TryBuilder Catch(Class cls, ActionWithOneParam action) { 82 | ActionWithOneParam foo = action::apply; 83 | this.catches.put(cls, foo); 84 | return this; 85 | } 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/Using.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import java.util.function.Function; 4 | import java.util.function.Supplier; 5 | 6 | public class Using { 7 | public static R using(Supplier closeableProvider, Function function) { 8 | try (T t = closeableProvider.get()) { 9 | return function.apply(t); 10 | } catch (RuntimeException | Error e) { 11 | throw e; 12 | } catch (Exception e) { 13 | throw new RuntimeException(e); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/Value.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.function.Function; 6 | 7 | public class Value implements EqualsHashcode, ToString { 8 | private List> props; 9 | 10 | @Override 11 | public boolean equals(Object other) { 12 | return autoEquals(other); 13 | } 14 | 15 | @Override 16 | public int hashCode() { 17 | return autoHashCode(); 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return autoToString(); 23 | } 24 | 25 | public List> props() { 26 | return props; 27 | } 28 | 29 | public T using(Function... props) { 30 | this.props = Arrays.asList(props); 31 | return (T) this; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/Case.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import uk.co.benjiweber.expressions.EqualsHashcode; 4 | import uk.co.benjiweber.expressions.caseclass.constructor.references.BiMatch; 5 | import uk.co.benjiweber.expressions.caseclass.constructor.references.TriMatch; 6 | import uk.co.benjiweber.expressions.caseclass.constructor.references.UniMatch; 7 | import uk.co.benjiweber.expressions.functions.TriFunction; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Optional; 12 | import java.util.function.BiFunction; 13 | import java.util.function.Function; 14 | import java.util.function.Predicate; 15 | import java.util.function.Supplier; 16 | import java.util.stream.Collectors; 17 | import java.util.stream.Stream; 18 | 19 | import static java.util.Arrays.asList; 20 | 21 | public interface Case extends EqualsHashcode { 22 | default MatchBuilder match() { 23 | return new MatchBuilder() { 24 | public MatchBuilderR when(T value, Function f) { 25 | return new MatchBuilderR(asList(MatchDefinition.create(value, f)), Case.this); 26 | } 27 | public MatchBuilderR when(TwoMissing value, BiFunction f) { 28 | Function valueExtractor = t -> f.apply(value.prop1((T)Case.this), value.prop2((T)Case.this)); 29 | return new MatchBuilderR(asList(MatchDefinition.create(value.original(), valueExtractor)), Case.this); 30 | } 31 | 32 | public ZeroMatchConstructorBuilder when(Supplier constructor) { 33 | return new ZeroMatchConstructorBuilder() { 34 | public MatchBuilderR then(Function f) { 35 | T original = constructor.get(); 36 | return new MatchBuilderR(asList(MatchDefinition.create(original, f)), Case.this); 37 | } 38 | }; 39 | } 40 | 41 | public ZeroMatchConstructorBuilder when(Function constructor, A a) { 42 | return new ZeroMatchConstructorBuilder() { 43 | public MatchBuilderR then(Function f) { 44 | T original = constructor.apply(a); 45 | return new MatchBuilderR(asList(MatchDefinition.create(original, f)), Case.this); 46 | } 47 | }; 48 | } 49 | 50 | public UniMatchConstructorBuilder when(Function constructor, MatchesAny a) { 51 | return new UniMatchConstructorBuilder() { 52 | public MatchBuilderR then(Function f) { 53 | T original = constructor.apply(null); 54 | List missingProps = missingProps((Case)Case.this, (Case)original); 55 | Function valueExtractor = t -> f.apply((A)missingProps.get(0)); 56 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 57 | } 58 | }; 59 | } 60 | 61 | 62 | @Override 63 | public UniMatchConstructorBuilder when(UniMatch ref) { 64 | return new UniMatchConstructorBuilder() { 65 | public MatchBuilderR then(Function f) { 66 | T original = ref.comparee(); 67 | List missingProps = missingProps((Case)Case.this, (Case)original); 68 | Function valueExtractor = t -> f.apply((A)missingProps.get(0)); 69 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 70 | } 71 | }; 72 | } 73 | 74 | @Override 75 | public BiMatchConstructorBuilder when(BiMatch ref) { 76 | return new BiMatchConstructorBuilder() { 77 | public MatchBuilderR then(BiFunction f) { 78 | T original = ref.comparee(); 79 | List missingProps = missingProps((Case)Case.this, (Case)original); 80 | Function valueExtractor = t -> f.apply((A)missingProps.get(0), (B)missingProps.get(1)); 81 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 82 | } 83 | }; 84 | } 85 | 86 | @Override 87 | public TriMatchConstructorBuilder when(TriMatch ref) { 88 | return new TriMatchConstructorBuilder() { 89 | public MatchBuilderR then(TriFunction f) { 90 | T original = ref.comparee(); 91 | List missingProps = missingProps((Case)Case.this, (Case)original); 92 | Function valueExtractor = t -> f.apply((A)missingProps.get(0), (B)missingProps.get(1), (C)missingProps.get(2)); 93 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 94 | } 95 | }; 96 | 97 | } 98 | 99 | 100 | public ZeroMatchConstructorBuilder when(BiFunction constructor, A a, B b) { 101 | return new ZeroMatchConstructorBuilder() { 102 | public MatchBuilderR then(Function f) { 103 | T original = constructor.apply(a,b); 104 | return new MatchBuilderR(asList(MatchDefinition.create(original, f)), Case.this); 105 | } 106 | }; 107 | } 108 | 109 | public BiMatchConstructorBuilder when(BiFunction constructor, MatchesAny a, MatchesAny b) { 110 | return new BiMatchConstructorBuilder() { 111 | public MatchBuilderR then(BiFunction f) { 112 | T original = constructor.apply(null,null); 113 | List missingProps = missingProps((Case)Case.this, (Case)original); 114 | Function valueExtractor = t -> f.apply((A)missingProps.get(0), (B)missingProps.get(1)); 115 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 116 | } 117 | }; 118 | } 119 | 120 | public UniMatchConstructorBuilder when(BiFunction constructor, MatchesAny a, B b) { 121 | return new UniMatchConstructorBuilder() { 122 | public MatchBuilderR then(Function f) { 123 | T original = constructor.apply(null,b); 124 | List missingProps = missingProps((Case)Case.this, (Case)original); 125 | Function valueExtractor = t -> f.apply((A)missingProps.get(0)); 126 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 127 | } 128 | }; 129 | } 130 | 131 | public UniMatchConstructorBuilder when(BiFunction constructor, A a, MatchesAny b) { 132 | return new UniMatchConstructorBuilder() { 133 | public MatchBuilderR then(Function f) { 134 | T original = constructor.apply(a,null); 135 | List missingProps = missingProps((Case)Case.this, (Case)original); 136 | Function valueExtractor = t -> f.apply((B)missingProps.get(0)); 137 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 138 | } 139 | }; 140 | } 141 | 142 | 143 | 144 | 145 | public ZeroMatchConstructorBuilder when(TriFunction constructor, A a, B b, C c) { 146 | return new ZeroMatchConstructorBuilder() { 147 | public MatchBuilderR then(Function f) { 148 | T original = constructor.apply(a,b,c); 149 | return new MatchBuilderR(asList(MatchDefinition.create(original, f)), Case.this); 150 | } 151 | }; 152 | } 153 | 154 | public TriMatchConstructorBuilder when(TriFunction constructor, MatchesAny a, MatchesAny b, MatchesAny c) { 155 | return new TriMatchConstructorBuilder() { 156 | public MatchBuilderR then(TriFunction f) { 157 | T original = constructor.apply(null,null,null); 158 | List missingProps = missingProps((Case)Case.this, (Case)original); 159 | Function valueExtractor = t -> f.apply((A)missingProps.get(0), (B)missingProps.get(1), (C)missingProps.get(2)); 160 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 161 | } 162 | }; 163 | } 164 | 165 | public BiMatchConstructorBuilder when(TriFunction constructor, MatchesAny a, MatchesAny b, C c) { 166 | return new BiMatchConstructorBuilder() { 167 | public MatchBuilderR then(BiFunction f) { 168 | T original = constructor.apply(null,null,c); 169 | List missingProps = missingProps((Case)Case.this, (Case)original); 170 | Function valueExtractor = t -> f.apply((A)missingProps.get(0), (B)missingProps.get(1)); 171 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 172 | } 173 | }; 174 | } 175 | 176 | 177 | public BiMatchConstructorBuilder when(TriFunction constructor, A a, MatchesAny b, MatchesAny c) { 178 | return new BiMatchConstructorBuilder() { 179 | public MatchBuilderR then(BiFunction f) { 180 | T original = constructor.apply(a,null,null); 181 | List missingProps = missingProps((Case)Case.this, (Case)original); 182 | Function valueExtractor = t -> f.apply((B)missingProps.get(0), (C)missingProps.get(1)); 183 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 184 | } 185 | }; 186 | } 187 | 188 | public BiMatchConstructorBuilder when(TriFunction constructor, MatchesAny a, B b, MatchesAny c) { 189 | return new BiMatchConstructorBuilder() { 190 | public MatchBuilderR then(BiFunction f) { 191 | T original = constructor.apply(null,b,null); 192 | List missingProps = missingProps((Case)Case.this, (Case)original); 193 | Function valueExtractor = t -> f.apply((A)missingProps.get(0), (C)missingProps.get(1)); 194 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 195 | } 196 | }; 197 | } 198 | 199 | @Override 200 | public UniMatchConstructorBuilder when(TriFunction constructor, MatchesAny a, B b, C c) { 201 | return new UniMatchConstructorBuilder() { 202 | public MatchBuilderR then(Function f) { 203 | T original = constructor.apply(null,b,c); 204 | List missingProps = missingProps((Case)Case.this, (Case)original); 205 | Function valueExtractor = t -> f.apply((A)missingProps.get(0)); 206 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 207 | } 208 | }; 209 | } 210 | 211 | @Override 212 | public UniMatchConstructorBuilder when(TriFunction constructor, A a, MatchesAny b, C c) { 213 | return new UniMatchConstructorBuilder() { 214 | public MatchBuilderR then(Function f) { 215 | T original = constructor.apply(a,null,c); 216 | List missingProps = missingProps((Case)Case.this, (Case)original); 217 | Function valueExtractor = t -> f.apply((B)missingProps.get(0)); 218 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 219 | } 220 | }; 221 | } 222 | 223 | 224 | @Override 225 | public UniMatchConstructorBuilder when(TriFunction constructor, A a, B b, MatchesAny c) { 226 | return new UniMatchConstructorBuilder() { 227 | public MatchBuilderR then(Function f) { 228 | T original = constructor.apply(a,b,null); 229 | List missingProps = missingProps((Case)Case.this, (Case)original); 230 | Function valueExtractor = t -> f.apply((C)missingProps.get(0)); 231 | return new MatchBuilderR(asList(MatchDefinition.create(original,valueExtractor)),Case.this); 232 | } 233 | }; 234 | } 235 | 236 | public MatchBuilderR when(OneMissing value, Function f) { 237 | Function valueExtractor = t -> f.apply(value.prop1((T) Case.this)); 238 | return new MatchBuilderR(asList(MatchDefinition.create(value.original(), valueExtractor)), Case.this); 239 | } 240 | }; 241 | } 242 | 243 | static List missingProps(Case value, Case toMatch) { 244 | Stream missingPropertiesFromNestedCaseProperties = 245 | toMatch.props().stream() 246 | .filter(prop -> prop.apply((T) toMatch) instanceof Case) 247 | .flatMap(prop -> missingProps((Case) prop.apply((T) value), (Case) prop.apply((T) toMatch)).stream()); 248 | 249 | Stream missingDirectProperties = toMatch.props().stream() 250 | .filter(prop -> prop.apply((T) toMatch) == null) 251 | .map(prop -> prop.apply((T) value)); 252 | 253 | return Stream.concat(missingPropertiesFromNestedCaseProperties,missingDirectProperties) 254 | .collect(Collectors.toList()); 255 | } 256 | 257 | default OneMissing missing(Function prop1) { 258 | return new OneMissing() { 259 | public A prop1(T extractFrom) { return prop1.apply(extractFrom); } 260 | public T original() { return (T)Case.this; } 261 | }; 262 | } 263 | 264 | default TwoMissing missing(Function prop1, Function prop2) { 265 | return new TwoMissing() { 266 | public A prop1(T extractFrom) { return prop1.apply(extractFrom); } 267 | public B prop2(T extractFrom) { return prop2.apply(extractFrom); } 268 | public T original() { return (T)Case.this; } 269 | } ; 270 | } 271 | 272 | public interface TwoMissing { 273 | A prop1(T extractFrom); 274 | B prop2(T extractFrom); 275 | T original(); 276 | } 277 | public interface OneMissing { 278 | A prop1(T extractFrom); 279 | T original(); 280 | } 281 | 282 | public interface MatchBuilder { 283 | MatchBuilderR when(T value, Function f); 284 | MatchBuilderR when(TwoMissing value, BiFunction f); 285 | 286 | BiMatchConstructorBuilder when(TriFunction constructor, MatchesAny a, MatchesAny b, C c); 287 | BiMatchConstructorBuilder when(TriFunction constructor, MatchesAny a, B b, MatchesAny c); 288 | BiMatchConstructorBuilder when(TriFunction constructor, A a, MatchesAny b, MatchesAny c); 289 | 290 | UniMatchConstructorBuilder when(TriFunction constructor, MatchesAny a, B b, C c); 291 | UniMatchConstructorBuilder when(TriFunction constructor, A a, MatchesAny b, C c); 292 | UniMatchConstructorBuilder when(TriFunction constructor, A a, B b, MatchesAny c); 293 | 294 | TriMatchConstructorBuilder when(TriFunction constructor, MatchesAny a, MatchesAny b, MatchesAny c); 295 | ZeroMatchConstructorBuilder when(TriFunction constructor, A a, B b, C c); 296 | 297 | 298 | ZeroMatchConstructorBuilder when(BiFunction constructor, A a, B b); 299 | BiMatchConstructorBuilder when(BiFunction constructor, MatchesAny a, MatchesAny b); 300 | UniMatchConstructorBuilder when(BiFunction constructor, MatchesAny a, B b); 301 | UniMatchConstructorBuilder when(BiFunction constructor, A a, MatchesAny b); 302 | 303 | ZeroMatchConstructorBuilder when(Function constructor, A a); 304 | UniMatchConstructorBuilder when(Function constructor, MatchesAny a); 305 | 306 | ZeroMatchConstructorBuilder when(Supplier constructor); 307 | 308 | 309 | 310 | UniMatchConstructorBuilder when(UniMatch ref); 311 | BiMatchConstructorBuilder when(BiMatch ref); 312 | TriMatchConstructorBuilder when(TriMatch ref); 313 | } 314 | 315 | public interface ZeroMatchConstructorBuilder { 316 | MatchBuilderR then(Function f); 317 | } 318 | 319 | public interface ZeroMatchConstructorBuilderR { 320 | MatchBuilderR then(Function f); 321 | } 322 | 323 | 324 | public interface TriMatchConstructorBuilder { 325 | MatchBuilderR then(TriFunction f); 326 | } 327 | 328 | public interface TriMatchConstructorBuilderR { 329 | MatchBuilderR then(TriFunction f); 330 | } 331 | 332 | public interface BiMatchConstructorBuilder { 333 | MatchBuilderR then(BiFunction f); 334 | } 335 | 336 | public interface BiMatchConstructorBuilderR { 337 | MatchBuilderR then(BiFunction f); 338 | } 339 | 340 | public interface UniMatchConstructorBuilder { 341 | MatchBuilderR then(Function f); 342 | } 343 | 344 | public interface UniMatchConstructorBuilderR { 345 | MatchBuilderR then(Function f); 346 | } 347 | 348 | interface MatchDefinition { 349 | T value(); 350 | Function f(); 351 | static MatchDefinition create(T value, Function f) { 352 | return new MatchDefinition() { 353 | public T value() { return value; } 354 | public Function f() { return f; } 355 | }; 356 | } 357 | 358 | static Predicate> matches(Case value) { 359 | return match -> recursiveCompareIgnoringUnknownProperties(value, match.value()); 360 | } 361 | 362 | static boolean recursiveCompareIgnoringUnknownProperties(Case value, T comparisonValue) { 363 | return value.props().stream() 364 | .allMatch( 365 | prop -> { 366 | Object lhs = prop.apply(comparisonValue); 367 | if (lhs == null) return true; 368 | if (lhs instanceof Case) return recursiveCompareIgnoringUnknownProperties((Case) prop.apply((T) value), lhs); 369 | return (lhs.equals(prop.apply((T) value))); 370 | } 371 | ); 372 | } 373 | 374 | } 375 | 376 | public static class MatchBuilderR { 377 | private List> cases = new ArrayList>(); 378 | private Function defaultCase; 379 | private Case value; 380 | 381 | private MatchBuilderR(List> cases, Case value) { 382 | this.value = value; 383 | this.cases.addAll(cases); 384 | } 385 | 386 | public MatchBuilderR when(T value, Function f) { 387 | cases.add(MatchDefinition.create(value, f)); 388 | return this; 389 | } 390 | 391 | public MatchBuilderR when(OneMissing value, Function f) { 392 | Function valueExtractor = t -> f.apply(value.prop1((T)this.value)); 393 | cases.add(MatchDefinition.create(value.original(), valueExtractor)); 394 | return this; 395 | } 396 | 397 | public MatchBuilderR when(TwoMissing value, BiFunction f) { 398 | Function valueExtractor = t -> f.apply(value.prop1((T)this.value), value.prop2((T)this.value)); 399 | cases.add(MatchDefinition.create(value.original(), valueExtractor)); 400 | return this; 401 | } 402 | 403 | 404 | 405 | public ZeroMatchConstructorBuilderR when(Supplier constructor) { 406 | return new ZeroMatchConstructorBuilderR() { 407 | public MatchBuilderR then(Function f) { 408 | T original = constructor.get(); 409 | cases.add(MatchDefinition.create(original, f)); 410 | return MatchBuilderR.this; 411 | } 412 | }; 413 | } 414 | 415 | 416 | 417 | 418 | 419 | public ZeroMatchConstructorBuilderR when(Function constructor, A a) { 420 | return new ZeroMatchConstructorBuilderR() { 421 | public MatchBuilderR then(Function f) { 422 | T original = constructor.apply(a); 423 | cases.add(MatchDefinition.create(original, f)); 424 | return MatchBuilderR.this; 425 | } 426 | }; 427 | } 428 | 429 | public UniMatchConstructorBuilderR when(Function constructor, MatchesAny a) { 430 | return new UniMatchConstructorBuilderR() { 431 | public MatchBuilderR then(Function f) { 432 | T original = constructor.apply(null); 433 | List missingProps = missingProps(MatchBuilderR.this.value, (Case)original); 434 | Function valueExtractor = t -> f.apply((A)missingProps.get(0)); 435 | cases.add(MatchDefinition.create(original, valueExtractor)); 436 | return MatchBuilderR.this; 437 | } 438 | }; 439 | } 440 | 441 | 442 | 443 | 444 | 445 | public ZeroMatchConstructorBuilderR when(BiFunction constructor, A a, B b) { 446 | return new ZeroMatchConstructorBuilderR() { 447 | public MatchBuilderR then(Function f) { 448 | T original = constructor.apply(a,b); 449 | cases.add(MatchDefinition.create(original, f)); 450 | return MatchBuilderR.this; 451 | } 452 | }; 453 | } 454 | 455 | public BiMatchConstructorBuilderR when(BiFunction constructor, MatchesAny a, MatchesAny b) { 456 | return new BiMatchConstructorBuilderR() { 457 | public MatchBuilderR then(BiFunction f) { 458 | T original = constructor.apply(null,null); 459 | List missingProps = missingProps(MatchBuilderR.this.value, (Case)original); 460 | Function valueExtractor = t -> f.apply((A)missingProps.get(0), (B)missingProps.get(1)); 461 | cases.add(MatchDefinition.create(original, valueExtractor)); 462 | return MatchBuilderR.this; 463 | } 464 | }; 465 | } 466 | 467 | public UniMatchConstructorBuilderR when(BiFunction constructor, MatchesAny a, B b) { 468 | return new UniMatchConstructorBuilderR() { 469 | public MatchBuilderR then(Function f) { 470 | T original = constructor.apply(null,b); 471 | List missingProps = missingProps(MatchBuilderR.this.value, (Case)original); 472 | Function valueExtractor = t -> f.apply((A)missingProps.get(0)); 473 | cases.add(MatchDefinition.create(original, valueExtractor)); 474 | return MatchBuilderR.this; 475 | } 476 | }; 477 | } 478 | 479 | public UniMatchConstructorBuilderR when(BiFunction constructor, A a, MatchesAny b) { 480 | return new UniMatchConstructorBuilderR() { 481 | public MatchBuilderR then(Function f) { 482 | T original = constructor.apply(a,null); 483 | List missingProps = missingProps(MatchBuilderR.this.value, (Case)original); 484 | Function valueExtractor = t -> f.apply((B)missingProps.get(0)); 485 | cases.add(MatchDefinition.create(original, valueExtractor)); 486 | return MatchBuilderR.this; 487 | } 488 | }; 489 | } 490 | 491 | 492 | 493 | 494 | 495 | public ZeroMatchConstructorBuilderR when(TriFunction constructor, A a, B b, C c) { 496 | return new ZeroMatchConstructorBuilderR() { 497 | public MatchBuilderR then(Function f) { 498 | T original = constructor.apply(a,b,c); 499 | cases.add(MatchDefinition.create(original, f)); 500 | return MatchBuilderR.this; 501 | } 502 | }; 503 | } 504 | 505 | public TriMatchConstructorBuilderR when(TriFunction constructor, MatchesAny a, MatchesAny b, MatchesAny c) { 506 | return new TriMatchConstructorBuilderR() { 507 | public MatchBuilderR then(TriFunction f) { 508 | T original = constructor.apply(null,null,null); 509 | List missingProps = missingProps(MatchBuilderR.this.value, (Case)original); 510 | Function valueExtractor = t -> f.apply((A)missingProps.get(0), (B)missingProps.get(1), (C)missingProps.get(2)); 511 | cases.add(MatchDefinition.create(original,valueExtractor)); 512 | return MatchBuilderR.this; 513 | } 514 | }; 515 | } 516 | 517 | public BiMatchConstructorBuilderR when(TriFunction constructor, MatchesAny a, MatchesAny b, C c) { 518 | return new BiMatchConstructorBuilderR() { 519 | public MatchBuilderR then(BiFunction f) { 520 | T original = constructor.apply(null,null,c); 521 | List missingProps = missingProps(MatchBuilderR.this.value, (Case)original); 522 | Function valueExtractor = t -> f.apply((A)missingProps.get(0), (B)missingProps.get(1)); 523 | cases.add(MatchDefinition.create(original,valueExtractor)); 524 | return MatchBuilderR.this; 525 | } 526 | }; 527 | } 528 | 529 | public BiMatchConstructorBuilderR when(TriFunction constructor, MatchesAny a, B b, MatchesAny c) { 530 | return new BiMatchConstructorBuilderR() { 531 | public MatchBuilderR then(BiFunction f) { 532 | T original = constructor.apply(null,b,null); 533 | List missingProps = missingProps(MatchBuilderR.this.value, (Case)original); 534 | Function valueExtractor = t -> f.apply((A)missingProps.get(0), (C)missingProps.get(1)); 535 | cases.add(MatchDefinition.create(original,valueExtractor)); 536 | return MatchBuilderR.this; 537 | } 538 | }; 539 | } 540 | 541 | public BiMatchConstructorBuilderR when(TriFunction constructor, A a, MatchesAny b, MatchesAny c) { 542 | return new BiMatchConstructorBuilderR() { 543 | public MatchBuilderR then(BiFunction f) { 544 | T original = constructor.apply(a,null,null); 545 | List missingProps = missingProps(MatchBuilderR.this.value, (Case)original); 546 | Function valueExtractor = t -> f.apply((B)missingProps.get(0), (C)missingProps.get(1)); 547 | cases.add(MatchDefinition.create(original,valueExtractor)); 548 | return MatchBuilderR.this; 549 | } 550 | }; 551 | } 552 | 553 | public UniMatchConstructorBuilderR when(TriFunction constructor, MatchesAny a, B b, C c) { 554 | return new UniMatchConstructorBuilderR() { 555 | public MatchBuilderR then(Function f) { 556 | T original = constructor.apply(null,b,c); 557 | List missingProps = missingProps(MatchBuilderR.this.value, (Case)original); 558 | Function valueExtractor = t -> f.apply((A)missingProps.get(0)); 559 | cases.add(MatchDefinition.create(original,valueExtractor)); 560 | return MatchBuilderR.this; 561 | } 562 | }; 563 | } 564 | 565 | public UniMatchConstructorBuilderR when(TriFunction constructor, A a, MatchesAny b, C c) { 566 | return new UniMatchConstructorBuilderR() { 567 | public MatchBuilderR then(Function f) { 568 | T original = constructor.apply(a,null,c); 569 | List missingProps = missingProps(MatchBuilderR.this.value, (Case)original); 570 | Function valueExtractor = t -> f.apply((B)missingProps.get(0)); 571 | cases.add(MatchDefinition.create(original,valueExtractor)); 572 | return MatchBuilderR.this; 573 | } 574 | }; 575 | } 576 | 577 | public UniMatchConstructorBuilderR when(TriFunction constructor, A a, B b, MatchesAny c) { 578 | return new UniMatchConstructorBuilderR() { 579 | public MatchBuilderR then(Function f) { 580 | T original = constructor.apply(a,b,null); 581 | List missingProps = missingProps(MatchBuilderR.this.value, (Case)original); 582 | Function valueExtractor = t -> f.apply((C)missingProps.get(0)); 583 | cases.add(MatchDefinition.create(original,valueExtractor)); 584 | return MatchBuilderR.this; 585 | } 586 | }; 587 | } 588 | 589 | public R otherwise(R defaultValue) { 590 | return _(t -> defaultValue); 591 | } 592 | 593 | public R _(R defaultValue) { 594 | return _(t -> defaultValue); 595 | } 596 | 597 | public Optional toOptional() { 598 | return Optional.ofNullable(_(t -> null)); 599 | } 600 | 601 | public R _(Function f) { 602 | defaultCase = f; 603 | return cases.stream() 604 | .filter(MatchDefinition.matches(value)) 605 | .findFirst() 606 | .map(match -> match.f().apply((T)value)) 607 | .orElseGet(() -> defaultCase.apply((T)value)); 608 | } 609 | } 610 | 611 | } 612 | 613 | 614 | 615 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/Case2.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import java.util.function.Function; 4 | 5 | public interface Case2 { 6 | default MatchBuilderNone match() { 7 | return new MatchBuilderNone() { 8 | public MatchBuilderOne when(Class clsT, Function fT) { 9 | return (clsU, fU) -> { 10 | if (clsT.isAssignableFrom(Case2.this.getClass())) return fT.apply((T)Case2.this); 11 | if (clsU.isAssignableFrom(Case2.this.getClass())) return fU.apply((U)Case2.this); 12 | 13 | throw new IllegalStateException("Match failed"); 14 | }; 15 | } 16 | }; 17 | } 18 | 19 | public interface MatchBuilderNone { 20 | MatchBuilderOne when(Class cls, Function f); 21 | 22 | } 23 | 24 | public interface MatchBuilderOne { 25 | R when(Class cls, Function f); 26 | } 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/Case3.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import java.util.function.Function; 4 | 5 | public interface Case3 { 6 | default MatchBuilderNone match() { 7 | return new MatchBuilderNone() { 8 | public MatchBuilderOne when(Class clsT, Function fT) { 9 | return (clsU, fU) -> (clsV, fV) -> { 10 | if (clsT.isAssignableFrom(Case3.this.getClass())) return fT.apply((T)Case3.this); 11 | if (clsU.isAssignableFrom(Case3.this.getClass())) return fU.apply((U)Case3.this); 12 | if (clsV.isAssignableFrom(Case3.this.getClass())) return fV.apply((V)Case3.this); 13 | 14 | throw new IllegalStateException("Match failed"); 15 | }; 16 | } 17 | }; 18 | } 19 | 20 | public interface MatchBuilderNone { 21 | MatchBuilderOne when(Class cls, Function f); 22 | 23 | } 24 | 25 | public interface MatchBuilderOne { 26 | MatchBuilderTwo when(Class cls, Function f); 27 | } 28 | 29 | public interface MatchBuilderTwo { 30 | R when(Class cls, Function f); 31 | } 32 | 33 | 34 | static Class erasesTo(Class cls) { 35 | return (Class) cls; 36 | } 37 | } 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/MatchExample.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import uk.co.benjiweber.expressions.Value; 4 | 5 | import static uk.co.benjiweber.expressions.caseclass.MatchesAny._; 6 | 7 | public class MatchExample { 8 | interface Person extends Case { 9 | String firstname(); 10 | String lastname(); 11 | Integer age(); 12 | 13 | static Person person(String firstname, String lastname, Integer age) { 14 | abstract class PersonValue extends Value implements Person {} 15 | return new PersonValue() { 16 | public String firstname() { return firstname; } 17 | public String lastname() { return lastname; } 18 | public Integer age() { return age; } 19 | }.using(Person::firstname, Person::lastname, Person::age); 20 | } 21 | } 22 | public static void main(String... args) { 23 | System.out.println(description(Person.person("Bill","Smith",18))); 24 | } 25 | 26 | static String description(Person person) { 27 | return person.match() 28 | .when(Person::person, "Bob", _, _).then( (lastname, age) -> lastname ) 29 | .when(Person::person, _,_, 18).then( (firstname, lastname) -> lastname ) 30 | ._("unknown"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/MatchesAny.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | public class MatchesAny { 4 | public static MatchesAny _ = new MatchesAny(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/ShapeExample.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import static uk.co.benjiweber.expressions.caseclass.ShapeExample.Rectangle.rectangle; 4 | 5 | public class ShapeExample { 6 | 7 | interface Shape extends Case3 {} 8 | 9 | interface Circle extends Shape { 10 | int radius(); 11 | static Circle circle(int radius) { 12 | return () -> radius; 13 | } 14 | } 15 | 16 | interface Square extends Shape { 17 | int width(); 18 | static Square square(int width) { 19 | return () -> width; 20 | } 21 | } 22 | 23 | interface Rectangle extends Shape { 24 | int width(); 25 | int height(); 26 | static Rectangle rectangle(int width, int height) { 27 | return new Rectangle() { 28 | public int width() { return width; } 29 | public int height() { return height; } 30 | }; 31 | } 32 | } 33 | 34 | public static void main(String... args) { 35 | System.out.println(description(rectangle(5, 4))); 36 | } 37 | 38 | private static String description(Shape shape) { 39 | return shape.match() 40 | .when(Circle.class, circle -> "circle" + circle.radius()) 41 | .when(Square.class, square -> "square" + square.width()) 42 | .when(Rectangle.class, rectangle -> "rectangle" + rectangle.width() + rectangle.height()); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/constructor/BiConstructor.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass.constructor; 2 | 3 | import uk.co.benjiweber.expressions.caseclass.MatchesAny; 4 | import uk.co.benjiweber.expressions.caseclass.constructor.references.BiMatch; 5 | import uk.co.benjiweber.expressions.caseclass.constructor.references.NoMatch; 6 | import uk.co.benjiweber.expressions.caseclass.constructor.references.TriMatch; 7 | import uk.co.benjiweber.expressions.caseclass.constructor.references.UniMatch; 8 | 9 | import java.util.function.BiFunction; 10 | 11 | public class BiConstructor { 12 | private BiFunction constructor; 13 | 14 | public BiConstructor(BiFunction ctor) { 15 | this.constructor = ctor; 16 | } 17 | 18 | public static BiConstructor a(BiFunction ctor) { 19 | return new BiConstructor<>(ctor); 20 | } 21 | public static BiConstructor an(BiFunction ctor) { 22 | return new BiConstructor<>(ctor); 23 | } 24 | 25 | public NoMatch matching(A arg0, B arg1) { 26 | return () -> constructor.apply(arg0, arg1); 27 | } 28 | 29 | public UniMatch matching(A arg0, MatchesAny _) { 30 | return () -> constructor.apply(arg0, null); 31 | } 32 | public UniMatch matching(MatchesAny _, B arg1) { 33 | return () -> constructor.apply(null, arg1); 34 | } 35 | public UniMatch matching(A arg0, UniMatch arg1) { 36 | return () -> constructor.apply(arg0, arg1.comparee()); 37 | } 38 | public UniMatch matching(UniMatch arg0, B arg1) { 39 | return () -> constructor.apply(arg0.comparee(), arg1); 40 | } 41 | 42 | public BiMatch matching(MatchesAny arg0, MatchesAny arg1) { 43 | return () -> constructor.apply(null, null); 44 | } 45 | public BiMatch matching(A arg0, BiMatch arg1) { 46 | return () -> constructor.apply(arg0, arg1.comparee()); 47 | } 48 | public BiMatch matching(BiMatch arg0, B arg1) { 49 | return () -> constructor.apply(arg0.comparee(), arg1); 50 | } 51 | public BiMatch matching(UniMatch arg0, UniMatch arg1) { 52 | return () -> constructor.apply(arg0.comparee(), arg1.comparee()); 53 | } 54 | public BiMatch matching(UniMatch arg0, MatchesAny arg1) { 55 | return () -> constructor.apply(arg0.comparee(), null); 56 | } 57 | public BiMatch matching(MatchesAny arg0, UniMatch arg1) { 58 | return () -> constructor.apply(null, arg1.comparee()); 59 | } 60 | 61 | public TriMatch matching(A arg0, TriMatch arg1) { 62 | return () -> constructor.apply(arg0, arg1.comparee()); 63 | } 64 | public TriMatch matching(TriMatch arg0, B arg1) { 65 | return () -> constructor.apply(arg0.comparee(), arg1); 66 | } 67 | 68 | public TriMatch matching(MatchesAny arg0, BiMatch arg1) { 69 | return () -> constructor.apply(null, arg1.comparee()); 70 | } 71 | 72 | public TriMatch matching(BiMatch arg0, MatchesAny arg1) { 73 | return () -> constructor.apply(arg0.comparee(), null); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/constructor/ForComparing.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass.constructor; 2 | 3 | public interface ForComparing { 4 | T comparee(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/constructor/TriConstructor.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass.constructor; 2 | 3 | import uk.co.benjiweber.expressions.caseclass.MatchesAny; 4 | import uk.co.benjiweber.expressions.caseclass.constructor.references.BiMatch; 5 | import uk.co.benjiweber.expressions.caseclass.constructor.references.NoMatch; 6 | import uk.co.benjiweber.expressions.caseclass.constructor.references.TriMatch; 7 | import uk.co.benjiweber.expressions.caseclass.constructor.references.UniMatch; 8 | import uk.co.benjiweber.expressions.functions.TriFunction; 9 | 10 | public class TriConstructor { 11 | private TriFunction constructor; 12 | 13 | public TriConstructor(TriFunction ctor) { 14 | this.constructor = ctor; 15 | } 16 | 17 | public static TriConstructor a(TriFunction ctor) { 18 | return new TriConstructor<>(ctor); 19 | } 20 | public static TriConstructor an(TriFunction ctor) { 21 | return new TriConstructor<>(ctor); 22 | } 23 | 24 | 25 | public NoMatch matching(A arg0, B arg1, C arg2) { 26 | return () -> constructor.apply(arg0, arg1, arg2); 27 | } 28 | public UniMatch matching(A arg0, B arg1, UniMatch arg2) { 29 | return () -> constructor.apply(arg0, arg1, arg2.comparee()); 30 | } 31 | public UniMatch matching(A arg0, UniMatch arg1, C arg2) { 32 | return () -> constructor.apply(arg0, arg1.comparee(), arg2); 33 | } 34 | public UniMatch matching(UniMatch arg0, B arg1, C arg2) { 35 | return () -> constructor.apply(arg0.comparee(), arg1, arg2); 36 | } 37 | 38 | public UniMatch matching(A arg0, B arg1, MatchesAny arg2) { 39 | return () -> constructor.apply(arg0, arg1, null); 40 | } 41 | public UniMatch matching(A arg0, MatchesAny arg1, C arg2) { 42 | return () -> constructor.apply(arg0, null, arg2); 43 | } 44 | public UniMatch matching(MatchesAny arg0, B arg1, C arg2) { 45 | return () -> constructor.apply(null, arg1, arg2); 46 | } 47 | 48 | 49 | public BiMatch matching(BiMatch arg0, B arg1, C arg2) { 50 | return () -> constructor.apply(arg0.comparee(), arg1, arg2); 51 | } 52 | 53 | public BiMatch matching(A arg0, BiMatch arg1, C arg2) { 54 | return () -> constructor.apply(arg0, arg1.comparee(), arg2); 55 | } 56 | 57 | public BiMatch matching(A arg0, B arg1, BiMatch arg2) { 58 | return () -> constructor.apply(arg0, arg1, arg2.comparee()); 59 | } 60 | 61 | public BiMatch matching(A arg0, MatchesAny arg1, MatchesAny arg2) { 62 | return () -> constructor.apply(arg0, null, null); 63 | } 64 | public BiMatch matching(MatchesAny arg0, B arg1, MatchesAny arg2) { 65 | return () -> constructor.apply(null, arg1, null); 66 | } 67 | public BiMatch matching(MatchesAny arg0, MatchesAny arg1, C arg2) { 68 | return () -> constructor.apply(null, null, arg2); 69 | } 70 | 71 | public BiMatch matching(MatchesAny arg0, B arg1, UniMatch arg2) { 72 | return () -> constructor.apply(null, arg1, arg2.comparee()); 73 | } 74 | public BiMatch matching(A arg0, MatchesAny arg1, UniMatch arg2) { 75 | return () -> constructor.apply(arg0, null, arg2.comparee()); 76 | } 77 | public BiMatch matching(MatchesAny arg0, UniMatch arg1, C arg2) { 78 | return () -> constructor.apply(null, arg1.comparee(), arg2); 79 | } 80 | public BiMatch matching(A arg0, UniMatch arg1, MatchesAny arg2) { 81 | return () -> constructor.apply(arg0, arg1.comparee(), null); 82 | } 83 | public BiMatch matching(UniMatch arg0, MatchesAny arg1, C arg2) { 84 | return () -> constructor.apply(arg0.comparee(), null, arg2); 85 | } 86 | public BiMatch matching(UniMatch arg0, B arg1, MatchesAny arg2) { 87 | return () -> constructor.apply(arg0.comparee(), arg1, null); 88 | } 89 | 90 | public TriMatch matching(MatchesAny _, BiMatch arg1, C arg2) { 91 | return () -> constructor.apply(null, arg1.comparee(), arg2); 92 | } 93 | 94 | public TriMatch matching(MatchesAny _, B arg1, BiMatch arg2) { 95 | return () -> constructor.apply(null, arg1, arg2.comparee()); 96 | } 97 | 98 | public TriMatch matching(BiMatch arg0, MatchesAny _, C arg2) { 99 | return () -> constructor.apply(arg0.comparee(), null, arg2); 100 | } 101 | 102 | public TriMatch matching(A arg0, MatchesAny _, BiMatch arg2) { 103 | return () -> constructor.apply(arg0, null, arg2.comparee()); 104 | } 105 | 106 | public TriMatch matching(A arg0, BiMatch arg1, MatchesAny _) { 107 | return () -> constructor.apply(arg0, arg1.comparee(), null); 108 | } 109 | 110 | public TriMatch matching(BiMatch arg0, B arg1, MatchesAny _) { 111 | return () -> constructor.apply(arg0.comparee(), arg1, null); 112 | } 113 | 114 | public TriMatch matching(TriMatch arg0, B arg1, C arg2) { 115 | return () -> constructor.apply(arg0.comparee(), arg1, arg2); 116 | } 117 | 118 | public TriMatch matching(A arg0, TriMatch arg1, C arg2) { 119 | return () -> constructor.apply(arg0, arg1.comparee(), arg2); 120 | } 121 | 122 | public TriMatch matching(A arg0, B arg1, TriMatch arg2) { 123 | return () -> constructor.apply(arg0, arg1, arg2.comparee()); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/constructor/UniConstructor.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass.constructor; 2 | 3 | import uk.co.benjiweber.expressions.caseclass.MatchesAny; 4 | import uk.co.benjiweber.expressions.caseclass.constructor.references.BiMatch; 5 | import uk.co.benjiweber.expressions.caseclass.constructor.references.NoMatch; 6 | import uk.co.benjiweber.expressions.caseclass.constructor.references.TriMatch; 7 | import uk.co.benjiweber.expressions.caseclass.constructor.references.UniMatch; 8 | 9 | import java.util.function.Function; 10 | 11 | public class UniConstructor { 12 | private Function constructor; 13 | 14 | public UniConstructor(Function ctor) { 15 | this.constructor = ctor; 16 | } 17 | 18 | public static UniConstructor a(Function ctor) { 19 | return new UniConstructor<>(ctor); 20 | } 21 | public static UniConstructor an(Function ctor) { 22 | return new UniConstructor<>(ctor); 23 | } 24 | 25 | 26 | public NoMatch $(A arg0) { 27 | return () -> constructor.apply(arg0); 28 | } 29 | public UniMatch $(MatchesAny _) { 30 | return () -> constructor.apply(null); 31 | } 32 | public UniMatch $(UniMatch arg0) { 33 | return () -> constructor.apply(arg0.comparee()); 34 | } 35 | public BiMatch $(BiMatch arg0) { 36 | return () -> constructor.apply(arg0.comparee()); 37 | } 38 | public TriMatch $(TriMatch arg0) { 39 | return () -> constructor.apply(arg0.comparee()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/constructor/references/BiMatch.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass.constructor.references; 2 | 3 | import uk.co.benjiweber.expressions.caseclass.constructor.ForComparing; 4 | 5 | public interface BiMatch extends ForComparing {} 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/constructor/references/NoMatch.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass.constructor.references; 2 | 3 | import uk.co.benjiweber.expressions.caseclass.constructor.ForComparing; 4 | 5 | public interface NoMatch extends ForComparing {} 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/constructor/references/TriMatch.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass.constructor.references; 2 | 3 | import uk.co.benjiweber.expressions.caseclass.constructor.ForComparing; 4 | 5 | public interface TriMatch extends ForComparing {} 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/caseclass/constructor/references/UniMatch.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass.constructor.references; 2 | 3 | import uk.co.benjiweber.expressions.caseclass.constructor.ForComparing; 4 | 5 | public interface UniMatch extends ForComparing {} 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/collections/EnhancedList.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.collections; 2 | 3 | import java.util.List; 4 | 5 | import static java.util.Arrays.asList; 6 | 7 | public interface EnhancedList extends ForwardingList, WithIndex { 8 | 9 | static EnhancedList enhancedList(T... values) { 10 | return enhance(asList(values)); 11 | } 12 | 13 | static EnhancedList enhance(List list) { 14 | return () -> list; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/collections/ForwardingList.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.collections; 2 | 3 | import java.util.*; 4 | import java.util.function.Consumer; 5 | import java.util.function.Predicate; 6 | import java.util.function.UnaryOperator; 7 | import java.util.stream.Stream; 8 | 9 | public interface ForwardingList { 10 | List delegate(); 11 | 12 | default int size() { 13 | return delegate().size(); 14 | } 15 | 16 | default Spliterator spliterator() { 17 | return delegate().spliterator(); 18 | } 19 | 20 | default ListIterator listIterator() { 21 | return delegate().listIterator(); 22 | } 23 | 24 | default List subList(int fromIndex, int toIndex) { 25 | return delegate().subList(fromIndex, toIndex); 26 | } 27 | 28 | default boolean removeAll(Collection c) { 29 | return delegate().removeAll(c); 30 | } 31 | 32 | default int lastIndexOf(Object o) { 33 | return delegate().lastIndexOf(o); 34 | } 35 | 36 | default T remove(int index) { 37 | return delegate().remove(index); 38 | } 39 | 40 | default Object[] toArray() { 41 | return delegate().toArray(); 42 | } 43 | 44 | default void replaceAll(UnaryOperator operator) { 45 | delegate().replaceAll(operator); 46 | } 47 | 48 | default T set(int index, T element) { 49 | return delegate().set(index, element); 50 | } 51 | 52 | default boolean add(T s) { 53 | return delegate().add(s); 54 | } 55 | 56 | default int indexOf(Object o) { 57 | return delegate().indexOf(o); 58 | } 59 | 60 | default Iterator iterator() { 61 | return delegate().iterator(); 62 | } 63 | 64 | default boolean containsAll(Collection c) { 65 | return delegate().containsAll(c); 66 | } 67 | 68 | default void clear() { 69 | delegate().clear(); 70 | } 71 | 72 | default boolean retainAll(Collection c) { 73 | return delegate().retainAll(c); 74 | } 75 | 76 | default boolean remove(Object o) { 77 | return delegate().remove(o); 78 | } 79 | 80 | default boolean removeIf(Predicate filter) { 81 | return delegate().removeIf(filter); 82 | } 83 | 84 | default ListIterator listIterator(int index) { 85 | return delegate().listIterator(index); 86 | } 87 | 88 | default void sort(Comparator c) { 89 | delegate().sort(c); 90 | } 91 | 92 | default boolean addAll(int index, Collection c) { 93 | return delegate().addAll(index, c); 94 | } 95 | 96 | default boolean isEmpty() { 97 | return delegate().isEmpty(); 98 | } 99 | 100 | default void forEach(Consumer action) { 101 | delegate().forEach(action); 102 | } 103 | 104 | default T[] toArray(T[] a) { 105 | return delegate().toArray(a); 106 | } 107 | 108 | default Stream parallelStream() { 109 | return delegate().parallelStream(); 110 | } 111 | 112 | default T get(int index) { 113 | return delegate().get(index); 114 | } 115 | 116 | default void add(int index, T element) { 117 | delegate().add(index, element); 118 | } 119 | 120 | default Stream stream() { 121 | return delegate().stream(); 122 | } 123 | 124 | default boolean contains(Object o) { 125 | return delegate().contains(o); 126 | } 127 | 128 | default boolean addAll(Collection c) { 129 | return delegate().addAll(c); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/collections/WithIndex.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.collections; 2 | 3 | import uk.co.benjiweber.expressions.tuples.Tuple; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import static uk.co.benjiweber.expressions.collections.EnhancedList.enhance; 9 | 10 | public interface WithIndex extends ForwardingList { 11 | public interface IndexedItemMapper { 12 | R map(T it, int index); 13 | } 14 | public interface IndexedItemConsumer { 15 | void accept(T it, int index); 16 | } 17 | 18 | default void withIndex(IndexedItemConsumer consumer) { 19 | for (int i = 0; i < delegate().size(); i++) { 20 | consumer.accept(delegate().get(i), i); 21 | } 22 | } 23 | 24 | default List mapWithIndex(IndexedItemMapper mapper) { 25 | List results = new ArrayList<>(); 26 | for (int i = 0; i < delegate().size(); i++) { 27 | results.add(mapper.map(delegate().get(i), i)); 28 | } 29 | return results; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/exceptions/Exceptions.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.exceptions; 2 | 3 | import uk.co.benjiweber.expressions.functions.ExceptionalFunction; 4 | import uk.co.benjiweber.expressions.functions.ExceptionalSupplier; 5 | import uk.co.benjiweber.expressions.functions.ExceptionalVoid; 6 | 7 | import java.lang.reflect.InvocationTargetException; 8 | import java.util.Optional; 9 | import java.util.function.Function; 10 | import java.util.function.Supplier; 11 | import java.util.stream.Stream; 12 | 13 | public class Exceptions { 14 | 15 | public static Function unchecked(ExceptionalFunction f) { 16 | return to(f, t->t ,e -> { throw new RuntimeException(e); }); 17 | } 18 | 19 | public static T unchecked(ExceptionalSupplier supplier) { 20 | try { 21 | return supplier.supply(); 22 | } catch (Error | RuntimeException rex) { 23 | throw rex; 24 | } catch (Exception e) { 25 | throw new RuntimeException(e); 26 | } 27 | } 28 | 29 | public static void unchecked(ExceptionalVoid method) { 30 | try { 31 | method.apply(); 32 | } catch (Error | RuntimeException rex) { 33 | throw rex; 34 | } catch (Exception e) { 35 | throw new RuntimeException(e); 36 | } 37 | } 38 | 39 | 40 | public interface Wrapper { 41 | T in(Function exceptionMapper) throws U; 42 | T in(Supplier exceptionSupplier) throws U; 43 | T in(Class exceptionClass) throws U; 44 | } 45 | 46 | public static Wrapper wrappingChecked(ExceptionalSupplier supplier) { 47 | return new Wrapper() { 48 | public T in(Function exceptionMapper) throws U { 49 | try { 50 | return supplier.supply(); 51 | } catch (RuntimeException | Error rex) { 52 | throw rex; 53 | } catch (Exception e) { 54 | throw exceptionMapper.apply(e); 55 | } 56 | } 57 | 58 | @Override 59 | public T in(Supplier exceptionSupplier) throws U { 60 | try { 61 | return supplier.supply(); 62 | } catch (RuntimeException | Error rex) { 63 | throw rex; 64 | } catch (Exception e) { 65 | throw exceptionSupplier.get(); 66 | } 67 | } 68 | 69 | public T in(Class exceptionClass) throws U { 70 | try { 71 | return supplier.supply(); 72 | } catch (RuntimeException | Error rex) { 73 | throw rex; 74 | } catch (Exception e) { 75 | throw constructAndWrapIfPossible(exceptionClass, e); 76 | } 77 | } 78 | }; 79 | } 80 | 81 | public static Wrapper wrappingAll(ExceptionalSupplier supplier) { 82 | return new Wrapper() { 83 | public T in(Function exceptionMapper) throws U { 84 | try { 85 | return supplier.supply(); 86 | } catch (Exception e) { 87 | throw exceptionMapper.apply(e); 88 | } 89 | } 90 | 91 | @Override 92 | public T in(Supplier exceptionSupplier) throws U { 93 | try { 94 | return supplier.supply(); 95 | } catch (Exception e) { 96 | throw exceptionSupplier.get(); 97 | } 98 | } 99 | 100 | public T in(Class exceptionClass) throws U { 101 | try { 102 | return supplier.supply(); 103 | } catch (Exception e) { 104 | throw constructAndWrapIfPossible(exceptionClass, e); 105 | } 106 | } 107 | }; 108 | } 109 | 110 | static class UnableToInstantiateSuppliedException extends RuntimeException { 111 | public UnableToInstantiateSuppliedException(Exception e) { 112 | super(e); 113 | } 114 | } 115 | 116 | private static U constructAndWrapIfPossible(Class exceptionClass, Exception e) { 117 | try { 118 | return exceptionClass.getConstructor(Exception.class).newInstance(e); 119 | } catch (Exception ex1) { 120 | try { 121 | return exceptionClass.getConstructor().newInstance(); 122 | } catch (Exception ex2) { 123 | throw new UnableToInstantiateSuppliedException(ex2); 124 | } 125 | } 126 | } 127 | 128 | public static Function> toOptional(ExceptionalFunction f) { 129 | return t -> { 130 | try { 131 | return Optional.ofNullable(f.apply(t)); 132 | } catch (Exception e) { 133 | return Optional.empty(); 134 | } 135 | }; 136 | } 137 | 138 | public static Supplier> toOptional(ExceptionalSupplier f) { 139 | return () -> { 140 | try { 141 | return Optional.ofNullable(f.supply()); 142 | } catch (Exception e) { 143 | return Optional.empty(); 144 | } 145 | }; 146 | } 147 | 148 | public static Function to(ExceptionalFunction f, Function successHandler, Function exceptionHandler) { 149 | return t -> { 150 | try { 151 | return successHandler.apply(f.apply(t)); 152 | } catch (Error e) { 153 | throw e; 154 | } catch (Exception e) { 155 | return exceptionHandler.apply((E) e); 156 | } 157 | }; 158 | } 159 | 160 | public static Function> stream(ExceptionalFunction f) { 161 | return to(f,Stream::of, e -> Stream.empty()); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/exceptions/OrElse.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.exceptions; 2 | 3 | import java.util.function.Function; 4 | import java.util.function.Supplier; 5 | 6 | public interface OrElse { 7 | default Function orElse(R value) { 8 | return orElse(() -> value); 9 | } 10 | Function orElse(Supplier supplier); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/exceptions/Result.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.exceptions; 2 | 3 | import uk.co.benjiweber.expressions.functions.ExceptionalFunction; 4 | import uk.co.benjiweber.expressions.functions.ExceptionalSupplier; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.function.Consumer; 9 | import java.util.function.Function; 10 | import java.util.function.Supplier; 11 | 12 | public interface Result { 13 | T unwrap(); 14 | boolean success(); 15 | Result map(Function f); 16 | Result mapExceptional(ExceptionalFunction f); 17 | Result map(Class cls, Function f); 18 | 19 | static class Success implements Result { 20 | private T value; 21 | 22 | public Success(T value) { 23 | this.value = value; 24 | } 25 | 26 | public T unwrap() { 27 | return value; 28 | } 29 | 30 | public boolean success() { 31 | return true; 32 | } 33 | 34 | public Result map(Function f) { 35 | return new Success(f.apply(value)); 36 | } 37 | 38 | public Result mapExceptional(ExceptionalFunction f) { 39 | try { 40 | return new Success(f.apply(value)); 41 | } catch (RuntimeException e) { 42 | throw e; 43 | } catch (Exception e) { 44 | return new Failure(e); 45 | } 46 | } 47 | 48 | public Result map(Class cls, Function f) { 49 | return new Failure<>(new IllegalStateException()); 50 | } 51 | } 52 | 53 | static class Failure implements Result { 54 | 55 | private Exception e; 56 | 57 | public Failure(Exception e) { 58 | this.e = e; 59 | } 60 | 61 | public Failure(Result t) { 62 | this.e = t instanceof Failure ? ((Failure)t).e : new IllegalStateException(); 63 | } 64 | 65 | public T unwrap() { 66 | throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e); 67 | } 68 | 69 | public boolean success() { 70 | return false; 71 | } 72 | 73 | public Result map(Function f) { 74 | return new Failure(e); 75 | } 76 | 77 | public Result mapExceptional(ExceptionalFunction f) { 78 | return new Failure(e); 79 | } 80 | 81 | public Result map(Class cls, Function f) { 82 | if (e.getClass().isAssignableFrom(cls)) return new Success(f.apply((E)e)); 83 | return new Failure(e); 84 | } 85 | } 86 | 87 | public static Supplier> wrapReturn(ExceptionalSupplier f) { 88 | return () -> { 89 | try { 90 | return new Success(f.supply()); 91 | } catch (Exception e) { 92 | return new Failure(e); 93 | } 94 | }; 95 | } 96 | 97 | public static Consumer> wrapConsumer(Consumer f) { 98 | Function f2 = t -> { 99 | f.accept(t); 100 | return t; 101 | }; 102 | return t -> { 103 | try { 104 | t.map(f2); 105 | } catch (Exception e) { 106 | 107 | } 108 | }; 109 | } 110 | 111 | public static Function> wrapReturn(ExceptionalFunction f) { 112 | return t -> { 113 | try { 114 | return new Success(f.apply(t)); 115 | } catch (Exception e) { 116 | return new Failure(e); 117 | } 118 | }; 119 | } 120 | 121 | public static Function,Result> wrap(Function f) { 122 | return t -> { 123 | try { 124 | return t.map(f); 125 | } catch (Exception e) { 126 | return new Failure(e); 127 | } 128 | }; 129 | } 130 | 131 | public static Function,Result> wrapExceptional(ExceptionalFunction f) { 132 | return t -> { 133 | try { 134 | return t.mapExceptional(f); 135 | } catch (Exception e) { 136 | return new Failure(e); 137 | } 138 | }; 139 | } 140 | 141 | public static class ResultMapper { 142 | private Function successMapper; 143 | private List exceptionHandlers = new ArrayList<>(); 144 | 145 | public ResultMapper(Function successMapper) { 146 | this.successMapper = successMapper; 147 | } 148 | 149 | public Function, Result> mapper() { 150 | return t -> { 151 | if (t instanceof Success) return t.map(successMapper); 152 | for (ExceptionHandler handler : this.exceptionHandlers) { 153 | Result r = t.map(handler.type, handler.function); 154 | if (r instanceof Success) return r; 155 | } 156 | return new Failure(t); 157 | }; 158 | } 159 | 160 | public ResultMapper on(Class eCls, Function f) { 161 | exceptionHandlers.add(new ExceptionHandler(eCls, f)); 162 | return this; 163 | } 164 | 165 | } 166 | 167 | static class ExceptionHandler { 168 | final Class type; 169 | final Function function; 170 | 171 | public ExceptionHandler(Class type, Function function) { 172 | this.type = type; 173 | this.function = function; 174 | } 175 | } 176 | 177 | public static ResultMapper onSuccess(Function f) { 178 | return new ResultMapper(f); 179 | } 180 | 181 | } 182 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/exceptions/WrapBuilder.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.exceptions; 2 | 3 | import java.util.function.Consumer; 4 | import java.util.function.Function; 5 | import java.util.function.Supplier; 6 | 7 | public interface WrapBuilder extends OrElse { 8 | 9 | default Function orElse(R value) { 10 | return wrapException(e -> value); 11 | } 12 | 13 | default Function orElse(Supplier supplier) { 14 | return wrapException(supplier); 15 | } 16 | 17 | default OrElse peek(Consumer peeker) { 18 | return supplier -> wrapException(e -> { 19 | peeker.accept(e); 20 | return supplier.get(); 21 | }); 22 | } 23 | 24 | default Function wrapException(Supplier supplier) { 25 | return wrapException(e -> supplier.get()); 26 | } 27 | Function wrapException(Function f); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalBiConsumer.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalBiConsumer { 4 | void accept(A a, B b) throws E; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalBiFunction.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalBiFunction { 4 | R apply(A a, B b) throws E; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalConsumer.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalConsumer { 4 | void accept(A a) throws E; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalFunction.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | import uk.co.benjiweber.expressions.exceptions.Exceptions; 4 | import uk.co.benjiweber.expressions.exceptions.Result; 5 | import uk.co.benjiweber.expressions.exceptions.WrapBuilder; 6 | 7 | import java.util.Objects; 8 | import java.util.Optional; 9 | import java.util.function.Function; 10 | import java.util.stream.Stream; 11 | 12 | public interface ExceptionalFunction { 13 | R apply(T a) throws E; 14 | 15 | public static ExceptionalFunction exceptional(ExceptionalFunction f) { 16 | return f; 17 | } 18 | 19 | default Function> optional() { 20 | return Exceptions.toOptional(this); 21 | } 22 | 23 | default Function> stream() { 24 | return wrapReturn(Stream::of).wrapException(Stream::empty); 25 | } 26 | 27 | default Function unchecked() { 28 | return Exceptions.unchecked(this); 29 | } 30 | 31 | default Function> resultOut() { 32 | return Result.wrapReturn(this); 33 | } 34 | 35 | default Function,Result> resultInOut() { 36 | return Result.wrapExceptional(this); 37 | } 38 | 39 | default WrapBuilder wrapReturn(Function resultWrapper) { 40 | return errorWrapper -> t -> { 41 | try { 42 | return resultWrapper.apply(this.apply(t)); 43 | } catch (Exception e) { 44 | return errorWrapper.apply((E)e); 45 | } 46 | }; 47 | } 48 | 49 | default ExceptionalFunction compose(ExceptionalFunction before) { 50 | return (V v) -> apply(before.apply(v)); 51 | } 52 | 53 | default ExceptionalFunction andThen(ExceptionalFunction after) { 54 | return (T t) -> after.apply(apply(t)); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalQuadConsumer.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalQuadConsumer { 4 | void accept(T t, U u, V v, W w) throws E; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalQuadFunction.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalQuadFunction { 4 | R apply(T t, U u, V v, W w) throws E; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalQuinConsumer.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalQuinConsumer { 4 | void accept(A a, B b, C c, D d, E e) throws EX; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalQuinFunction.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalQuinFunction { 4 | R apply(A a, B b, C c, D d, E e) throws EX; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalSexConsumer.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalSexConsumer { 4 | void accept(A a, B b, C c, D d, E e, F f) throws EX; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalSexFunction.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalSexFunction { 4 | R apply(A a, B b, C c, D d, E e, F f) throws EX; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalSupplier.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalSupplier { 4 | T supply() throws E; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalTriConsumer.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalTriConsumer { 4 | void accept(T t, U u, V v) throws E; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalTriFunction.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalTriFunction { 4 | R apply(A a, B b, C c) throws E; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/ExceptionalVoid.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface ExceptionalVoid { 4 | void apply() throws E; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/QuadConsumer.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface QuadConsumer { 4 | void accept(T t, U u, V v, W w); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/QuadFunction.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface QuadFunction { 4 | R apply(T t, U u, V v, W w); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/QuinConsumer.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface QuinConsumer { 4 | void accept(T t, U u, V v, W w, X x); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/QuinFunction.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface QuinFunction { 4 | R apply(A a, B b, C c, D d, E e); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/SexConsumer.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface SexConsumer { 4 | void accept(T t, U u, V v, W w, X x,Y y); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/SexFunction.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface SexFunction { 4 | R apply(A a, B b, C c, D d, E e, F f); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/TriConsumer.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface TriConsumer { 4 | void accept(T t, U u, V v); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/functions/TriFunction.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | public interface TriFunction { 4 | R apply(T t, U u, V v); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/properties/ExplicitName.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.properties; 2 | 3 | import java.util.function.Function; 4 | import java.util.function.Supplier; 5 | 6 | public class ExplicitName extends Property implements Named { 7 | 8 | private final String name; 9 | 10 | ExplicitName(Supplier getter, Function setter, String name) { 11 | super(getter, setter); 12 | this.name = name; 13 | } 14 | 15 | public String name() { 16 | return name; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/properties/GuessesName.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.properties; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.Optional; 5 | import java.util.function.Function; 6 | import java.util.function.Supplier; 7 | 8 | import static java.util.Arrays.asList; 9 | import static uk.co.benjiweber.expressions.exceptions.Exceptions.unchecked; 10 | 11 | public class GuessesName extends Property implements Named { 12 | 13 | private String declaringClassName; 14 | private int declaringLineNumber; 15 | 16 | GuessesName(Supplier getter, Function setter) { 17 | super(getter, setter); 18 | recordPosition(); 19 | } 20 | 21 | private void recordPosition() { 22 | int expectedDepth = 3; 23 | StackTraceElement stackTraceElement = new Throwable().fillInStackTrace().getStackTrace()[expectedDepth]; 24 | this.declaringClassName = stackTraceElement.getClassName(); 25 | this.declaringLineNumber = stackTraceElement.getLineNumber(); 26 | } 27 | 28 | private Object createInstanceOfDeclarer() { 29 | Class cls = unchecked(() -> Class.forName(declaringClassName)); 30 | return unchecked(() -> cls.newInstance()); 31 | } 32 | 33 | private String guessName() { 34 | Class cls = unchecked(() -> Class.forName(declaringClassName)); 35 | Object o = createInstanceOfDeclarer(); 36 | Optional field = asList(cls.getDeclaredFields()) 37 | .stream() 38 | .map(f -> { 39 | f.setAccessible(true); 40 | return f; 41 | }) 42 | .filter(f -> f.getType().isAssignableFrom(GuessesName.class)) 43 | .filter(f -> unchecked(() -> (GuessesName) f.get(o)).declaringLineNumber == this.declaringLineNumber) 44 | .findFirst(); 45 | return field.get().getName(); 46 | } 47 | 48 | public String name() { 49 | return guessName(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/properties/Named.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.properties; 2 | 3 | public interface Named { 4 | String name(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/properties/Property.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.properties; 2 | 3 | import java.util.function.Function; 4 | import java.util.function.Supplier; 5 | 6 | public class Property { 7 | private Supplier getter; 8 | private final Function setter; 9 | 10 | public Property(Supplier getter, Function setter) { 11 | this.getter = getter; 12 | this.setter = setter; 13 | } 14 | 15 | public interface PropertyBuilder { 16 | public Property set(Function setter); 17 | public Readonly readonly(); 18 | } 19 | 20 | public static Writeonly set(Function setter) { 21 | Property prop = new Property(() -> {throw new UnsupportedOperationException(); }, setter); 22 | return prop::set; 23 | } 24 | 25 | public static PropertyBuilder get(Supplier getter) { 26 | return new PropertyBuilder() { 27 | public Property set(Function setter) { 28 | return new Property(getter, setter); 29 | } 30 | 31 | public Readonly readonly() { 32 | Property prop = new Property(getter, Function.identity()); 33 | return prop::get; 34 | } 35 | }; 36 | } 37 | 38 | public T set(T value) { 39 | return setter.apply(value); 40 | } 41 | 42 | public T get() { 43 | return getter.get(); 44 | } 45 | 46 | public Named named() { 47 | return new GuessesName(this.getter, this.setter); 48 | } 49 | 50 | 51 | public Named named(String name) { 52 | return new ExplicitName(this.getter, this.setter, name); 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/properties/Readonly.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.properties; 2 | 3 | public interface Readonly { 4 | T get(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/properties/Writeonly.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.properties; 2 | 3 | public interface Writeonly { 4 | T set(T t); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/tuples/BiTuple.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.tuples; 2 | 3 | import uk.co.benjiweber.expressions.Value; 4 | import uk.co.benjiweber.expressions.functions.ExceptionalBiConsumer; 5 | import uk.co.benjiweber.expressions.functions.ExceptionalBiFunction; 6 | 7 | public interface BiTuple { 8 | A one(); 9 | B two(); 10 | static BiTuple of(A a, B b) { 11 | abstract class BiTupleValue extends Value> implements BiTuple {} 12 | return new BiTupleValue() { 13 | public A one() { return a; } 14 | public B two() { return b; } 15 | }.using(BiTuple::one, BiTuple::two); 16 | } 17 | 18 | default R map(ExceptionalBiFunction f) throws E { 19 | return f.apply(one(), two()); 20 | } 21 | 22 | default void consume(ExceptionalBiConsumer consumer) throws E { 23 | consumer.accept(one(), two()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/tuples/QuadTuple.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.tuples; 2 | 3 | import uk.co.benjiweber.expressions.Value; 4 | import uk.co.benjiweber.expressions.functions.ExceptionalQuadConsumer; 5 | import uk.co.benjiweber.expressions.functions.ExceptionalQuadFunction; 6 | 7 | public interface QuadTuple { 8 | A one(); 9 | B two(); 10 | C three(); 11 | D four(); 12 | static QuadTuple of(A a, B b, C c, D d) { 13 | abstract class QuadTupleValue extends Value> implements QuadTuple {} 14 | return new QuadTupleValue() { 15 | public A one() { return a; } 16 | public B two() { return b; } 17 | public C three() { return c; } 18 | public D four() { return d; } 19 | }.using(QuadTuple::one, QuadTuple::two, QuadTuple::three, QuadTuple::four); 20 | } 21 | 22 | 23 | default R map(ExceptionalQuadFunction f) throws E { 24 | return f.apply(one(), two(), three(), four()); 25 | } 26 | 27 | default void consume(ExceptionalQuadConsumer consumer) throws E { 28 | consumer.accept(one(), two(), three(), four()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/tuples/QuinTuple.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.tuples; 2 | 3 | import uk.co.benjiweber.expressions.Value; 4 | import uk.co.benjiweber.expressions.functions.ExceptionalQuinConsumer; 5 | import uk.co.benjiweber.expressions.functions.ExceptionalQuinFunction; 6 | 7 | public interface QuinTuple { 8 | A one(); 9 | B two(); 10 | C three(); 11 | D four(); 12 | E five(); 13 | static QuinTuple of(A a, B b, C c, D d, E e) { 14 | abstract class QuinTupleValue extends Value> implements QuinTuple {} 15 | return new QuinTupleValue() { 16 | public A one() { return a; } 17 | public B two() { return b; } 18 | public C three() { return c; } 19 | public D four() { return d; } 20 | public E five() { return e; } 21 | }.using(QuinTuple::one, QuinTuple::two, QuinTuple::three, QuinTuple::four, QuinTuple::five); 22 | } 23 | 24 | 25 | default R map(ExceptionalQuinFunction f) throws EX { 26 | return f.apply(one(), two(), three(), four(), five()); 27 | } 28 | 29 | default void consume(ExceptionalQuinConsumer consumer) throws EX { 30 | consumer.accept(one(), two(), three(), four(), five()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/tuples/SexTuple.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.tuples; 2 | 3 | import uk.co.benjiweber.expressions.Value; 4 | import uk.co.benjiweber.expressions.functions.ExceptionalSexConsumer; 5 | import uk.co.benjiweber.expressions.functions.ExceptionalSexFunction; 6 | 7 | public interface SexTuple { 8 | A one(); 9 | B two(); 10 | C three(); 11 | D four(); 12 | E five(); 13 | F six(); 14 | static SexTuple of(A a, B b, C c, D d, E e, F f) { 15 | abstract class SexTupleValue extends Value> implements SexTuple {} 16 | return new SexTupleValue() { 17 | public A one() { return a; } 18 | public B two() { return b; } 19 | public C three() { return c; } 20 | public D four() { return d; } 21 | public E five() { return e; } 22 | public F six() { return f; } 23 | }.using(SexTuple::one, SexTuple::two, SexTuple::three, SexTuple::four, SexTuple::five, SexTuple::six); 24 | } 25 | 26 | 27 | default R map(ExceptionalSexFunction f) throws EX { 28 | return f.apply(one(), two(), three(), four(), five(), six()); 29 | } 30 | 31 | default void consume(ExceptionalSexConsumer consumer) throws EX { 32 | consumer.accept(one(), two(), three(), four(), five(), six()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/tuples/TriTuple.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.tuples; 2 | 3 | import uk.co.benjiweber.expressions.Value; 4 | import uk.co.benjiweber.expressions.functions.ExceptionalTriConsumer; 5 | import uk.co.benjiweber.expressions.functions.ExceptionalTriFunction; 6 | 7 | public interface TriTuple { 8 | A one(); 9 | B two(); 10 | C three(); 11 | static TriTuple of(A a, B b, C c) { 12 | abstract class TriTupleValue extends Value> implements TriTuple {} 13 | return new TriTupleValue() { 14 | public A one() { return a; } 15 | public B two() { return b; } 16 | public C three() { return c; } 17 | }.using(TriTuple::one, TriTuple::two, TriTuple::three); 18 | } 19 | 20 | default R map(ExceptionalTriFunction f) throws E { 21 | return f.apply(one(),two(),three()); 22 | 23 | } 24 | 25 | default void consume(ExceptionalTriConsumer consumer) throws E { 26 | consumer.accept(one(), two(), three()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/tuples/Tuple.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.tuples; 2 | 3 | public interface Tuple { 4 | static UniTuple tuple(A a) { 5 | return UniTuple.of(a); 6 | } 7 | 8 | static BiTuple tuple(A a, B b) { 9 | return BiTuple.of(a, b); 10 | } 11 | 12 | static TriTuple tuple(A a, B b, C c) { 13 | return TriTuple.of(a, b, c); 14 | } 15 | 16 | static QuadTuple tuple(A a, B b, C c, D d) { 17 | return QuadTuple.of(a, b, c, d); 18 | } 19 | 20 | static QuinTuple tuple(A a, B b, C c, D d, E e) { 21 | return QuinTuple.of(a, b, c, d, e); 22 | } 23 | 24 | static SexTuple tuple(A a, B b, C c, D d, E e, F f) { 25 | return SexTuple.of(a, b, c, d, e, f); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/uk/co/benjiweber/expressions/tuples/UniTuple.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.tuples; 2 | 3 | import uk.co.benjiweber.expressions.Value; 4 | import uk.co.benjiweber.expressions.functions.ExceptionalConsumer; 5 | import uk.co.benjiweber.expressions.functions.ExceptionalFunction; 6 | 7 | import java.util.function.Function; 8 | 9 | public interface UniTuple { 10 | A one(); 11 | static UniTuple of(A a) { 12 | abstract class UniTupleValue extends Value> implements UniTuple {} 13 | return new UniTupleValue() { 14 | public A one() { return a; } 15 | }.using(UniTuple::one); 16 | } 17 | 18 | default R map(ExceptionalFunction f) throws E { 19 | return f.apply(one()); 20 | } 21 | 22 | default UniTuple flatmap(Function f) { 23 | return of(f.apply(one())); 24 | } 25 | 26 | default void consume(ExceptionalConsumer consumer) throws E{ 27 | consumer.accept(one()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/ChainableVoidTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertTrue; 8 | import static uk.co.benjiweber.expressions.ChainableVoid.chain; 9 | 10 | public class ChainableVoidTest { 11 | 12 | @Test 13 | public void chaining_void_example() { 14 | Duck duck = chain(new Duck()) 15 | .invoke(Duck::quack) 16 | .invoke(Duck::waddle) 17 | .invoke(Duck::setName, "ducky") 18 | .invoke(Duck::setFooBar, "curry_and_partially_apply", 5) 19 | .unwrap(); 20 | 21 | assertTrue(duck.quackCalled); 22 | assertTrue(duck.waddleCalled); 23 | assertEquals("ducky", duck.name); 24 | assertEquals("curry_and_partially_apply", duck.foo); 25 | assertEquals(Integer.valueOf(5), duck.bar); 26 | 27 | } 28 | 29 | static class Duck { 30 | private boolean quackCalled = false; 31 | private boolean waddleCalled = false; 32 | public void quack() { 33 | quackCalled = true; 34 | System.out.println("quack"); 35 | } 36 | public void waddle() { 37 | waddleCalled = true; 38 | System.out.println("waddle"); 39 | } 40 | 41 | String name; 42 | public void setName(String name) { 43 | this.name = name; 44 | } 45 | 46 | String foo; 47 | Integer bar; 48 | public void setFooBar(String foo, Integer bar) { 49 | this.foo = foo; 50 | this.bar = bar; 51 | } 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/CoalesceTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.Optional; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | import static org.junit.Assert.fail; 9 | import static uk.co.benjiweber.expressions.Coalesce.coalesce; 10 | 11 | public class CoalesceTest { 12 | 13 | @Test 14 | public void should_return_first_non_null_value() { 15 | Person nullName = new Person(null); 16 | Person bob = new Person("bob"); 17 | Person barbara = new Person("barbara"); 18 | assertEquals("bob", coalesce(nullName::name, bob::name, barbara::name)); 19 | } 20 | 21 | @Test 22 | public void should_be_lazy() { 23 | Person bob = new Person("bob"); 24 | Person angryPerson = new Person("angry") { 25 | @Override public String name() { 26 | fail("Should not have asked for the angry person's name"); 27 | return "angry"; 28 | } 29 | }; 30 | 31 | assertEquals("bob", coalesce(bob::name, angryPerson::name)); 32 | } 33 | 34 | @Test 35 | public void should_be_able_to_use_lambdas() { 36 | assertEquals("bob", coalesce(() -> new Person("bob").name(), () -> new Person("barbara").name())); 37 | } 38 | 39 | @Test 40 | public void should_be_able_to_use_optionals() { 41 | assertEquals("bob", 42 | coalesce( 43 | () -> Optional.empty(), 44 | () -> Optional.of(new Person("bob").name()), 45 | () -> Optional.of(new Person("barbara").name()) 46 | ).get() 47 | ); 48 | } 49 | 50 | 51 | static class Person { 52 | private String name; 53 | 54 | public Person(String name) { 55 | this.name = name; 56 | } 57 | 58 | public String name() { 59 | return name; 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/CurryTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | import static uk.co.benjiweber.expressions.Curry.*; 7 | public class CurryTest { 8 | 9 | @Test 10 | public void curry_bi() { 11 | assertEquals(new Integer(3), curry(CurryTest::add).apply(2).apply(1)); 12 | } 13 | 14 | @Test 15 | public void curry_tri() { 16 | assertEquals(new Integer(6), curry(CurryTest::addThree).apply(3).apply(2).apply(1)); 17 | } 18 | 19 | @Test 20 | public void curry_quad() { 21 | assertEquals("alphabetagammadelta", curry(CurryTest::concat).apply("alpha").apply("beta").apply("gamma").apply("delta")); 22 | } 23 | 24 | 25 | public static int add(int a, int b) { 26 | return a + b; 27 | } 28 | 29 | public static int addThree(int a, int b, int c) { 30 | return a + b + c; 31 | } 32 | 33 | public static String concat(String a, String b, String c, String d) { 34 | return a + b + c + d; 35 | } 36 | 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/InstanceOfTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.Optional; 6 | 7 | import static org.junit.Assert.assertFalse; 8 | import static org.junit.Assert.assertTrue; 9 | import static uk.co.benjiweber.expressions.InstanceOf.when; 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class InstanceOfTest { 13 | @Test 14 | public void should_return_value_when_input_object_is_instance() { 15 | Object foo = "ffoo"; 16 | 17 | String result = when(foo).instanceOf(String.class) 18 | .then(s -> s.substring(1)) 19 | .otherwise("incorrect"); 20 | 21 | assertEquals("foo", result); 22 | } 23 | 24 | @Test 25 | public void should_return_alternative_when_input_object_is_not_instance() { 26 | Object foo = 5; 27 | 28 | String result = when(foo).instanceOf(String.class) 29 | .then(s -> s.substring(1)) 30 | .otherwise("expected"); 31 | 32 | assertEquals("expected", result); 33 | } 34 | 35 | @Test 36 | public void should_return_optional_value_when_input_object_is_instance() { 37 | Object foo = "ffoo"; 38 | 39 | Optional result = when(foo).instanceOf(String.class) 40 | .then(s -> s.substring(1)) 41 | .optional(); 42 | 43 | assertEquals("foo", result.get()); 44 | } 45 | 46 | @Test 47 | public void should_return_empty_optional_when_input_object_is_not_instance() { 48 | Object foo = 5; 49 | 50 | Optional result = when(foo).instanceOf(String.class) 51 | .then(s -> s.substring(1)) 52 | .optional(); 53 | 54 | assertFalse(result.isPresent()); 55 | } 56 | 57 | @Test(expected = NullPointerException.class) 58 | public void should_throw_exceptions() { 59 | Object foo = "hello"; 60 | 61 | Optional result = when(foo).instanceOf(String.class) 62 | .then(s -> { 63 | if (true) throw new NullPointerException(); 64 | return "nope"; 65 | }) 66 | .optional(); 67 | } 68 | 69 | @Test 70 | public void should_not_throw_exception_if_block_does_not_need_evaluating() { 71 | Object foo = 5; 72 | 73 | Optional result = when(foo).instanceOf(String.class) 74 | .then(s -> { 75 | if(true) throw new NullPointerException(); 76 | return "nope"; 77 | }) 78 | .optional(); 79 | 80 | assertFalse(result.isPresent()); 81 | } 82 | 83 | @Test(expected = RuntimeException.class) 84 | public void should_wrap_checked_exceptions() { 85 | Object foo = "hello"; 86 | 87 | Optional result = when(foo).instanceOf(String.class) 88 | .then(s -> { 89 | if(true) throw new Exception(); 90 | return "nope"; 91 | }) 92 | .optional(); 93 | } 94 | 95 | static class Duck { 96 | boolean quacked = false; 97 | Void quack() { 98 | quacked = true; 99 | return null; 100 | } 101 | } 102 | 103 | @Test 104 | public void should_return_value_when_input_object_is_instance_duck() { 105 | 106 | Object foo = new Duck(); 107 | 108 | when(foo).instanceOf(Duck.class) 109 | .then(duck -> duck.quack()) 110 | .otherwise(null); 111 | 112 | assertTrue(((Duck)foo).quacked); 113 | 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/NullSafeTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.Optional; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | import static org.junit.Assert.assertFalse; 9 | import static uk.co.benjiweber.expressions.NullSafe.nullSafe; 10 | 11 | public class NullSafeTest { 12 | 13 | Foo foo = new Foo(); 14 | 15 | @Test public void should_return_value_of_expression_passed_in() { 16 | Optional result = nullSafe(() -> foo.foo().bar().baz()); 17 | assertEquals("baz", result.get()); 18 | } 19 | 20 | @Test public void should_return_optional_empty_when_expression_evaluates_to_null() { 21 | Optional result = nullSafe(() -> foo.foo().bar().aNull()); 22 | assertFalse(result.isPresent()); 23 | } 24 | 25 | @Test public void should_return_optional_empty_when_null_pointer_exception() { 26 | Optional result = nullSafe(() -> foo.foo().npe().baz()); 27 | assertFalse(result.isPresent()); 28 | } 29 | 30 | static class Foo { 31 | Bar foo() { 32 | return new Bar(); 33 | } 34 | } 35 | 36 | static class Bar { 37 | Baz bar() { 38 | return new Baz(); 39 | } 40 | Baz npe() { 41 | return null; 42 | } 43 | } 44 | 45 | static class Baz { 46 | String baz() { 47 | return "baz"; 48 | } 49 | String aNull() { 50 | return null; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/OptionalFromExceptionalTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import org.junit.Test; 4 | import uk.co.benjiweber.expressions.exceptions.Exceptions; 5 | 6 | import java.util.Optional; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | import static org.junit.Assert.assertFalse; 10 | 11 | public class OptionalFromExceptionalTest { 12 | 13 | @Test 14 | public void should_be_empty_when_exception_thrown() { 15 | Optional foo = Exceptions.toOptional(this::foo).get(); 16 | assertFalse(foo.isPresent()); 17 | } 18 | 19 | @Test 20 | public void should_have_correct_value_when_value_returned() { 21 | Optional bar = Exceptions.toOptional(this::bar).get(); 22 | assertEquals("bar", bar.get()); 23 | } 24 | 25 | @Test 26 | public void should_have_correct_value_when_value_returned_function() { 27 | Optional foo = Exceptions.toOptional(this::oneParam).apply(false); 28 | assertEquals("foo", foo.get()); 29 | } 30 | 31 | @Test 32 | public void should_have_correct_value_when_function_throws() { 33 | Optional foo = Exceptions.toOptional(this::oneParam).apply(true); 34 | assertFalse(foo.isPresent()); 35 | } 36 | 37 | private String foo() throws FooNotFoundException { 38 | throw new FooNotFoundException(); 39 | } 40 | 41 | private String bar() throws FooNotFoundException { 42 | return "bar"; 43 | } 44 | 45 | private String oneParam(boolean throwRequested) throws FooNotFoundException { 46 | return throwRequested ? thenThrow(new FooNotFoundException(),"") : "foo"; 47 | } 48 | 49 | static class FooNotFoundException extends Exception {} 50 | 51 | static T thenThrow(E e, T t) throws E { 52 | throw e; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/Paint.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import java.util.List; 4 | import java.util.function.Function; 5 | 6 | import static java.util.Arrays.asList; 7 | import static java.util.Arrays.toString; 8 | 9 | interface Paint { 10 | int red(); 11 | int green(); 12 | int blue(); 13 | default Paint mix(Paint other) { 14 | return create(red() + other.red(), green() + other.green(), blue() + other.blue()); 15 | } 16 | 17 | static Paint create(int red, int green, int blue) { 18 | abstract class PaintValue extends Value implements Paint {} 19 | return new PaintValue() { 20 | public int red() { return red; } 21 | public int green() { return green; } 22 | public int blue() { return blue; } 23 | }.using(Paint::red, Paint::green, Paint::blue); 24 | } 25 | } -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/PropertyTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import org.junit.Test; 4 | import uk.co.benjiweber.expressions.properties.Named; 5 | import uk.co.benjiweber.expressions.properties.Property; 6 | import uk.co.benjiweber.expressions.properties.Readonly; 7 | import uk.co.benjiweber.expressions.properties.Writeonly; 8 | 9 | import java.util.function.Consumer; 10 | import java.util.function.Supplier; 11 | 12 | import static org.junit.Assert.assertEquals; 13 | import static org.junit.Assert.assertNotEquals; 14 | import static org.junit.Assert.assertNull; 15 | import static uk.co.benjiweber.expressions.properties.Property.get; 16 | import static uk.co.benjiweber.expressions.properties.Property.set; 17 | 18 | public class PropertyTest { 19 | 20 | static class Person { 21 | private String name; 22 | public final Property Name = get(() -> name).set(value -> name = value); 23 | public final Readonly ReadOnlyName = get(() -> name).readonly(); 24 | public final Writeonly WriteOnlyname = set(value -> name = value); 25 | } 26 | 27 | @Test 28 | public void mixingPaint() { 29 | Paint red = Paint.create(100,0,0); 30 | Paint green = Paint.create(0,100,0); 31 | 32 | Paint mixed = red.mix(green); 33 | 34 | assertEquals(100, mixed.red()); 35 | assertEquals(100, mixed.green()); 36 | } 37 | 38 | @Test 39 | public void paintEquals() { 40 | Paint red = Paint.create(100,0,0); 41 | Paint green = Paint.create(0,100,0); 42 | 43 | Paint mixed1 = red.mix(green); 44 | Paint mixed2 = green.mix(red); 45 | 46 | assertEquals(mixed1, mixed2); 47 | assertNotEquals(red, green); 48 | assertNotEquals(red, mixed1); 49 | } 50 | 51 | @Test 52 | public void paintToString() { 53 | Paint red = Paint.create(100,0,0); 54 | Paint green = Paint.create(0,100,0); 55 | 56 | assertEquals("{100, 0, 0}", red.toString()); 57 | assertEquals("{0, 100, 0}", green.toString()); 58 | } 59 | 60 | 61 | @Test 62 | public void property() { 63 | Person person = new Person(); 64 | assertNull(person.Name.get()); 65 | assertNull(person.name); 66 | 67 | person.Name.set("Bob"); 68 | assertEquals("Bob", person.Name.get()); 69 | assertEquals("Bob", person.name); 70 | 71 | person.Name.set("Bill"); 72 | assertEquals("Bill", person.Name.get()); 73 | assertEquals("Bill", person.name); 74 | assertEquals("Bill", person.ReadOnlyName.get()); 75 | 76 | person.WriteOnlyname.set("Alice"); 77 | assertEquals("Alice", person.Name.get()); 78 | } 79 | 80 | @Test 81 | public void pass_around_references() { 82 | Person person = new Person(); 83 | person.Name.set("Bob"); 84 | 85 | takesAProperty(person.Name, "Bill"); 86 | assertEquals("Bill", person.Name.get()); 87 | 88 | takesASetter(person.Name::set, "Matt"); 89 | assertEquals("Matt", person.Name.get()); 90 | 91 | String got = takesAGetter(person.Name::get); 92 | assertEquals("Matt", got); 93 | } 94 | 95 | @Test 96 | public void property_with_behaviour() { 97 | TimePeriod period = new TimePeriod(); 98 | period.Hours.set(2D); 99 | assertEquals(7200D, period.seconds, 0); 100 | assertEquals(2D, period.Hours.get(), 0); 101 | 102 | period.Hours.set(3D); 103 | assertEquals(10800D, period.seconds, 0); 104 | assertEquals(3D, period.Hours.get(), 0); 105 | } 106 | 107 | @Test 108 | public void pass_around_references_to_hours() { 109 | TimePeriod period = new TimePeriod(); 110 | period.Hours.set(2D); 111 | 112 | takesAProperty(period.Hours, 3D); 113 | assertEquals(3D, period.Hours.get(), 0); 114 | 115 | takesASetter(period.Hours::set, 2D); 116 | assertEquals(2D, period.Hours.get(), 0); 117 | 118 | Double got = takesAGetter(period.Hours::get); 119 | assertEquals(2D, got, 0); 120 | } 121 | 122 | 123 | 124 | static class TimePeriod { 125 | private double seconds; 126 | 127 | public final Property Hours = get(() -> seconds / 3600).set(value -> seconds = value * 3600); 128 | } 129 | 130 | public static class ExplicitPropertyNames { 131 | private String foo = "foo"; 132 | public final Named Foo = get(() -> foo).set(value -> foo = value).named("Foo"); 133 | public final Named Bar = get(() -> foo).set(value -> foo = value).named("Bar"); 134 | } 135 | 136 | @Test 137 | public void explicit_named_property() { 138 | ExplicitPropertyNames o = new ExplicitPropertyNames(); 139 | assertEquals("Foo", o.Foo.name()); 140 | assertEquals("Bar", o.Bar.name()); 141 | } 142 | 143 | public static class GuessablePropertyNames { 144 | private String foo = "foo"; 145 | public final Named Foo = get(() -> foo).set(value -> foo = value).named(); 146 | public final Named Bar = get(() -> foo).set(value -> foo = value).named(); 147 | } 148 | 149 | @Test 150 | public void guessable_named_property() { 151 | GuessablePropertyNames o = new GuessablePropertyNames(); 152 | assertEquals("Foo", o.Foo.name()); 153 | assertEquals("Bar", o.Bar.name()); 154 | } 155 | 156 | private void takesAProperty(Property property, T newValue) { 157 | property.set(newValue); 158 | } 159 | 160 | private void takesASetter(Consumer setter, T newValue) { 161 | setter.accept(newValue); 162 | } 163 | 164 | private T takesAGetter(Supplier getter) { 165 | return getter.get(); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/TimesTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.List; 6 | 7 | import static java.util.Arrays.asList; 8 | import static org.junit.Assert.assertEquals; 9 | import static uk.co.benjiweber.expressions.Times.times; 10 | 11 | public class TimesTest { 12 | 13 | @Test 14 | public void times_collection_size() { 15 | List aCollection = asList("one", "two", "three"); 16 | 17 | times(aCollection.size()).invoke(this::foo); 18 | 19 | assertEquals(3, numberOfTimesFooWasInvoked); 20 | } 21 | 22 | @Test 23 | public void times_literal() { 24 | times(6).invoke(this::bar); 25 | 26 | assertEquals(6, numberOfTimesBarWasInvoked); 27 | } 28 | 29 | 30 | int numberOfTimesFooWasInvoked = 0; 31 | private void foo() { 32 | numberOfTimesFooWasInvoked++; 33 | } 34 | 35 | int numberOfTimesBarWasInvoked = 0; 36 | private void bar() { 37 | numberOfTimesBarWasInvoked++; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/TryAsExpressionTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.fail; 6 | import static org.junit.Assert.assertTrue; 7 | import static org.junit.Assert.assertEquals; 8 | import static uk.co.benjiweber.expressions.Try.Try; 9 | 10 | public class TryAsExpressionTest { 11 | 12 | static class SuperException extends Exception {} 13 | static class SubException extends SuperException {} 14 | 15 | @Test public void should_return_try_value() { 16 | String result = Try(() -> { 17 | return "try"; 18 | }).Catch(NullPointerException.class, e -> { 19 | return "catch"; 20 | }).apply(); 21 | 22 | assertEquals("try", result); 23 | } 24 | 25 | @Test public void should_return_try_value_when_number() { 26 | Number result = Try(() -> { 27 | return 1; 28 | }).Catch(NullPointerException.class, e -> { 29 | return 2; 30 | }).apply(); 31 | 32 | assertEquals(1, result); 33 | } 34 | 35 | 36 | @Test public void should_return_catch_value() { 37 | String result = Try(() -> { 38 | if (true) throw new NullPointerException(); 39 | return "try"; 40 | }).Catch(NullPointerException.class, e -> { 41 | return "catch"; 42 | }).apply(); 43 | 44 | assertEquals("catch", result); 45 | } 46 | 47 | 48 | @Test public void should_catch_subclass() throws SubException { 49 | String result = Try(() -> { 50 | if (true) throw new SubException(); 51 | return "try"; 52 | }).Catch(SuperException.class, e -> { 53 | return "catch"; 54 | }).apply(); 55 | 56 | assertEquals("catch", result); 57 | } 58 | 59 | @Test public void should_catch_in_order_specified() throws SubException { 60 | String result = Try(() -> { 61 | if (true) throw new SubException(); 62 | return "try"; 63 | }).Catch(SubException.class, e -> { 64 | return "firstcatch"; 65 | }).Catch(SuperException.class, e -> { 66 | return "secondcatch"; 67 | }).apply(); 68 | 69 | assertEquals("firstcatch", result); 70 | } 71 | 72 | @Test public void should_wrap_but_not_catch_uncaught_checked_exception() { 73 | try { 74 | Try(() -> { 75 | if (true) throw new SubException(); 76 | return "try"; 77 | }).apply(); 78 | fail("Should have thrown"); 79 | } catch (RuntimeException e) { 80 | assertEquals(SubException.class, e.getCause().getClass()); 81 | } 82 | } 83 | 84 | @Test public void should_not_wrap_or_catch_uncaught_runtime_exception() { 85 | try { 86 | Try(() -> { 87 | if (true) throw new NullPointerException(); 88 | return "try"; 89 | }).Catch(SubException.class, e -> { 90 | return "firstcatch"; 91 | }).Catch(SuperException.class, e -> { 92 | return "secondcatch"; 93 | }).apply(); 94 | 95 | fail("should have thrown npe"); 96 | } catch (NullPointerException e) { 97 | 98 | } 99 | } 100 | 101 | @Test public void should_not_wrap_or_catch_uncaught_error() { 102 | try { 103 | Try(() -> { 104 | if (true) throw new Error(); 105 | return "try"; 106 | }).Catch(SubException.class, e -> { 107 | return "firstcatch"; 108 | }).Catch(SuperException.class, e -> { 109 | return "secondcatch"; 110 | }).apply(); 111 | 112 | fail("should have thrown error"); 113 | } catch (Error e) { 114 | 115 | } 116 | } 117 | 118 | @Test public void should_execute_finally_when_returning_normally() { 119 | boolean[] finallyCalled = new boolean[1]; 120 | String result = Try(() -> { 121 | return "try"; 122 | }).Catch(SuperException.class, e -> { 123 | return "catch"; 124 | }).Finally(() -> { 125 | finallyCalled[0] = true; 126 | }).apply(); 127 | 128 | assertTrue(finallyCalled[0]); 129 | } 130 | 131 | @Test public void should_execute_finally_when_catching_exception() throws SuperException { 132 | boolean[] finallyCalled = new boolean[1]; 133 | String result = Try(() -> { 134 | if(true) throw new SuperException(); 135 | return "try"; 136 | }).Catch(SuperException.class, e -> { 137 | return "catch"; 138 | }).Finally(() -> { 139 | finallyCalled[0] = true; 140 | }).apply(); 141 | 142 | assertTrue(finallyCalled[0]); 143 | } 144 | 145 | @Test public void should_execute_finally_when_uncaught_exception() { 146 | boolean[] finallyCalled = new boolean[1]; 147 | try { 148 | String result = Try(() -> { 149 | if(true) throw new NullPointerException(); 150 | return "try"; 151 | }).Catch(SuperException.class, e -> { 152 | return "catch"; 153 | }).Finally(() -> { 154 | finallyCalled[0] = true; 155 | }).apply(); 156 | 157 | fail(); 158 | } catch (RuntimeException e) { 159 | 160 | } 161 | assertTrue(finallyCalled[0]); 162 | } 163 | 164 | @Test public void should_execute_finally_when_uncaught_error() { 165 | boolean[] finallyCalled = new boolean[1]; 166 | try { 167 | String result = Try(() -> { 168 | if(true) throw new Error(); 169 | return "try"; 170 | }).Catch(SuperException.class, e -> { 171 | return "catch"; 172 | }).Finally(() -> { 173 | finallyCalled[0] = true; 174 | }).apply(); 175 | 176 | fail(); 177 | } catch (Error e) { 178 | 179 | } 180 | assertTrue(finallyCalled[0]); 181 | } 182 | 183 | 184 | @Test(expected=NullPointerException.class) public void should_propagate_exception_in_finally() { 185 | String result = Try(() -> { 186 | if(true) throw new SuperException(); 187 | return "try"; 188 | }).Catch(SuperException.class, e -> { 189 | return "catch"; 190 | }).Finally(() -> { 191 | throw new NullPointerException(); 192 | }).apply(); 193 | } 194 | 195 | @Test(expected=Error.class) public void should_propagate_error_in_finally() { 196 | String result = Try(() -> { 197 | if(true) throw new SuperException(); 198 | return "try"; 199 | }).Catch(SuperException.class, e -> { 200 | return "catch"; 201 | }).Finally(() -> { 202 | throw new Error(); 203 | }).apply(); 204 | } 205 | 206 | @Test(expected=RuntimeException.class) public void should_wrap_and_propagate_checked_exception_in_finally() { 207 | String result = Try(() -> { 208 | if(true) throw new SuperException(); 209 | return "try"; 210 | }).Catch(SuperException.class, e -> { 211 | return "catch"; 212 | }).Finally(() -> { 213 | throw new Exception(); 214 | }).apply(); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/UsingTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | 7 | import static uk.co.benjiweber.expressions.Using.using; 8 | 9 | public class UsingTest { 10 | 11 | @Test public void should_return_value() { 12 | String result = using(Foo::new, foo -> { 13 | return foo.result(); 14 | }); 15 | } 16 | 17 | @Test public void should_close_closeable() { 18 | Foo closeable = using(Foo::new, foo -> { 19 | return foo; 20 | }); 21 | 22 | Assert.assertTrue(closeable.closed); 23 | } 24 | 25 | @Test public void should_close_closeable_when_exception() { 26 | try { 27 | using(this::getClosable, foo -> { 28 | if (true) throw new NullPointerException(); 29 | return ""; 30 | }); 31 | } catch (NullPointerException e) { 32 | // Expected 33 | } 34 | 35 | Assert.assertTrue(foo.closed); 36 | } 37 | 38 | Foo foo = new Foo(); 39 | Foo getClosable() { 40 | return foo; 41 | } 42 | 43 | static class Foo implements AutoCloseable { 44 | 45 | public String result() { 46 | return "result"; 47 | } 48 | 49 | boolean closed = false; 50 | public void close() throws Exception { 51 | closed = true; 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/caseclass/CaseConstructorTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import org.junit.Test; 4 | import uk.co.benjiweber.expressions.EqualsHashcode; 5 | import uk.co.benjiweber.expressions.ToString; 6 | import uk.co.benjiweber.expressions.Value; 7 | import uk.co.benjiweber.expressions.caseclass.Case; 8 | import uk.co.benjiweber.expressions.caseclass.MatchesAny; 9 | 10 | import java.util.List; 11 | import java.util.function.Function; 12 | 13 | import static java.util.Arrays.asList; 14 | import static org.junit.Assert.assertEquals; 15 | import static uk.co.benjiweber.expressions.caseclass.CaseConstructorTest.Person.person; 16 | import static uk.co.benjiweber.expressions.caseclass.MatchesAny._; 17 | 18 | public class CaseConstructorTest { 19 | 20 | @Test 21 | public void constructor_matching_example() { 22 | Person so = person("Some", "One"); 23 | 24 | String someone = so.match() 25 | .when(person("Ann", "Other"), p -> "another") 26 | .when(person("Some", "One"), p -> "someone") 27 | ._("Unknown Person"); 28 | 29 | assertEquals("someone", someone); 30 | } 31 | 32 | @Test 33 | public void constructor_matching_any() { 34 | Person so = person("Some", "One"); 35 | Person an = person("Ann", "Other"); 36 | 37 | String someone = so.match() 38 | .when(person("Some", _), p -> "someone") 39 | .when(person(_, "Other"), p -> "another") 40 | ._("Unknown Person"); 41 | 42 | assertEquals("someone", someone); 43 | 44 | String another = an.match() 45 | .when(person("Some", _), p -> "someone") 46 | .when(person(_, "Other"), p -> "another") 47 | ._("Unknown Person"); 48 | 49 | assertEquals("another", another); 50 | 51 | } 52 | 53 | interface Person extends Case { 54 | String firstname(); 55 | String lastname(); 56 | 57 | static Person person(String firstname, String lastname) { 58 | return person(firstname, lastname, Person::firstname, Person::lastname); 59 | } 60 | static Person person(String firstname, MatchesAny lastname) { 61 | return person(firstname, null, Person::firstname); 62 | } 63 | static Person person(MatchesAny firstname, String lastname) { 64 | return person(null, lastname, Person::lastname); 65 | } 66 | static Person person(String firstname, String lastname, Function... props) { 67 | abstract class PersonValue extends Value implements Person {} 68 | return new PersonValue() { 69 | public String firstname() { return firstname; } 70 | public String lastname() { return lastname; } 71 | }.using(props); 72 | } 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/caseclass/CaseListTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import org.junit.Test; 4 | import uk.co.benjiweber.expressions.EqualsHashcode; 5 | import uk.co.benjiweber.expressions.ToString; 6 | import uk.co.benjiweber.expressions.Value; 7 | import uk.co.benjiweber.expressions.caseclass.Case2; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.function.Function; 12 | 13 | import static org.junit.Assert.assertEquals; 14 | import static uk.co.benjiweber.expressions.caseclass.CaseListTest.EmptyList.empty; 15 | import static uk.co.benjiweber.expressions.caseclass.CaseListTest.Tail.list; 16 | import static uk.co.benjiweber.expressions.caseclass.Case3.erasesTo; 17 | 18 | public class CaseListTest { 19 | 20 | @Test 21 | public void list_size_example() { 22 | List list = list("one", list("two", list("three", empty()))); 23 | 24 | assertEquals(3, size(list)); 25 | } 26 | 27 | private int size(List list) { 28 | return list.match() 29 | .when(erasesTo(Tail.class), n -> 1 + size(n.tail())) 30 | .when(erasesTo(EmptyList.class), n -> 0); 31 | } 32 | 33 | @Test 34 | public void list_double_item_example() { 35 | List list = list("one", list("two", list("three", empty()))); 36 | List expected = list("oneone", list("twotwo", list("threethree", empty()))); 37 | 38 | assertEquals(expected, longer(list)); 39 | } 40 | 41 | private List longer(List list) { 42 | return list.match() 43 | .when(erasesTo(Tail.class), n -> list(n.head() + n.head(), longer(n.tail()))) 44 | .when(erasesTo(EmptyList.class), n -> empty()); 45 | } 46 | 47 | 48 | interface List extends Case2,EmptyList> {} 49 | 50 | interface Tail extends List { 51 | T head(); 52 | List tail(); 53 | static List list(T head, List tail) { 54 | abstract class TailValue extends Value implements Tail {} 55 | return new TailValue() { 56 | public T head() { return head;} 57 | public List tail() { return tail; } 58 | }.using(Tail::head, Tail::tail); 59 | } 60 | 61 | default java.util.List, ?>> props() { 62 | return Arrays.asList(Tail::head, Tail::tail); 63 | } 64 | } 65 | interface EmptyList extends List { 66 | static EmptyList empty = new EmptyList(){ 67 | @Override public boolean equals(Object other) { return other == this; } 68 | @Override public String toString() { return ""; } 69 | }; 70 | static List empty() { return (EmptyList)empty;}; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/caseclass/CaseParseArgumentsTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import org.junit.Test; 4 | import uk.co.benjiweber.expressions.EqualsHashcode; 5 | import uk.co.benjiweber.expressions.Value; 6 | import uk.co.benjiweber.expressions.caseclass.Case; 7 | import uk.co.benjiweber.expressions.caseclass.MatchesAny; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.function.Consumer; 12 | import java.util.function.Function; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | import static org.junit.Assert.assertTrue; 16 | import static uk.co.benjiweber.expressions.caseclass.CaseParseArgumentsTest.Argument.arg; 17 | import static uk.co.benjiweber.expressions.caseclass.MatchesAny._; 18 | 19 | public class CaseParseArgumentsTest { 20 | 21 | @Test 22 | public void parse_arguments_example() { 23 | applyArgument(arg("--help", "foo")); 24 | assertEquals("foo", this.helpRequested); 25 | 26 | applyArgument(arg("--lang", "English")); 27 | assertEquals("English", this.language); 28 | 29 | applyArgument(arg("--nonsense","this does not exist")); 30 | assertTrue(badArg); 31 | } 32 | 33 | private void applyArgument(Argument input) { 34 | input.match() 35 | .when(arg("--help", _), arg -> this.printHelp(arg.value())) 36 | .when(arg("--lang", _), arg -> this.setLanguage(arg.value())) 37 | ._(arg -> printUsageAndExit()); 38 | } 39 | 40 | interface ArgumentHandler { 41 | void apply(); 42 | } 43 | 44 | private String helpRequested; 45 | private CaseParseArgumentsTest printHelp(String command) { 46 | this.helpRequested = command; 47 | return this; 48 | } 49 | 50 | private String language; 51 | private CaseParseArgumentsTest setLanguage(String language) { 52 | this.language = language; 53 | return this; 54 | } 55 | 56 | private boolean badArg; 57 | private CaseParseArgumentsTest printUsageAndExit() { 58 | this.badArg = true; 59 | return this; 60 | } 61 | 62 | interface Argument extends Case { 63 | String flag(); 64 | String value(); 65 | 66 | static Argument arg(String flag, MatchesAny value) { 67 | return arg(flag, null, Argument::flag); 68 | } 69 | static Argument arg(String flag, String value) { 70 | return arg(flag, value, Argument::flag, Argument::value); 71 | } 72 | static Argument arg(String flag, String value, Function... props) { 73 | abstract class ArgValue extends Value implements Argument {} 74 | return new ArgValue() { 75 | public String flag() { return flag; } 76 | public String value() { return value; } 77 | }.using(props); 78 | } 79 | } 80 | 81 | 82 | 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/caseclass/CaseSomeNoneTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import org.junit.Test; 4 | import uk.co.benjiweber.expressions.caseclass.Case; 5 | import uk.co.benjiweber.expressions.caseclass.Case2; 6 | import uk.co.benjiweber.expressions.caseclass.Case3; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | import static uk.co.benjiweber.expressions.caseclass.CaseSomeNoneTest.None.none; 10 | import static uk.co.benjiweber.expressions.caseclass.CaseSomeNoneTest.Some.some; 11 | import static uk.co.benjiweber.expressions.caseclass.Case3.erasesTo; 12 | 13 | public class CaseSomeNoneTest { 14 | interface Option extends Case2, None>{ } 15 | interface Some extends Option { 16 | T value(); 17 | static Some some(T value) { 18 | return () -> value; 19 | } 20 | } 21 | interface None extends Option { 22 | static final None none = new None(){} ; 23 | static None none() { return (None)none;} 24 | } 25 | 26 | @Test 27 | public void some_none_match_example() { 28 | Option exists = some("hello"); 29 | Option missing = none(); 30 | 31 | assertEquals("hello", describe(exists)); 32 | 33 | assertEquals("missing", describe(missing)); 34 | } 35 | 36 | private String describe(Option option) { 37 | return option.match() 38 | .when(erasesTo(Some.class), some -> some.value()) 39 | .when(erasesTo(None.class), none -> "missing"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/caseclass/CaseTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import org.junit.Test; 4 | import uk.co.benjiweber.expressions.caseclass.Case3; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | public class CaseTest { 9 | 10 | @Test 11 | public void case_example() { 12 | Shape cube = Cube.create(4f); 13 | Shape circle = Circle.create(6f); 14 | Shape rectangle = Rectangle.create(1f, 2f); 15 | 16 | assertEquals("Cube with size 4.0", description(cube)); 17 | assertEquals("Circle with radius 6.0", description(circle)); 18 | assertEquals("Rectangle 1.0x2.0", description(rectangle)); 19 | } 20 | 21 | public String description(Shape shape) { 22 | return shape.match() 23 | .when(Rectangle.class, rect -> "Rectangle " + rect.width() + "x" + rect.length()) 24 | .when(Circle.class, circle -> "Circle with radius " + circle.radius()) 25 | .when(Cube.class, cube -> "Cube with size " + cube.size()); 26 | } 27 | 28 | interface Shape extends Case3 { } 29 | 30 | interface Rectangle extends Shape { 31 | float length(); 32 | float width(); 33 | static Rectangle create(float width, float length) { 34 | return new Rectangle() { 35 | public float length() { return length; } 36 | public float width() { return width; } 37 | }; 38 | } 39 | } 40 | 41 | interface Circle extends Shape { 42 | float radius(); 43 | static Circle create(float radius) { 44 | return () -> radius; 45 | } 46 | } 47 | 48 | interface Cube extends Shape { 49 | float size(); 50 | static Cube create(float size) { 51 | return () -> size; 52 | } 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/caseclass/CaseTreeTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import org.junit.Test; 4 | import uk.co.benjiweber.expressions.caseclass.Case2; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | import static uk.co.benjiweber.expressions.caseclass.CaseTreeTest.Leaf.leaf; 8 | import static uk.co.benjiweber.expressions.caseclass.CaseTreeTest.Node.node; 9 | 10 | public class CaseTreeTest { 11 | 12 | @Test 13 | public void counting_nodes_example() { 14 | Tree tree = 15 | node( 16 | 5, 17 | node(1, leaf, leaf), 18 | node( 19 | 3, 20 | leaf, 21 | node(4, leaf, leaf) 22 | ) 23 | ); 24 | 25 | assertEquals(4, countNodes(tree)); 26 | } 27 | 28 | int countNodes(Tree tree) { 29 | return tree.match() 30 | .when(Leaf.class, n -> 0) 31 | .when(Node.class, n -> 1 + countNodes(n.left()) + countNodes(n.right())); 32 | } 33 | 34 | interface Tree extends Case2 {} 35 | interface Leaf extends Tree { 36 | static Leaf leaf = new Leaf() {}; 37 | } 38 | interface Node extends Tree { 39 | Tree left(); 40 | Tree right(); 41 | T value(); 42 | static Node node(T value, Tree left, Tree right) { 43 | return new Node() { 44 | public T value() { return value; } 45 | public Tree left() { return left; } 46 | public Tree right() { return right; } 47 | }; 48 | } 49 | } 50 | } 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/caseclass/DecompositionConstructorReferenceTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import org.junit.Test; 4 | import uk.co.benjiweber.expressions.Value; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | import static uk.co.benjiweber.expressions.caseclass.DecompositionConstructorReferenceTest.Person.person; 8 | import static uk.co.benjiweber.expressions.caseclass.MatchesAny._; 9 | 10 | public class DecompositionConstructorReferenceTest { 11 | @Test 12 | public void decomposition_variable_items_example() { 13 | Person a = person("Bob", "Smith", 18); 14 | Person b = person("Bill", "Smith", 28); 15 | Person c = person("Old", "Person", 90); 16 | 17 | assertEquals("first_Smith_18", matchExample(a)); 18 | assertEquals("second_28", matchExample(b)); 19 | assertEquals("unknown", matchExample(c)); 20 | } 21 | 22 | String matchExample(Person person) { 23 | return person.match() 24 | .when(Person::person, "Bob", _, _).then( (surname, age) -> "first_" + surname + "_" + age ) 25 | .when(Person::person,"Bill", "Smith",_).then( age -> "second_" + age ) 26 | ._("unknown"); 27 | } 28 | 29 | 30 | public interface Person extends Case { 31 | String firstname(); 32 | String lastname(); 33 | Integer age(); 34 | 35 | static Person person(String firstname, String lastname, Integer age) { 36 | abstract class PersonValue extends Value implements Person {} 37 | return new PersonValue() { 38 | public String firstname() { return firstname; } 39 | public String lastname() { return lastname; } 40 | public Integer age() { return age; } 41 | }.using(Person::firstname, Person::lastname, Person::age); 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/caseclass/DecompositionTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import org.junit.Test; 4 | import uk.co.benjiweber.expressions.EqualsHashcode; 5 | import uk.co.benjiweber.expressions.ToString; 6 | import uk.co.benjiweber.expressions.Value; 7 | 8 | import java.util.List; 9 | import java.util.function.Function; 10 | 11 | import static java.util.Arrays.asList; 12 | import static org.junit.Assert.assertEquals; 13 | import static uk.co.benjiweber.expressions.caseclass.DecompositionTest.Person.person; 14 | import static uk.co.benjiweber.expressions.caseclass.MatchesAny._; 15 | 16 | public class DecompositionTest { 17 | @Test 18 | public void decomposition_variable_items_example() { 19 | Person a = person("Bob", "Smith", 18); 20 | Person b = person("Bill", "Smith", 28); 21 | Person c = person("Old", "Person", 90); 22 | 23 | assertEquals("first_Smith_18", matchExample(a)); 24 | assertEquals("second_28", matchExample(b)); 25 | assertEquals("unknown", matchExample(c)); 26 | } 27 | 28 | String matchExample(Person person) { 29 | return person.match() 30 | .when(person("Bob", _, _), (surname, age) -> "first_" + surname + "_" + age) 31 | .when(person("Bill", "Smith", _), age -> "second_" + age) 32 | ._("unknown"); 33 | } 34 | 35 | 36 | interface Person extends Case { 37 | String firstname(); 38 | String lastname(); 39 | Integer age(); 40 | 41 | static Person person(String firstname, String lastname, Integer age, Function... props) { 42 | abstract class PersonValue extends Value implements Person {} 43 | return new PersonValue() { 44 | public String firstname() { return firstname; } 45 | public String lastname() { return lastname; } 46 | public Integer age() { return age; } 47 | }.using(props); 48 | } 49 | 50 | static Person person(String firstname, String lastname, Integer age) { 51 | return person(firstname, lastname, age, Person::firstname, Person::lastname, Person::age); 52 | } 53 | static OneMissing person(String firstname, MatchesAny lastname, Integer age) { 54 | return person(firstname, null, age, Person::firstname, Person::age) 55 | .missing(Person::lastname); 56 | } 57 | static OneMissing person(MatchesAny firstname, String lastname, Integer age) { 58 | return person(null, lastname, age, Person::lastname, Person::age) 59 | .missing(Person::firstname); 60 | } 61 | static OneMissing person(String firstName, String lastname, MatchesAny age) { 62 | return person(firstName, lastname, null, Person::firstname, Person::lastname) 63 | .missing(Person::age); 64 | } 65 | static TwoMissing person(MatchesAny firstname, MatchesAny lastname, Integer age) { 66 | return person(null, null, age, Person::age) 67 | .missing(Person::firstname, Person::lastname); 68 | } 69 | static TwoMissing person(String firstname, MatchesAny lastname, MatchesAny age) { 70 | return person(firstname, null, null, Person::firstname) 71 | .missing(Person::lastname, Person::age); 72 | } 73 | static TwoMissing person(MatchesAny firstname, String lastname, MatchesAny age) { 74 | return person(null, lastname, null, Person::lastname) 75 | .missing(Person::firstname, Person::age); 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/caseclass/NestedDecompositionTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import org.junit.Test; 4 | import uk.co.benjiweber.expressions.Value; 5 | 6 | import java.util.Optional; 7 | 8 | import static junit.framework.TestCase.assertEquals; 9 | import static org.junit.Assert.assertFalse; 10 | import static uk.co.benjiweber.expressions.caseclass.MatchesAny._; 11 | import static uk.co.benjiweber.expressions.caseclass.NestedDecompositionTest.Address.address; 12 | import static uk.co.benjiweber.expressions.caseclass.NestedDecompositionTest.Customer.customer; 13 | import static uk.co.benjiweber.expressions.caseclass.NestedDecompositionTest.FirstLine.firstLine; 14 | import static uk.co.benjiweber.expressions.caseclass.constructor.TriConstructor.*; 15 | import static uk.co.benjiweber.expressions.caseclass.constructor.BiConstructor.*; 16 | 17 | public class NestedDecompositionTest { 18 | 19 | @Test 20 | public void nested_decomposition_using_constructor_references() { 21 | Customer a = customer("Benji", "Weber", address(firstLine(123, "Some Road"), "AB123CD")); 22 | String result = a.match().when( 23 | a(Customer::customer).matching( 24 | "Benji", 25 | "Weber", 26 | an(Address::address).matching(_, "AB123CD") 27 | ) 28 | ).then(firstLine -> firstLine.roadName()) 29 | .otherwise("unknown"); 30 | 31 | assertEquals("Some Road", result); 32 | } 33 | 34 | @Test 35 | public void nested_decomposition_using_constructor_references_no_match() { 36 | Customer a = customer("Benji", "Weber", address(firstLine(123, "Some Road"), "AB123CD")); 37 | String result = a.match().when( 38 | a(Customer::customer).matching( 39 | "Someone", 40 | "Else", 41 | an(Address::address).matching(_, "AB123CD") 42 | ) 43 | ).then(firstLine -> firstLine.roadName()) 44 | .otherwise("unknown"); 45 | 46 | assertEquals("unknown", result); 47 | } 48 | 49 | @Test 50 | public void nested_decomposition_using_constructor_references_deeper() { 51 | Customer a = customer("Benji", "Weber", address(firstLine(123, "Some Road"), "AB123CD")); 52 | String result = a.match().when( 53 | a(Customer::customer).matching( 54 | "Benji", 55 | "Weber", 56 | an(Address::address).matching( 57 | a(FirstLine::firstLine).matching(_, _), 58 | _ 59 | ) 60 | ) 61 | ).then((houseNo, road, postCode) -> houseNo + " " + road) 62 | .otherwise("unknown"); 63 | 64 | assertEquals("123 Some Road", result); 65 | } 66 | 67 | @Test 68 | public void nested_decomposition_using_constructor_references_optional() { 69 | Customer a = customer("Benji", "Weber", address(firstLine(123, "Some Road"), "AB123CD")); 70 | Optional result = a.match().when( 71 | a(Customer::customer).matching( 72 | "Benji", 73 | "Weber", 74 | an(Address::address).matching( 75 | a(FirstLine::firstLine).matching(_, _), 76 | _ 77 | ) 78 | ) 79 | ).then((houseNo, road, postCode) -> houseNo + " " + road) 80 | .toOptional(); 81 | 82 | assertEquals("123 Some Road", result.get()); 83 | } 84 | 85 | @Test 86 | public void nested_decomposition_using_constructor_references_optional_unknown() { 87 | Customer a = customer("Benji", "Weber", address(firstLine(123, "Some Road"), "AB123CD")); 88 | Optional result = a.match().when( 89 | a(Customer::customer).matching( 90 | "Someone", 91 | "Weber", 92 | an(Address::address).matching( 93 | a(FirstLine::firstLine).matching(_,_), 94 | _ 95 | ) 96 | ) 97 | ).then((houseNo, road, postCode) -> houseNo + " " + road) 98 | .toOptional(); 99 | 100 | assertFalse(result.isPresent()); 101 | } 102 | 103 | interface Customer extends Case { 104 | String firstName(); 105 | String lastName(); 106 | Address address(); 107 | public static Customer customer(String firstName, String lastName, Address address) { 108 | abstract class CustomerValue extends Value implements Customer {} 109 | return new CustomerValue() { 110 | public String firstName() { return firstName; } 111 | public String lastName() { return lastName; } 112 | public Address address() { return address; } 113 | }.using(Customer::firstName, Customer::lastName, Customer::address); 114 | } 115 | } 116 | 117 | interface Address extends Case
{ 118 | FirstLine firstLine(); 119 | String postCode(); 120 | public static Address address(FirstLine firstLine, String postCode) { 121 | abstract class AddressValue extends Value
implements Address {} 122 | return new AddressValue() { 123 | public FirstLine firstLine() { return firstLine; } 124 | public String postCode() { return postCode; } 125 | }.using(Address::firstLine, Address::postCode); 126 | } 127 | } 128 | 129 | interface FirstLine extends Case { 130 | Integer houseNumber(); 131 | String roadName(); 132 | public static FirstLine firstLine(Integer houseNumber, String roadName) { 133 | abstract class FirstLineValue extends Value implements FirstLine{} 134 | return new FirstLineValue() { 135 | public Integer houseNumber() { return houseNumber; } 136 | public String roadName() { return roadName; } 137 | }.using(FirstLine::houseNumber, FirstLine::roadName); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/caseclass/UnreadableDecompositionTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.caseclass; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | import static uk.co.benjiweber.expressions.caseclass.DecompositionConstructorReferenceTest.Person; 7 | import static uk.co.benjiweber.expressions.caseclass.DecompositionConstructorReferenceTest.Person.person; 8 | import static uk.co.benjiweber.expressions.caseclass.MatchesAny._; 9 | 10 | public class UnreadableDecompositionTest { 11 | 12 | 13 | @Test 14 | public void one_missings() { 15 | assertEquals("foo", person("Bob", "asdf", 18).match().when(Person::person, _, "asdf", 18).then(a -> "foo")._("unknown")); 16 | assertEquals("unknown", person("Bob", "fdsa", 18).match().when(Person::person, _, "asdf", 18).then(a -> "foo")._("unknown")); 17 | 18 | assertEquals("foo", person("Bob", "sfasdf", 18).match().when(Person::person, "Bob", _, 18).then(a -> "foo")._("unknown")); 19 | assertEquals("unknown", person("Bob", "", 18).match().when(Person::person, "Bill", _, 18).then(a -> "foo")._("unknown")); 20 | 21 | assertEquals("foo", person("Bob", "sfasdf", 18).match().when(Person::person, "Bob", "sfasdf", _).then(a -> "foo")._("unknown")); 22 | assertEquals("unknown", person("Bob", "", 18).match().when(Person::person, "Bob", "sfasdf", _).then(a -> "foo")._("unknown")); 23 | 24 | 25 | assertEquals("foo", person("Bob", "asdf", 18).match().when(Person::person, _, "aaaaaaa", 18).then(age -> "aaaaaaa").when(Person::person, _,"asdf", 18).then(a -> "foo")._("unknown")); 26 | assertEquals("unknown", person("Bob", "fdsa", 18).match().when(Person::person, _, "aaaaaaa", 18).then(age -> "aaaaaaa").when(Person::person, _,"asdf", 18).then(a -> "foo")._("unknown")); 27 | 28 | assertEquals("foo", person("Bob", "asdf", 18).match().when(Person::person, _, "aaaaaaa", 18).then(age -> "aaaaaaa").when(Person::person, "Bob",_, 18).then(a -> "foo")._("unknown")); 29 | assertEquals("unknown", person("Bob", "fdsa", 18).match().when(Person::person, _, "aaaaaaa", 18).then(age -> "aaaaaaa").when(Person::person, "Bill",_, 18).then(a -> "foo")._("unknown")); 30 | 31 | assertEquals("foo", person("Bob", "asdf", 18).match().when(Person::person, _, "aaaaaaa", 18).then(age -> "aaaaaaa").when(Person::person, "Bob","asdf", _).then(a -> "foo")._("unknown")); 32 | assertEquals("unknown", person("Bob", "fdsa", 18).match().when(Person::person, _, "aaaaaaa", 18).then(age -> "aaaaaaa").when(Person::person, "Bob","asdf", _).then(a -> "foo")._("unknown")); 33 | } 34 | 35 | @Test 36 | public void all_missing() { 37 | assertEquals("foo", person("Bob","asdf",18).match().when(Person::person, _,_,_).then((firstname,lastname,age) -> "foo")._("")); 38 | } 39 | 40 | @Test 41 | public void none_missing() { 42 | assertEquals("foo", person("Bob","asdf",18).match().when(Person::person, "Bob", "asdf", 18).then(person -> "foo")._("")); 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/collections/IndexedListTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.collections; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import static java.util.Arrays.asList; 9 | import static org.junit.Assert.*; 10 | import static uk.co.benjiweber.expressions.collections.EnhancedList.enhancedList; 11 | 12 | public class IndexedListTest { 13 | @Test 14 | public void example_mapping_with_index() { 15 | List result = enhancedList("foo", "bar") 16 | .mapWithIndex((it, i) -> it + "_" + i); 17 | 18 | assertEquals(asList("foo_0","bar_1"), result); 19 | } 20 | 21 | @Test 22 | public void example_consuming_with_index() { 23 | List result = new ArrayList<>(); 24 | enhancedList("foo", "bar") 25 | .withIndex((it, i) -> result.add(it + "_" + i)); 26 | 27 | assertEquals(asList("foo_0","bar_1"), result); 28 | } 29 | } -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/exceptions/Example.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.exceptions; 2 | 3 | public class Example { 4 | 5 | public enum Do { THROW(true), NOT_THROW(false); 6 | public final boolean value; 7 | 8 | Do(boolean value) { 9 | this.value = value; 10 | } 11 | } 12 | public static String methodThatThrowsCheckedException(Do throwPlease) throws ExceptionsTest.ACheckedExceptionIDontHaveAGoodWayToDealWith { 13 | if (throwPlease.value) throw new ExceptionsTest.ACheckedExceptionIDontHaveAGoodWayToDealWith(); 14 | return "foo"; 15 | } 16 | 17 | public static void voidMethodThatThrowsACheckedException(Do throwPlease) throws ExceptionsTest.ACheckedExceptionIDontHaveAGoodWayToDealWith { 18 | if (throwPlease.value) throw new ExceptionsTest.ACheckedExceptionIDontHaveAGoodWayToDealWith(); 19 | } 20 | 21 | public static void methodThatThrowsCheckedExceptionNoParams() throws ExceptionsTest.ACheckedExceptionIDontHaveAGoodWayToDealWith { 22 | 23 | } 24 | 25 | public static String methodThatThrowsNPE(Do throwPlease) { 26 | if(throwPlease.value) throw new NullPointerException(); 27 | return "not_an_npe"; 28 | } 29 | 30 | public static String throwingSupplier() throws ExceptionsTest.InputTooLongException { 31 | throw new ExceptionsTest.InputTooLongException(); 32 | } 33 | 34 | public static String notThrowingSupplier() throws ExceptionsTest.InputTooLongException { 35 | return "hello"; 36 | } 37 | 38 | public static String methodThatThrowsRuntimeException(Do throwPlease) { 39 | if(throwPlease.value) throw new NullPointerException(); 40 | return "not_an_exception"; 41 | } 42 | 43 | public static String duplicatesShortStrings(String input) throws ExceptionsTest.InputTooLongException { 44 | if (input.length() > 3) throw new ExceptionsTest.InputTooLongException(); 45 | return input + input; 46 | } 47 | 48 | public static String uppercasesStrings(String input) throws ExceptionsTest.InputContainsUppercaseException { 49 | if (input.matches(".*?[A-Z].*")) throw new ExceptionsTest.InputContainsUppercaseException(); 50 | return input.toUpperCase(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/exceptions/ExceptionsTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.exceptions; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.List; 6 | import java.util.concurrent.CompletableFuture; 7 | import java.util.function.Function; 8 | 9 | import static java.util.Arrays.asList; 10 | import static java.util.stream.Collectors.toList; 11 | import static org.junit.Assert.assertEquals; 12 | import static org.junit.Assert.assertTrue; 13 | import static uk.co.benjiweber.expressions.exceptions.Exceptions.unchecked; 14 | import static uk.co.benjiweber.expressions.exceptions.Exceptions.wrappingAll; 15 | import static uk.co.benjiweber.expressions.exceptions.Exceptions.wrappingChecked; 16 | import static uk.co.benjiweber.expressions.exceptions.Example.Do; 17 | 18 | public class ExceptionsTest { 19 | @Test 20 | public void checked_to_unchecked() { 21 | String foo = unchecked(() -> Example.methodThatThrowsCheckedException(Do.NOT_THROW)); 22 | assertEquals("foo", foo); 23 | } 24 | 25 | @Test 26 | public void checked_to_unchecked_method_reference() { 27 | unchecked(Example::methodThatThrowsCheckedExceptionNoParams); 28 | } 29 | 30 | @Test 31 | public void checked_to_unchecked_void() { 32 | unchecked(() -> Example.voidMethodThatThrowsACheckedException(Example.Do.NOT_THROW)); 33 | } 34 | 35 | @Test(expected = RuntimeException.class) 36 | public void checked_to_unchecked_should_wrap_in_runtime_exception() { 37 | String foo = unchecked(() -> Example.methodThatThrowsCheckedException(Do.THROW)); 38 | } 39 | 40 | @Test(expected = RuntimeException.class) 41 | public void checked_to_unchecked_void_should_wrap_in_runtime_exception() { 42 | unchecked(() -> Example.voidMethodThatThrowsACheckedException(Do.THROW)); 43 | } 44 | 45 | @Test(expected = Wrapped.class) 46 | public void wrapping_checked() throws Wrapped { 47 | wrappingChecked(() -> Example.methodThatThrowsCheckedException(Do.THROW)).in(Wrapped::new); 48 | } 49 | 50 | @Test(expected = NullPointerException.class) 51 | public void wrapping_checked_should_not_wrap_npe() throws Wrapped { 52 | wrappingChecked(() -> Example.methodThatThrowsNPE(Do.THROW)).in(Wrapped::new); 53 | } 54 | 55 | 56 | @Test 57 | public void wrapping_checked_should_return_value_when_no_exception() throws Wrapped { 58 | String foo = wrappingChecked(() -> Example.methodThatThrowsCheckedException(Example.Do.NOT_THROW)).in(Wrapped::new); 59 | assertEquals("foo", foo); 60 | } 61 | 62 | @Test 63 | public void wrapping_all_should_return_value_when_no_exception() throws Wrapped { 64 | String foo = wrappingAll(() -> Example.methodThatThrowsCheckedException(Do.NOT_THROW)).in(Wrapped::new); 65 | assertEquals("foo", foo); 66 | } 67 | 68 | @Test(expected = RuntimeException.class) 69 | public void wrapping_checked_should_not_wrap_runtime_exception() throws Wrapped { 70 | wrappingChecked(() -> Example.methodThatThrowsRuntimeException(Do.THROW)).in(Wrapped::new); 71 | } 72 | 73 | @Test(expected = Wrapped.class) 74 | public void wrapping_all_should_wrap_npe() throws Wrapped { 75 | wrappingAll(() -> Example.methodThatThrowsNPE(Do.THROW)).in(Wrapped::new); 76 | } 77 | 78 | @Test(expected = Wrapped.class) 79 | public void wrapping_all_should_wrap_runtime_exception() throws Wrapped { 80 | wrappingAll(() -> Example.methodThatThrowsRuntimeException(Do.THROW)).in(Wrapped::new); 81 | } 82 | 83 | @Test(expected = Wrapped.class) 84 | public void wrapping_checked_using_supplier() throws Wrapped { 85 | wrappingChecked(() -> Example.methodThatThrowsCheckedException(Do.THROW)).in(() -> new Wrapped(null)); 86 | } 87 | 88 | @Test(expected = Wrapped.class) 89 | public void wrapping_checked_using_reflection() throws Wrapped { 90 | wrappingChecked(() -> Example.methodThatThrowsCheckedException(Do.THROW)).in(Wrapped.class); 91 | } 92 | 93 | @Test(expected = Wrapped.class) 94 | public void wrapping_all_using_supplier() throws Wrapped { 95 | wrappingAll(() -> Example.methodThatThrowsCheckedException(Do.THROW)).in(() -> new Wrapped(null)); 96 | } 97 | 98 | @Test(expected = Wrapped.class) 99 | public void wrapping_all_using_reflection() throws Wrapped { 100 | wrappingAll(() -> Example.methodThatThrowsCheckedException(Do.THROW)).in(Wrapped.class); 101 | } 102 | 103 | @Test 104 | public void streams_and_exceptions() { 105 | List result = 106 | asList("foo", "bar", "baz", "boooo") 107 | .stream() 108 | .map(Result.wrapReturn(Example::duplicatesShortStrings)) 109 | .map(Result.wrap(s -> s.toUpperCase())) 110 | .filter(Result::success) 111 | .map(Result::unwrap) 112 | .collect(toList()); 113 | 114 | assertEquals(asList("FOOFOO", "BARBAR", "BAZBAZ"), result); 115 | } 116 | 117 | @Test 118 | public void streams_and_exceptions_exceptions_mid_stream() { 119 | List result = 120 | asList("foo", "bar", "baz", "UPR", "boooo") 121 | .stream() 122 | .map(Result.wrapReturn(Example::duplicatesShortStrings)) 123 | .map(Result.wrapExceptional(Example::uppercasesStrings)) 124 | .filter(Result::success) 125 | .map(Result::unwrap) 126 | .collect(toList()); 127 | 128 | assertEquals(asList("FOOFOO", "BARBAR", "BAZBAZ"), result); 129 | } 130 | 131 | @Test 132 | public void streams_and_exceptions_exceptions_map_failure_cases() { 133 | List result = 134 | asList("foo", "bar", "baz", "UPR", "boooo") 135 | .stream() 136 | .map(Result.wrapReturn(Example::duplicatesShortStrings)) 137 | .map(Result.wrapExceptional(Example::uppercasesStrings)) 138 | .map(Result.onSuccess(Function.identity()).on(InputTooLongException.class, s -> "OhNoes").mapper()) 139 | .filter(Result::success) 140 | .map(Result::unwrap) 141 | .collect(toList()); 142 | 143 | assertEquals(asList("FOOFOO","BARBAR","BAZBAZ", "OhNoes"), result); 144 | } 145 | 146 | @Test 147 | public void streams_and_exceptions_exceptions_throw_unfiltered_failures() { 148 | try { 149 | asList("foo", "bar", "baz", "UPR", "boooo") 150 | .stream() 151 | .map(Result.wrapReturn(Example::duplicatesShortStrings)) 152 | .map(Result.wrapExceptional(Example::uppercasesStrings)) 153 | .map(Result::unwrap) 154 | .collect(toList()); 155 | } catch (RuntimeException e) { 156 | assertTrue(e.getCause() instanceof InputContainsUppercaseException); 157 | } 158 | } 159 | 160 | @Test 161 | public void completable_future_supplyaync_exceptional() { 162 | CompletableFuture 163 | .supplyAsync(Result.wrapReturn(Example::throwingSupplier)) 164 | .thenApply(Result.wrap(String::toUpperCase)) 165 | .thenAccept(Result.wrapConsumer(System.out::println)); 166 | 167 | 168 | CompletableFuture 169 | .supplyAsync(Result.wrapReturn(Example::notThrowingSupplier)) 170 | .thenApply(Result.wrap(String::toUpperCase)) 171 | .thenAccept(Result.wrapConsumer(System.out::println)); 172 | } 173 | 174 | @Test 175 | public void exceptional_stream_flatmap() { 176 | Book book = () -> "book"; 177 | 178 | List books = asList(book).stream() 179 | .flatMap(Exceptions.stream(Book::name)) 180 | .collect(toList()); 181 | 182 | assertEquals(asList("book"), books); 183 | } 184 | 185 | 186 | public static class ACheckedExceptionIDontHaveAGoodWayToDealWith extends Exception { 187 | 188 | } 189 | 190 | public static class Wrapped extends Exception { 191 | public Wrapped(Exception e) { 192 | super(e); 193 | } 194 | } 195 | 196 | public static class InputTooLongException extends Exception { 197 | 198 | } 199 | 200 | public static class InputContainsUppercaseException extends Exception { 201 | 202 | } 203 | 204 | public interface Book { 205 | String name() throws NoNameException; 206 | } 207 | 208 | public static class NoNameException extends Exception {} 209 | 210 | 211 | } 212 | -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/functions/ExceptionalFunctionTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.functions; 2 | 3 | import org.junit.Test; 4 | import uk.co.benjiweber.expressions.exceptions.Example; 5 | import uk.co.benjiweber.expressions.exceptions.Result; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | import java.util.function.Function; 10 | import java.util.stream.Stream; 11 | 12 | import static java.util.Arrays.asList; 13 | import static java.util.stream.Collectors.toList; 14 | import static org.junit.Assert.assertEquals; 15 | import static uk.co.benjiweber.expressions.exceptions.Example.Do; 16 | import static uk.co.benjiweber.expressions.functions.ExceptionalFunction.exceptional; 17 | 18 | public class ExceptionalFunctionTest { 19 | @Test 20 | public void unchecked_returns_result() { 21 | String foo = exceptional(Example::methodThatThrowsCheckedException).unchecked().apply(Do.NOT_THROW); 22 | assertEquals("foo", foo); 23 | } 24 | 25 | @Test(expected = RuntimeException.class) 26 | public void unchecked_throws() { 27 | exceptional(Example::methodThatThrowsCheckedException).unchecked().apply(Do.THROW); 28 | } 29 | 30 | @Test 31 | public void wrap_in_stream() { 32 | Function> f = exceptional(Example::methodThatThrowsCheckedException).stream(); 33 | 34 | assertEquals(Optional.empty(), f.apply(Do.THROW).findAny()); 35 | assertEquals(Optional.of("foo"), f.apply(Do.NOT_THROW).findAny()); 36 | } 37 | 38 | @Test 39 | public void wrap_in_optional() { 40 | Function> f = exceptional(Example::methodThatThrowsCheckedException).optional(); 41 | 42 | assertEquals(Optional.empty(), f.apply(Do.THROW)); 43 | assertEquals(Optional.of("foo"), f.apply(Do.NOT_THROW)); 44 | } 45 | 46 | 47 | interface Container { 48 | T value(); 49 | static Container empty() { return () -> null; } 50 | static Container of(T value) { return () -> value; } 51 | } 52 | 53 | @Test 54 | public void wrap_in_custom_container() { 55 | Function> f = exceptional(Example::methodThatThrowsCheckedException).wrapReturn(Container::of).wrapException(Container::empty); 56 | assertEquals("foo", f.apply(Do.NOT_THROW).value()); 57 | assertEquals(null, f.apply(Do.THROW).value()); 58 | } 59 | 60 | @Test 61 | public void wrapping_in_results() { 62 | List result = 63 | asList("foo", "bar", "baz", "UPR", "boooo") 64 | .stream() 65 | .map(exceptional(Example::duplicatesShortStrings).resultOut()) 66 | .map(exceptional(Example::uppercasesStrings).resultInOut()) 67 | .filter(Result::success) 68 | .map(Result::unwrap) 69 | .collect(toList()); 70 | 71 | assertEquals(asList("FOOFOO", "BARBAR", "BAZBAZ"), result); 72 | } 73 | 74 | @Test 75 | public void flatmapping_with_stream() { 76 | List result = 77 | asList("foo", "bar", "baz", "UPR", "boooo") 78 | .stream() 79 | .flatMap(exceptional(Example::duplicatesShortStrings).stream()) 80 | .flatMap(exceptional(Example::uppercasesStrings).stream()) 81 | .collect(toList()); 82 | 83 | assertEquals(asList("FOOFOO", "BARBAR", "BAZBAZ"), result); 84 | } 85 | 86 | @Test 87 | public void logging_discarded() { 88 | List result = 89 | asList("foo", "bar", "baz", "UPR", "boooo") 90 | .stream() 91 | .flatMap(exceptional(Example::duplicatesShortStrings).wrapReturn(Stream::of).peek(System.out::println).orElse(Stream::empty)) 92 | .flatMap(exceptional(Example::uppercasesStrings).wrapReturn(Stream::of).peek(System.out::println).orElse(Stream::empty)) 93 | .collect(toList()); 94 | 95 | assertEquals(asList("FOOFOO", "BARBAR", "BAZBAZ"), result); 96 | } 97 | 98 | 99 | 100 | } -------------------------------------------------------------------------------- /src/test/java/uk/co/benjiweber/expressions/tuples/TupleTest.java: -------------------------------------------------------------------------------- 1 | package uk.co.benjiweber.expressions.tuples; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | import static org.junit.Assert.assertNotEquals; 7 | import static uk.co.benjiweber.expressions.tuples.Tuple.tuple; 8 | 9 | public class TupleTest { 10 | 11 | @Test public void return_and_expand_tuples() { 12 | String name = me() 13 | .map((firstname, favouriteNo, surname) -> tuple(firstname, surname)) 14 | .map((firstname, surname) -> firstname + " " + surname); 15 | 16 | assertEquals("benji weber", name); 17 | } 18 | 19 | static TriTuple me() { 20 | return tuple("benji",9001,"weber"); 21 | } 22 | 23 | @Test public void tuple_equality() { 24 | assertEquals(tuple("hello"), tuple("hello")); 25 | assertNotEquals(tuple("hello"), tuple("hello2")); 26 | 27 | assertEquals(tuple("hello","world"), tuple("hello", "world")); 28 | assertNotEquals(tuple("hello", "world"), tuple("hello", "world2")); 29 | 30 | assertEquals(tuple("hello","world",5), tuple("hello", "world",5)); 31 | assertNotEquals(tuple("hello", "world",5), tuple("hello", "world2",5)); 32 | } 33 | 34 | @Test public void tuple_toString() { 35 | assertEquals("{hello}",tuple("hello").toString()); 36 | assertEquals("{hello, world}",tuple("hello","world").toString()); 37 | assertEquals("{hello, world, 9}",tuple("hello","world",9).toString()); 38 | } 39 | 40 | @Test(expected = NumberTooBigException.class) 41 | public void return_and_throw_checked_exception() throws NumberTooBigException { 42 | String name = me() 43 | .map((firstname, favouriteNo, surname) -> { 44 | if (favouriteNo > 9000) throw new NumberTooBigException(); 45 | return firstname; 46 | }); 47 | } 48 | 49 | static class NumberTooBigException extends Exception {} 50 | } 51 | --------------------------------------------------------------------------------