diff --git a/README.adoc b/README.adoc index 5cc2b63..895bb64 100644 --- a/README.adoc +++ b/README.adoc @@ -1,8 +1,15 @@ # Conditional -If-then-else in a functional-style. +image:https://travis-ci.org/kemitix/conditional.svg?branch=master["Build Status", link="https://travis-ci.org/kemitix/conditional"] -## Usage +image::https://coveralls.io/repos/github/kemitix/conditional/badge.svg?branch=master["Coverage Status", link="https://coveralls.io/github/kemitix/conditional?branch=master"] + +* link:#_condition[Condition] +* link:#_value[Value] + +## Condition + +If-then-else in a functional-style. ### if-then @@ -169,3 +176,126 @@ Condition.where(isTrue()) .and(isAlsoTrue()) .then(() -> doSomethingElse()); ---- + +## Value + +Values from an if-then-else in a functional-style. + +Functional, and verbose, alternative to the ternary operator (`?:`). + +### if-then-else + +[[source,java]] +---- +String result; +if (isTrue()) { + result = TRUE; +} else { + result = FALSE; +} +---- + +[[source,java]] +---- +String result = isTrue() ? TRUE : FALSE; +---- + +[[source,java]] +---- +final String result = Value.where(isTrue()).then(() -> TRUE) + .otherwise(() -> FALSE); +---- + +### if-not-then-else + +[[source,java]] +---- +String result; +if (!isTrue()) { + result = TRUE; +} else { + result = FALSE; +} +---- + +[[source,java]] +---- +final String result = Value.whereNot(isTrue()).then(() -> TRUE) + .otherwise(() -> FALSE); +---- + +### if-and-then-else + +[[source,java]] +---- +String result; +if (isTrue() && alternativeIsTrue()) { + result = TRUE; +} else { + result = FALSE; +} +---- + +[[source,java]] +---- +final String result = Value.where(isTrue()).and(alternativeIsTrue()) + .then(() -> TRUE) + .otherwise(() -> FALSE); +---- + +### if-and-not-then-else + +[[source,java]] +---- +String result; +if (isTrue() && !alternativeIsFalse()) { + result = TRUE; +} else { + result = FALSE; +} +---- + +[[source,java]] +---- +final String result = Value.where(isTrue()).andNot(alternativeIsFalse()) + .then(() -> TRUE) + .otherwise(() -> FALSE); +---- + +### if-or-then-else + +[[source,java]] +---- +String result; +if (isTrue() || alternativeIsTrue()) { + result = TRUE; +} else { + result = FALSE; +} +---- + +[[source,java]] +---- +final String result = Value.where(isTrue()).or(alternativeIsTrue()) + .then(() -> TRUE) + .otherwise(() -> FALSE); +---- + +### if-or-not-then-else + +[[source,java]] +---- +String result; +if (isTrue() || !isFalse()) { + result = TRUE; +} else { + result = FALSE; +} +---- + +[[source,java]] +---- +final String result = Value.where(isTrue()).orNot(isFalse()) + .then(() -> TRUE) + .otherwise(() -> FALSE); +---- diff --git a/pom.xml b/pom.xml index d75aa0a..c2e1b13 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ conditional - 0.1.0 + 0.2.0 diff --git a/src/main/java/net/kemitix/conditional/Condition.java b/src/main/java/net/kemitix/conditional/Condition.java index 7a5f313..842c98c 100644 --- a/src/main/java/net/kemitix/conditional/Condition.java +++ b/src/main/java/net/kemitix/conditional/Condition.java @@ -21,8 +21,11 @@ package net.kemitix.conditional; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; +import java.util.AbstractMap.SimpleEntry; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * If-then-else in a functional-style. @@ -31,6 +34,14 @@ import lombok.NoArgsConstructor; */ public interface Condition { + Condition TRUE = new TrueCondition(); + + Condition FALSE = new FalseCondition(); + + Map CONDITIONS = Collections.unmodifiableMap( + Stream.of(new SimpleEntry<>(true, TRUE), new SimpleEntry<>(false, FALSE)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); + /** * Create a new {@code Condition} for the clause. * @@ -39,10 +50,7 @@ public interface Condition { * @return the Condition */ static Condition where(final boolean clause) { - if (clause) { - return TrueCondition.getInstance(); - } - return FalseCondition.getInstance(); + return CONDITIONS.get(clause); } /** @@ -52,7 +60,7 @@ public interface Condition { * * @return the Condition */ - static Condition whereNot(boolean clause) { + static Condition whereNot(final boolean clause) { return where(!clause); } @@ -72,7 +80,7 @@ public interface Condition { * * @return the Condition */ - default Condition andNot(boolean clause) { + default Condition andNot(final boolean clause) { return and(!clause); } @@ -92,7 +100,7 @@ public interface Condition { * * @return the Condition */ - default Condition orNot(boolean clause) { + default Condition orNot(final boolean clause) { return or(!clause); } @@ -119,39 +127,29 @@ public interface Condition { * * @return the Condition */ - default Condition otherwise(boolean clause) { + default Condition otherwise(final boolean clause) { return where(clause); } /** * A {@code Condition} that has evaluated to {@code true}. */ - @NoArgsConstructor(access = AccessLevel.PRIVATE) class TrueCondition implements Condition { - private static final Condition INSTANCE = new TrueCondition(); - - private static Condition getInstance() { - return INSTANCE; - } - @Override public Condition and(final boolean clause) { - if (clause) { - return this; - } - return FalseCondition.getInstance(); + return where(clause); } @Override public Condition or(final boolean secondClause) { - return this; + return TRUE; } @Override public Condition then(final Runnable response) { response.run(); - return this; + return TRUE; } @Override @@ -163,31 +161,21 @@ public interface Condition { /** * A {@code Condition} that has evaluated to {@code false}. */ - @NoArgsConstructor(access = AccessLevel.PRIVATE) class FalseCondition implements Condition { - private static final Condition INSTANCE = new FalseCondition(); - - private static Condition getInstance() { - return INSTANCE; - } - @Override public Condition and(final boolean clause) { - return this; + return FALSE; } @Override public Condition or(final boolean secondClause) { - if (secondClause) { - return TrueCondition.getInstance(); - } - return this; + return where(secondClause); } @Override public Condition then(final Runnable response) { - return this; + return FALSE; } @Override diff --git a/src/main/java/net/kemitix/conditional/Value.java b/src/main/java/net/kemitix/conditional/Value.java new file mode 100644 index 0000000..35618df --- /dev/null +++ b/src/main/java/net/kemitix/conditional/Value.java @@ -0,0 +1,214 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2017 Paul Campbell + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies + * or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE + * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package net.kemitix.conditional; + +import java.util.function.Supplier; + +/** + * A Value from an if-then-else in a functional-style. + * + * @author Paul Campbell (pcampbell@kemitix.net). + */ +public interface Value { + + /** + * Create a new {@link ValueClause} for the clause. + * + * @param clause the condition to test + * @param the type of the value + * + * @return a true or false value clause + */ + static ValueClause where(final boolean clause) { + if (clause) { + return new TrueValueClause<>(); + } + return new FalseValueClause<>(); + } + + /** + * Create a new {@link ValueClause} for the boolean opposite of the clause. + * + * @param clause the condition to test + * @param the type of the value + * + * @return a true or false value clause + */ + static ValueClause whereNot(boolean clause) { + return where(!clause); + } + + /** + * An intermediate state in determining the final {@link Value}. + * + * @param the type of the value + */ + interface ValueClause { + + /** + * Create a {@link ValueSupplier} with the {@link Supplier} should the {@link ValueClause} be true. + * + * @param trueSupplier the Supplier for the true value + * + * @return the value supplier + */ + ValueSupplier then(Supplier trueSupplier); + + /** + * Logically AND combine the current {@link ValueClause} with clause. + * + * @param clause the condition to test + * + * @return a true or false value clause + */ + ValueClause and(boolean clause); + + /** + * Logically OR combine the current {@link ValueClause} with clause. + * + * @param clause the condition to test + * + * @return a true or false value clause + */ + ValueClause or(boolean clause); + + /** + * Logically AND combine the current {@link ValueClause} with boolean opposite of the clause. + * + * @param clause the condition to test + * + * @return a true or false value clause + */ + default ValueClause andNot(final boolean clause) { + return and(!clause); + } + + /** + * Logically OR combine the current {@link ValueClause} with boolean opposite of the clause. + * + * @param clause the condition to test + * + * @return a true or false value clause + */ + default ValueClause orNot(boolean clause) { + return or(!clause); + } + + /** + * An intermediate result of the {@link Value}. + * + * @param the type of the value + */ + interface ValueSupplier { + + /** + * Determine the value by whether the {@link ValueClause} was true or false. + * + * @param falseSupplier the Supplier for the false value + * + * @return the value + */ + T otherwise(Supplier falseSupplier); + + } + + } + + /** + * An intermediate state where the clause has evaluated to true. + * + * @param the type of the value + */ + class TrueValueClause implements ValueClause { + + @Override + public ValueSupplier then(final Supplier trueSupplier) { + return new TrueValueSupplier(trueSupplier); + } + + @Override + public ValueClause and(final boolean clause) { + return Value.where(clause); + } + + @Override + public ValueClause or(final boolean clause) { + return this; + } + + /** + * An intermediate result of the {@link Value} where the clause has evaluated to true. + */ + private class TrueValueSupplier implements ValueSupplier { + + private final Supplier valueSupplier; + + TrueValueSupplier(final Supplier valueSupplier) { + this.valueSupplier = valueSupplier; + } + + @Override + public T otherwise(final Supplier falseSupplier) { + return valueSupplier.get(); + } + + } + + } + + /** + * An intermediate state where the clause has evaluated to false. + * + * @param the type of the value + */ + class FalseValueClause implements ValueClause { + + @Override + public ValueSupplier then(final Supplier trueSupplier) { + return new FalseValueSupplier(); + } + + @Override + public ValueClause and(final boolean clause) { + return this; + } + + @Override + public ValueClause or(final boolean clause) { + return Value.where(clause); + } + + /** + * An intermediate result of the {@link Value} where the clause has evaluated to false. + */ + private class FalseValueSupplier implements ValueSupplier { + + @Override + public T otherwise(final Supplier falseSupplier) { + return falseSupplier.get(); + } + + } + + } + +} diff --git a/src/test/java/net/kemitix/conditional/ConditionalTest.java b/src/test/java/net/kemitix/conditional/ConditionalTest.java index 67c52a1..2891fa0 100644 --- a/src/test/java/net/kemitix/conditional/ConditionalTest.java +++ b/src/test/java/net/kemitix/conditional/ConditionalTest.java @@ -124,6 +124,16 @@ public class ConditionalTest { thenTheThenResponseRuns(); } + @Test + public void whereNotTrueThenOtherwiseRuns() { + //when + Condition.whereNot(true) + .then(thenResponse) + .otherwise(otherwiseResponse); + //then + thenTheOtherwiseResponseRuns(); + } + @Test public void whereTrueAndNotFalseThenRuns() { //when @@ -134,6 +144,17 @@ public class ConditionalTest { thenTheThenResponseRuns(); } + @Test + public void whereTrueAndNotTrueThenOtherwiseRuns() { + //when + Condition.where(true) + .andNot(true) + .then(thenResponse) + .otherwise(otherwiseResponse); + //then + thenTheOtherwiseResponseRuns(); + } + @Test public void whereFalseOrNotFalseThenRuns() { //when @@ -144,6 +165,17 @@ public class ConditionalTest { thenTheThenResponseRuns(); } + @Test + public void whereFalseOrNotTrueThenOtherwiseRuns() { + //when + Condition.where(false) + .orNot(true) + .then(thenResponse) + .otherwise(otherwiseResponse); + //then + thenTheOtherwiseResponseRuns(); + } + @Test public void whereFalseElseTrueThenOtherwiseRuns() { //when @@ -166,6 +198,26 @@ public class ConditionalTest { thenNoResponseRuns(); } + @Test + public void whereTrueChainedThensBothRuns() { + //when + Condition.where(true) + .then(thenResponse) + .then(otherwiseResponse); + //then + thenBothResponsesRun(); + } + + @Test + public void whereFalseChainedThensNothingRuns() { + //when + Condition.where(false) + .then(thenResponse) + .then(otherwiseResponse); + //then + thenNoResponseRuns(); + } + private void whenOr(final boolean firstClause, final boolean secondClause) { Condition.where(firstClause) .or(secondClause) @@ -180,23 +232,43 @@ public class ConditionalTest { } private void thenBothResponsesRun() { - assertThat(thenFlag).isTrue(); - assertThat(otherwiseFlag).isTrue(); + theThenResponseRan(); + theOtherwiseResponseRan(); } private void thenTheThenResponseRuns() { - assertThat(thenFlag).isTrue(); - assertThat(otherwiseFlag).isFalse(); + theThenResponseRan(); + theOtherwiseResponseDidNotRun(); } private void thenTheOtherwiseResponseRuns() { - assertThat(thenFlag).isFalse(); - assertThat(otherwiseFlag).isTrue(); + theThenResponseDidNotRun(); + theOtherwiseResponseRan(); + } + + private void theOtherwiseResponseRan() { + assertThat(otherwiseFlag).as("otherwise response runs") + .isTrue(); + } + + private void theThenResponseRan() { + assertThat(thenFlag).as("then response runs") + .isTrue(); + } + + private void theOtherwiseResponseDidNotRun() { + assertThat(otherwiseFlag).as("otherwise response does not run") + .isFalse(); + } + + private void theThenResponseDidNotRun() { + assertThat(thenFlag).as("then response does not run") + .isFalse(); } private void thenNoResponseRuns() { - assertThat(thenFlag).isFalse(); - assertThat(otherwiseFlag).isFalse(); + theThenResponseDidNotRun(); + theOtherwiseResponseDidNotRun(); } private void when(final boolean firstClause, final boolean secondClause) { diff --git a/src/test/java/net/kemitix/conditional/ValueTest.java b/src/test/java/net/kemitix/conditional/ValueTest.java new file mode 100644 index 0000000..6971115 --- /dev/null +++ b/src/test/java/net/kemitix/conditional/ValueTest.java @@ -0,0 +1,220 @@ +package net.kemitix.conditional; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Paul Campbell (pcampbell@kemitix.net). + */ +public class ValueTest { + + private static final String TRUE = "true"; + + private static final String FALSE = "false"; + + @Test + public void valueWhereTrueIsTrue() { + //when + final String result = Value.where(true).then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsTrue(result); + } + + @Test + public void valueWhereFalseIsFalse() { + //when + final String result = Value.where(false).then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsFalse(result); + } + + @Test + public void valueWhereTrueAndTrueIsTrue() { + //when + final String result = Value.where(true).and(true) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsTrue(result); + } + + @Test + public void valueWhereTrueAndFalseIsFalse() { + //when + final String result = Value.where(true).and(false) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsFalse(result); + } + + @Test + public void valueWhereFalseAndTrueIsFalse() { + //when + final String result = Value.where(false).and(true) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsFalse(result); + } + + @Test + public void valueWhereFalseAndFalseIsFalse() { + //when + final String result = Value.where(false).and(false) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsFalse(result); + } + + @Test + public void valueWhereTrueOrTrueIsTrue() { + //when + final String result = Value.where(true).or(true) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsTrue(result); + } + + @Test + public void valueWhereTrueOrFalseIsTrue() { + //when + final String result = Value.where(true).or(false) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsTrue(result); + } + + @Test + public void valueWhereFalseOrTrueIsTrue() { + //when + final String result = Value.where(false).or(true) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsTrue(result); + } + + @Test + public void valueWhereFalseOrFalseIsFalse() { + //when + final String result = Value.where(false).or(false) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsFalse(result); + } + + @Test + public void valueWhereNotTrueIsFalse() { + //when + final String result = Value.whereNot(true).then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsFalse(result); + } + + @Test + public void valueWhereNotFalseIsTrue() { + //when + final String result = Value.whereNot(false).then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsTrue(result); + } + + @Test + public void valueWhereTrueAndNotTrueIsFalse() { + //when + final String result = Value.where(true).andNot(true) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsFalse(result); + } + + @Test + public void valueWhereTrueAndNotFalseIsTrue() { + //when + final String result = Value.where(true).andNot(false) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsTrue(result); + } + + @Test + public void valueWhereFalseAndNotTrueIsFalse() { + //when + final String result = Value.where(false).andNot(true) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsFalse(result); + } + + @Test + public void valueWhereFalseAndNotFalseIsFalse() { + //when + final String result = Value.where(false).andNot(false) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsFalse(result); + } + + @Test + public void valueWhereTrueOrNotTrueIsTrue() { + //when + final String result = Value.where(true).orNot(true) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsTrue(result); + } + + @Test + public void valueWhereTrueOrNotFalseIsTrue() { + //when + final String result = Value.where(true).orNot(false) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsTrue(result); + } + + @Test + public void valueWhereFalseOrNotTrueIsFalse() { + //when + final String result = Value.where(false).orNot(true) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsFalse(result); + } + + @Test + public void valueWhereFalseOrNotFalseIsTrue() { + //when + final String result = Value.where(false).orNot(false) + .then(() -> TRUE) + .otherwise(() -> FALSE); + //then + thenIsTrue(result); + } + + private void thenIsFalse(final String result) { + assertThat(result).isEqualTo(FALSE); + } + + private void thenIsTrue(final String result) { + assertThat(result).isEqualTo(TRUE); + } + +}