diff --git a/runtime-parent/runtime-compute/src/main/java/com/speedment/runtime/compute/ToBoolean.java b/runtime-parent/runtime-compute/src/main/java/com/speedment/runtime/compute/ToBoolean.java index a175e1929e..e66d8df7ad 100644 --- a/runtime-parent/runtime-compute/src/main/java/com/speedment/runtime/compute/ToBoolean.java +++ b/runtime-parent/runtime-compute/src/main/java/com/speedment/runtime/compute/ToBoolean.java @@ -125,10 +125,42 @@ default int compare(T first, T second) { return Boolean.compare(f, s); } + /** + * {@inheritDoc} + *

+ * {@code ToBoolean} is a bit special when it comes to the + * {@code compose()}-method. If the {@code before} method returns + * {@code null}, the composed expression will return {@code false}. This is + * to remain compatible with how Speedment handles predicates in streams. + * To get a {@code ToBooleanNullable} that acts as you might expect, instead + * use {@link #compose} + * + * @param before the function to apply before this function is applied + * @param the input type of the {@code before} function + * @return the composed expression + */ @Override - default ToBooleanNullable compose(Function before) { + default ToBoolean compose(Function before) { @SuppressWarnings("unchecked") final Function casted = (Function) before; return ComposedUtil.composeToBoolean(casted, this); } + + /** + * Returns a composed expression that first applies the {@code before} + * function to its input, and then applies this function to the result. + * If evaluation of either function throws an exception, it is relayed to + * the caller of the composed function. + * + * @param before the function to apply before this function is applied + * @param the input type of the {@code before} function + * @return the composed expression + * + * @since 3.1.2 + */ + default ToBooleanNullable composeNullable(Function before) { + @SuppressWarnings("unchecked") + final Function casted = (Function) before; + return ComposedUtil.composeToBooleanAsNullable(casted, this); + } } diff --git a/runtime-parent/runtime-compute/src/main/java/com/speedment/runtime/compute/internal/expression/ComposedUtil.java b/runtime-parent/runtime-compute/src/main/java/com/speedment/runtime/compute/internal/expression/ComposedUtil.java index 869ab87375..c11b876717 100644 --- a/runtime-parent/runtime-compute/src/main/java/com/speedment/runtime/compute/internal/expression/ComposedUtil.java +++ b/runtime-parent/runtime-compute/src/main/java/com/speedment/runtime/compute/internal/expression/ComposedUtil.java @@ -455,10 +455,24 @@ public Double apply(T object) { * @param the type returned by {@code first} * @return the composed expression */ - public static ToBooleanNullable composeToBoolean(Function before, ToBoolean after) { + public static ToBoolean composeToBoolean(Function before, ToBoolean after) { return new ComposeToBoolean<>(before, after); } + /** + * Returns a new expression that first applies the {@code first} function + * and then passes the result to the {@code second} expression. + * + * @param before the first function to apply + * @param after the expression to apply to the result + * @param the type of the initial input + * @param the type returned by {@code first} + * @return the composed expression + */ + public static ToBooleanNullable composeToBooleanAsNullable(Function before, ToBoolean after) { + return new ComposeToBooleanNullable<>(before, after); + } + /** * Returns a new expression that first applies the {@code first} function * and then passes the result to the {@code second} expression. @@ -470,18 +484,22 @@ public static ToBooleanNullable composeToBoolean(Function before * @return the composed expression */ public static ToBooleanNullable composeToBooleanNullable(Function before, ToBooleanNullable after) { - return new ComposeToBoolean<>(before, after); + return new ComposeToBooleanNullable<>(before, after); } /** - * Internal implementation of the {@link ComposedExpression}. + * Internal implementation of the {@link ComposedExpression}. Booleans are + * handled a bit differently when it comes to {@code null}-values. If the + * {@code before}-function returns {@code null}, then the expression will + * always evaluate to {@code false}. This is to stay compatible with how + * Speedment handles predicates in streams. * * @param the outer input type * @param the inner input type * @param the expression type of the {@code after} operation */ private final static class ComposeToBoolean & Expression> - implements ComposedExpression, ToBooleanNullable { + implements ComposedExpression, ToBoolean { private final Function before; private final AFTER after; @@ -501,6 +519,41 @@ public AFTER secondStep() { return after; } + @Override + public boolean applyAsBoolean(T object) throws NullPointerException { + final A intermediate = before.apply(object); + return intermediate != null && after.applyAsBoolean(intermediate); + } + } + + /** + * Internal implementation of the {@link ComposedExpression}. + * + * @param the outer input type + * @param the inner input type + * @param the expression type of the {@code after} operation + */ + private final static class ComposeToBooleanNullable & Expression> + implements ComposedExpression, ToBooleanNullable { + + private final Function before; + private final AFTER after; + + ComposeToBooleanNullable(Function before, AFTER after) { + this.before = requireNonNull(before); + this.after = requireNonNull(after); + } + + @Override + public Function firstStep() { + return before; + } + + @Override + public AFTER secondStep() { + return after; + } + @Override public boolean applyAsBoolean(T object) throws NullPointerException { final A intermediate = before.apply(object);