[LANG-1431] Add ComparableUtils (#398)

This commit is contained in:
Sam Kruglov 2019-12-31 15:35:10 +02:00 committed by Gary Gregory
parent 2a3c56efc0
commit 2425853701
2 changed files with 494 additions and 0 deletions

View File

@ -0,0 +1,206 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3;
import java.util.function.Predicate;
/**
* <p>Utility library to provide helper methods for translating {@link Comparable#compareTo} result into a boolean.</p>
*
* <p>Example: {@code boolean x = is(myComparable).lessThanOrEqualTo(otherComparable)}</p>
*
* <p>#ThreadSafe#</p>
*
* @since 3.10
*/
public class ComparableUtils {
private ComparableUtils() {}
/**
* Provides access to the available methods
*
* @param a base object in the further comparison
* @param <A> type of the base object
* @return a builder object with further methods
*/
public static <A extends Comparable<A>> ComparableCheckBuilder<A> is(A a) {
return new ComparableCheckBuilder<>(a);
}
/**
* Checks if the tested object is less than {@code b}
*
* @param b the object to compare to the tested object
* @param <A> type of the test object
* @return a predicate for true if the value returned by {@link Comparable#compareTo} is less than {@code 0}
*/
public static <A extends Comparable<A>> Predicate<A> lt(A b) {
return a -> is(a).lessThan(b);
}
/**
* Checks if the tested object is less than or equal to {@code b}
*
* @param b the object to compare to the tested object
* @param <A> type of the test object
* @return a predicate for true if the value returned by {@link Comparable#compareTo}
* is less than or equal to {@code 0}
*/
public static <A extends Comparable<A>> Predicate<A> le(A b) {
return a -> is(a).lessThanOrEqualTo(b);
}
/**
* Checks if the tested object is greater than {@code b}
*
* @param b the object to compare to the tested object
* @param <A> type of the test object
* @return a predicate for true if the value returned by {@link Comparable#compareTo} is greater than {@code 0}
*/
public static <A extends Comparable<A>> Predicate<A> gt(A b) {
return a -> is(a).greaterThan(b);
}
/**
* Checks if the tested object is greater than or equal to {@code b}
*
* @param b the object to compare to the tested object
* @param <A> type of the test object
* @return a predicate for true if the value returned by {@link Comparable#compareTo}
* is greater than or equal to {@code 0}
*/
public static <A extends Comparable<A>> Predicate<A> ge(A b) {
return a -> is(a).greaterThanOrEqualTo(b);
}
/**
* Checks if {@code [b <= a <= c]} or {@code [b >= a >= c]} where the {@code a} is the tested object.
*
* @param b the object to compare to the tested object
* @param c the object to compare to the tested object
* @param <A> type of the test object
* @return a predicate for true if the tested object is between b and c
*/
public static <A extends Comparable<A>> Predicate<A> between(A b, A c) {
return a -> is(a).between(b, c);
}
/**
* Checks if {@code (b < a < c)} or {@code (b > a > c)} where the {@code a} is the tested object.
*
* @param b the object to compare to the tested object
* @param c the object to compare to the tested object
* @param <A> type of the test object
* @return a predicate for true if the tested object is between b and c and not equal to those
*/
public static <A extends Comparable<A>> Predicate<A> betweenExclusive(A b, A c) {
return a -> is(a).betweenExclusive(b, c);
}
/**
* Provides access to the available methods
*/
public static class ComparableCheckBuilder<A extends Comparable<A>> {
private final A a;
private ComparableCheckBuilder(A a) {
this.a = a;
}
/**
* Checks if the object passed to {@link #is} is equal to {@code b}
*
* @param b the object to compare to the base object
* @return true if the value returned by {@link Comparable#compareTo} is equal to {@code 0}
*/
public boolean equalTo(A b) {
return a.compareTo(b) == 0;
}
/**
* Checks if the object passed to {@link #is} is less than {@code b}
*
* @param b the object to compare to the base object
* @return true if the value returned by {@link Comparable#compareTo} is less than {@code 0}
*/
public boolean lessThan(A b) {
return a.compareTo(b) < 0;
}
/**
* Checks if the object passed to {@link #is} is less than or equal to {@code b}
*
* @param b the object to compare to the base object
* @return true if the value returned by {@link Comparable#compareTo} is less than or equal to {@code 0}
*/
public boolean lessThanOrEqualTo(A b) {
return a.compareTo(b) <= 0;
}
/**
* Checks if the object passed to {@link #is} is greater than {@code b}
*
* @param b the object to compare to the base object
* @return true if the value returned by {@link Comparable#compareTo} is greater than {@code 0}
*/
public boolean greaterThan(A b) {
return a.compareTo(b) > 0;
}
/**
* Checks if the object passed to {@link #is} is greater than or equal to {@code b}
*
* @param b the object to compare to the base object
* @return true if the value returned by {@link Comparable#compareTo} is greater than or equal to {@code 0}
*/
public boolean greaterThanOrEqualTo(A b) {
return a.compareTo(b) >= 0;
}
/**
* Checks if {@code [b <= a <= c]} or {@code [b >= a >= c]} where the {@code a} is object passed to {@link #is}.
*
* @param b the object to compare to the base object
* @param c the object to compare to the base object
* @return true if the base object is between b and c
*/
public boolean between(A b, A c) {
return betweenOrdered(b, c) || betweenOrdered(c, b);
}
/**
* Checks if {@code (b < a < c)} or {@code (b > a > c)} where the {@code a} is object passed to {@link #is}.
*
* @param b the object to compare to the base object
* @param c the object to compare to the base object
* @return true if the base object is between b and c and not equal to those
*/
public boolean betweenExclusive(A b, A c) {
return betweenOrderedExclusive(b, c) || betweenOrderedExclusive(c, b);
}
private boolean betweenOrdered(A b, A c) {
return greaterThanOrEqualTo(b) && lessThanOrEqualTo(c);
}
private boolean betweenOrderedExclusive(A b, A c) {
return greaterThan(b) && lessThan(c);
}
}
}

View File

@ -0,0 +1,288 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import static org.apache.commons.lang3.ComparableUtils.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
public class ComparableUtilsTest {
@Nested
class A_is_1 {
BigDecimal a = BigDecimal.ONE;
@DisplayName("B is 1 (B = A)")
@Nested
class B_is_1 {
BigDecimal b = BigDecimal.ONE;
@Test
void equalTo_returns_true() {
assertTrue(is(a).equalTo(b));
}
@Test
void lessThan_returns_false() {
assertFalse(is(a).lessThan(b));
}
@Test
void lessThanOrEqualTo_returns_true() {
assertTrue(is(a).lessThanOrEqualTo(b));
}
@Test
void greaterThan_returns_false() {
assertFalse(is(a).greaterThan(b));
}
@Test
void greaterThanOrEqualTo_returns_true() {
assertTrue(is(a).greaterThanOrEqualTo(b));
}
@DisplayName("C is 1 (B = A = C)")
@Nested
class C_is_1 {
BigDecimal c = BigDecimal.ONE;
@Test
void between_returns_true() {
assertTrue(is(a).between(b, c));
}
@Test
void betweenExclusive_returns_false() {
assertFalse(is(a).betweenExclusive(b, c));
}
}
@DisplayName("C is 10 (B = A < C)")
@Nested
class C_is_10 {
BigDecimal c = BigDecimal.TEN;
@Test
void between_returns_true() {
assertTrue(is(a).between(b, c));
}
@Test
void betweenExclusive_returns_false() {
assertFalse(is(a).betweenExclusive(b, c));
}
}
@DisplayName("C is 0 (B = A > C)")
@Nested
class C_is_0 {
BigDecimal c = BigDecimal.ZERO;
@Test
void between_returns_true() {
assertTrue(is(a).between(b, c));
}
@Test
void betweenExclusive_returns_false() {
assertFalse(is(a).betweenExclusive(b, c));
}
}
}
@DisplayName("B is 10 (B > A)")
@Nested
class B_is_10 {
BigDecimal b = BigDecimal.TEN;
@Test
void equalTo_returns_false() {
assertFalse(is(a).equalTo(b));
}
@Test
void lessThan_returns_true() {
assertTrue(is(a).lessThan(b));
}
@Test
void lessThanOrEqualTo_returns_true() {
assertTrue(is(a).lessThanOrEqualTo(b));
}
@Test
void greaterThan_returns_false() {
assertFalse(is(a).greaterThan(b));
}
@Test
void greaterThanOrEqualTo_returns_false() {
assertFalse(is(a).greaterThanOrEqualTo(b));
}
@DisplayName("C is 1 (B > A = C)")
@Nested
class C_is_1 {
BigDecimal c = BigDecimal.ONE;
@Test
void between_returns_true() {
assertTrue(is(a).between(b, c));
}
@Test
void betweenExclusive_returns_false() {
assertFalse(is(a).betweenExclusive(b, c));
}
}
@DisplayName("C is 10 ([B,C] > A)")
@Nested
class C_is_10 {
BigDecimal c = BigDecimal.TEN;
@Test
void between_returns_false() {
assertFalse(is(a).between(b, c));
}
@Test
void betweenExclusive_returns_false() {
assertFalse(is(a).betweenExclusive(b, c));
}
}
@DisplayName("C is 0 (B > A > C)")
@Nested
class C_is_0 {
BigDecimal c = BigDecimal.ZERO;
@Test
void between_returns_true() {
assertTrue(is(a).between(b, c));
}
@Test
void betweenExclusive_returns_true() {
assertTrue(is(a).betweenExclusive(b, c));
}
}
}
@DisplayName("B is 0 (B < A)")
@Nested
class B_is_0 {
BigDecimal b = BigDecimal.ZERO;
@Test
void equalTo_returns_false() {
assertFalse(is(a).equalTo(b));
}
@Test
void lessThan_returns_false() {
assertFalse(is(a).lessThan(b));
}
@Test
void lessThanOrEqualTo_returns_false() {
assertFalse(is(a).lessThanOrEqualTo(b));
}
@Test
void greaterThan_returns_true() {
assertTrue(is(a).greaterThan(b));
}
@Test
void greaterThanOrEqualTo_returns_true() {
assertTrue(is(a).greaterThanOrEqualTo(b));
}
@DisplayName("C is 1 (B < A = C)")
@Nested
class C_is_1 {
BigDecimal c = BigDecimal.ONE;
@Test
void between_returns_true() {
assertTrue(is(a).between(b, c));
}
@Test
void betweenExclusive_returns_false() {
assertFalse(is(a).betweenExclusive(b, c));
}
}
@DisplayName("C is 10 (B < A < C)")
@Nested
class C_is_10 {
BigDecimal c = BigDecimal.TEN;
@Test
void between_returns_true() {
assertTrue(is(a).between(b, c));
}
@Test
void betweenExclusive_returns_true() {
assertTrue(is(a).betweenExclusive(b, c));
}
}
@DisplayName("C is 0 ([B=C] < A)")
@Nested
class C_is_0 {
BigDecimal c = BigDecimal.ZERO;
@Test
void between_returns_false() {
assertFalse(is(a).between(b, c));
}
@Test
void betweenExclusive_returns_false() {
assertFalse(is(a).betweenExclusive(b, c));
}
}
}
}
}