Merge branch 'release/0.2.0'

* release/0.2.0:
  pom.xml: version set to 0.2.0
  README.adoc: add documentation for Value
  Value: clean up unneeded generics type parameters
  Value: add javadoc
  ValueTest: remove ambiguity on then() and otherwise() parameters
  Value: rewrite using strong chaining logic
  Value: add initial implementation
  ConditionalTest: more tests for whereNot(), andNot() and orNot()
  Condition: remove use of 'if' internaly
  ConditionalTest: more tests
  ConditionalTest: clean up helper methods
  pom.xml: version set to 0.2.0-SNAPSHOT
This commit is contained in:
Paul Campbell 2017-04-23 21:45:06 +01:00
commit 3fcd5450bf
6 changed files with 671 additions and 47 deletions

View file

@ -1,8 +1,15 @@
# Conditional # 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 ### if-then
@ -169,3 +176,126 @@ Condition.where(isTrue())
.and(isAlsoTrue()) .and(isAlsoTrue())
.then(() -> doSomethingElse()); .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.<String>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.<String>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.<String>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.<String>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.<String>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.<String>where(isTrue()).orNot(isFalse())
.then(() -> TRUE)
.otherwise(() -> FALSE);
----

View file

@ -18,7 +18,7 @@
<relativePath/> <relativePath/>
</parent> </parent>
<artifactId>conditional</artifactId> <artifactId>conditional</artifactId>
<version>0.1.0</version> <version>0.2.0</version>
<dependencies> <dependencies>
<dependency> <dependency>

View file

@ -21,8 +21,11 @@
package net.kemitix.conditional; package net.kemitix.conditional;
import lombok.AccessLevel; import java.util.AbstractMap.SimpleEntry;
import lombok.NoArgsConstructor; 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. * If-then-else in a functional-style.
@ -31,6 +34,14 @@ import lombok.NoArgsConstructor;
*/ */
public interface Condition { public interface Condition {
Condition TRUE = new TrueCondition();
Condition FALSE = new FalseCondition();
Map<Boolean, Condition> 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. * Create a new {@code Condition} for the clause.
* *
@ -39,10 +50,7 @@ public interface Condition {
* @return the Condition * @return the Condition
*/ */
static Condition where(final boolean clause) { static Condition where(final boolean clause) {
if (clause) { return CONDITIONS.get(clause);
return TrueCondition.getInstance();
}
return FalseCondition.getInstance();
} }
/** /**
@ -52,7 +60,7 @@ public interface Condition {
* *
* @return the Condition * @return the Condition
*/ */
static Condition whereNot(boolean clause) { static Condition whereNot(final boolean clause) {
return where(!clause); return where(!clause);
} }
@ -72,7 +80,7 @@ public interface Condition {
* *
* @return the Condition * @return the Condition
*/ */
default Condition andNot(boolean clause) { default Condition andNot(final boolean clause) {
return and(!clause); return and(!clause);
} }
@ -92,7 +100,7 @@ public interface Condition {
* *
* @return the Condition * @return the Condition
*/ */
default Condition orNot(boolean clause) { default Condition orNot(final boolean clause) {
return or(!clause); return or(!clause);
} }
@ -119,39 +127,29 @@ public interface Condition {
* *
* @return the Condition * @return the Condition
*/ */
default Condition otherwise(boolean clause) { default Condition otherwise(final boolean clause) {
return where(clause); return where(clause);
} }
/** /**
* A {@code Condition} that has evaluated to {@code true}. * A {@code Condition} that has evaluated to {@code true}.
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
class TrueCondition implements Condition { class TrueCondition implements Condition {
private static final Condition INSTANCE = new TrueCondition();
private static Condition getInstance() {
return INSTANCE;
}
@Override @Override
public Condition and(final boolean clause) { public Condition and(final boolean clause) {
if (clause) { return where(clause);
return this;
}
return FalseCondition.getInstance();
} }
@Override @Override
public Condition or(final boolean secondClause) { public Condition or(final boolean secondClause) {
return this; return TRUE;
} }
@Override @Override
public Condition then(final Runnable response) { public Condition then(final Runnable response) {
response.run(); response.run();
return this; return TRUE;
} }
@Override @Override
@ -163,31 +161,21 @@ public interface Condition {
/** /**
* A {@code Condition} that has evaluated to {@code false}. * A {@code Condition} that has evaluated to {@code false}.
*/ */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
class FalseCondition implements Condition { class FalseCondition implements Condition {
private static final Condition INSTANCE = new FalseCondition();
private static Condition getInstance() {
return INSTANCE;
}
@Override @Override
public Condition and(final boolean clause) { public Condition and(final boolean clause) {
return this; return FALSE;
} }
@Override @Override
public Condition or(final boolean secondClause) { public Condition or(final boolean secondClause) {
if (secondClause) { return where(secondClause);
return TrueCondition.getInstance();
}
return this;
} }
@Override @Override
public Condition then(final Runnable response) { public Condition then(final Runnable response) {
return this; return FALSE;
} }
@Override @Override

View file

@ -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 <T> the type of the value
*
* @return a true or false value clause
*/
static <T> ValueClause<T> 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 <T> the type of the value
*
* @return a true or false value clause
*/
static <T> ValueClause<T> whereNot(boolean clause) {
return where(!clause);
}
/**
* An intermediate state in determining the final {@link Value}.
*
* @param <T> the type of the value
*/
interface ValueClause<T> {
/**
* 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<T> then(Supplier<T> trueSupplier);
/**
* Logically AND combine the current {@link ValueClause} with clause.
*
* @param clause the condition to test
*
* @return a true or false value clause
*/
ValueClause<T> 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<T> 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<T> 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<T> orNot(boolean clause) {
return or(!clause);
}
/**
* An intermediate result of the {@link Value}.
*
* @param <T> the type of the value
*/
interface ValueSupplier<T> {
/**
* 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<T> falseSupplier);
}
}
/**
* An intermediate state where the clause has evaluated to true.
*
* @param <T> the type of the value
*/
class TrueValueClause<T> implements ValueClause<T> {
@Override
public ValueSupplier<T> then(final Supplier<T> trueSupplier) {
return new TrueValueSupplier(trueSupplier);
}
@Override
public ValueClause<T> and(final boolean clause) {
return Value.where(clause);
}
@Override
public ValueClause<T> 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<T> {
private final Supplier<T> valueSupplier;
TrueValueSupplier(final Supplier<T> valueSupplier) {
this.valueSupplier = valueSupplier;
}
@Override
public T otherwise(final Supplier<T> falseSupplier) {
return valueSupplier.get();
}
}
}
/**
* An intermediate state where the clause has evaluated to false.
*
* @param <T> the type of the value
*/
class FalseValueClause<T> implements ValueClause<T> {
@Override
public ValueSupplier<T> then(final Supplier<T> trueSupplier) {
return new FalseValueSupplier();
}
@Override
public ValueClause<T> and(final boolean clause) {
return this;
}
@Override
public ValueClause<T> 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<T> {
@Override
public T otherwise(final Supplier<T> falseSupplier) {
return falseSupplier.get();
}
}
}
}

View file

@ -124,6 +124,16 @@ public class ConditionalTest {
thenTheThenResponseRuns(); thenTheThenResponseRuns();
} }
@Test
public void whereNotTrueThenOtherwiseRuns() {
//when
Condition.whereNot(true)
.then(thenResponse)
.otherwise(otherwiseResponse);
//then
thenTheOtherwiseResponseRuns();
}
@Test @Test
public void whereTrueAndNotFalseThenRuns() { public void whereTrueAndNotFalseThenRuns() {
//when //when
@ -134,6 +144,17 @@ public class ConditionalTest {
thenTheThenResponseRuns(); thenTheThenResponseRuns();
} }
@Test
public void whereTrueAndNotTrueThenOtherwiseRuns() {
//when
Condition.where(true)
.andNot(true)
.then(thenResponse)
.otherwise(otherwiseResponse);
//then
thenTheOtherwiseResponseRuns();
}
@Test @Test
public void whereFalseOrNotFalseThenRuns() { public void whereFalseOrNotFalseThenRuns() {
//when //when
@ -144,6 +165,17 @@ public class ConditionalTest {
thenTheThenResponseRuns(); thenTheThenResponseRuns();
} }
@Test
public void whereFalseOrNotTrueThenOtherwiseRuns() {
//when
Condition.where(false)
.orNot(true)
.then(thenResponse)
.otherwise(otherwiseResponse);
//then
thenTheOtherwiseResponseRuns();
}
@Test @Test
public void whereFalseElseTrueThenOtherwiseRuns() { public void whereFalseElseTrueThenOtherwiseRuns() {
//when //when
@ -166,6 +198,26 @@ public class ConditionalTest {
thenNoResponseRuns(); 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) { private void whenOr(final boolean firstClause, final boolean secondClause) {
Condition.where(firstClause) Condition.where(firstClause)
.or(secondClause) .or(secondClause)
@ -180,23 +232,43 @@ public class ConditionalTest {
} }
private void thenBothResponsesRun() { private void thenBothResponsesRun() {
assertThat(thenFlag).isTrue(); theThenResponseRan();
assertThat(otherwiseFlag).isTrue(); theOtherwiseResponseRan();
} }
private void thenTheThenResponseRuns() { private void thenTheThenResponseRuns() {
assertThat(thenFlag).isTrue(); theThenResponseRan();
assertThat(otherwiseFlag).isFalse(); theOtherwiseResponseDidNotRun();
} }
private void thenTheOtherwiseResponseRuns() { private void thenTheOtherwiseResponseRuns() {
assertThat(thenFlag).isFalse(); theThenResponseDidNotRun();
assertThat(otherwiseFlag).isTrue(); 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() { private void thenNoResponseRuns() {
assertThat(thenFlag).isFalse(); theThenResponseDidNotRun();
assertThat(otherwiseFlag).isFalse(); theOtherwiseResponseDidNotRun();
} }
private void when(final boolean firstClause, final boolean secondClause) { private void when(final boolean firstClause, final boolean secondClause) {

View file

@ -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.<String>where(true).then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsTrue(result);
}
@Test
public void valueWhereFalseIsFalse() {
//when
final String result = Value.<String>where(false).then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsFalse(result);
}
@Test
public void valueWhereTrueAndTrueIsTrue() {
//when
final String result = Value.<String>where(true).and(true)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsTrue(result);
}
@Test
public void valueWhereTrueAndFalseIsFalse() {
//when
final String result = Value.<String>where(true).and(false)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsFalse(result);
}
@Test
public void valueWhereFalseAndTrueIsFalse() {
//when
final String result = Value.<String>where(false).and(true)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsFalse(result);
}
@Test
public void valueWhereFalseAndFalseIsFalse() {
//when
final String result = Value.<String>where(false).and(false)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsFalse(result);
}
@Test
public void valueWhereTrueOrTrueIsTrue() {
//when
final String result = Value.<String>where(true).or(true)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsTrue(result);
}
@Test
public void valueWhereTrueOrFalseIsTrue() {
//when
final String result = Value.<String>where(true).or(false)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsTrue(result);
}
@Test
public void valueWhereFalseOrTrueIsTrue() {
//when
final String result = Value.<String>where(false).or(true)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsTrue(result);
}
@Test
public void valueWhereFalseOrFalseIsFalse() {
//when
final String result = Value.<String>where(false).or(false)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsFalse(result);
}
@Test
public void valueWhereNotTrueIsFalse() {
//when
final String result = Value.<String>whereNot(true).then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsFalse(result);
}
@Test
public void valueWhereNotFalseIsTrue() {
//when
final String result = Value.<String>whereNot(false).then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsTrue(result);
}
@Test
public void valueWhereTrueAndNotTrueIsFalse() {
//when
final String result = Value.<String>where(true).andNot(true)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsFalse(result);
}
@Test
public void valueWhereTrueAndNotFalseIsTrue() {
//when
final String result = Value.<String>where(true).andNot(false)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsTrue(result);
}
@Test
public void valueWhereFalseAndNotTrueIsFalse() {
//when
final String result = Value.<String>where(false).andNot(true)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsFalse(result);
}
@Test
public void valueWhereFalseAndNotFalseIsFalse() {
//when
final String result = Value.<String>where(false).andNot(false)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsFalse(result);
}
@Test
public void valueWhereTrueOrNotTrueIsTrue() {
//when
final String result = Value.<String>where(true).orNot(true)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsTrue(result);
}
@Test
public void valueWhereTrueOrNotFalseIsTrue() {
//when
final String result = Value.<String>where(true).orNot(false)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsTrue(result);
}
@Test
public void valueWhereFalseOrNotTrueIsFalse() {
//when
final String result = Value.<String>where(false).orNot(true)
.then(() -> TRUE)
.otherwise(() -> FALSE);
//then
thenIsFalse(result);
}
@Test
public void valueWhereFalseOrNotFalseIsTrue() {
//when
final String result = Value.<String>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);
}
}