diff --git a/src/main/java/edu/hm/hafner/util/Ensure.java b/src/main/java/edu/hm/hafner/util/Ensure.java
index 4ea6dfb02..e5f74d307 100644
--- a/src/main/java/edu/hm/hafner/util/Ensure.java
+++ b/src/main/java/edu/hm/hafner/util/Ensure.java
@@ -1,28 +1,30 @@
package edu.hm.hafner.util;
+import org.apache.commons.lang3.StringUtils;
+
import com.google.errorprone.annotations.FormatMethod;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.CheckReturnValue;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Formatter;
import java.util.List;
+import java.util.Objects;
/**
- * Provides several helper methods to validate method arguments and class invariants, thus supporting the design by
- * contract concept (DBC).
+ * Provides several helper methods to validate method arguments and class invariants, thus supporting the
+ * design-by-contract concept (DBC).
*
*
- * Note: the static methods provided by this class use a fluent interface, i.e., in order to verify an assertion a
- * method sequence needs to be called.
+ * Note: the static methods provided by this class use a fluent interface, i.e., to verify an assertion, a method
+ * sequence needs to be called.
*
*
*
- * Available checks:
+ * Available checks:
*
*
* - Boolean assertions, e.g., {@code Ensure.that(condition).isTrue(); }
@@ -33,7 +35,8 @@
*
*
* @author Ullrich Hafner
- * @see Design by Contract (Meyer, Bertrand)
+ * @see Design by Contract (Meyer,
+ * Bertrand)
*/
public final class Ensure {
/**
@@ -172,23 +175,6 @@ private static void throwException(final String message, @CheckForNull final Obj
throw new AssertionError(message.formatted(args));
}
- /**
- * Throws a {@link NullPointerException} with the specified detail message.
- *
- * @param message
- * a {@link Formatter formatted message} with the description of the error
- * @param args
- * Arguments referenced by the format specifiers in the formatted message. If there are more arguments than
- * format specifiers, the extra arguments are ignored. The number of arguments is variable and may be zero.
- *
- * @throws AssertionError
- * always thrown
- */
- @FormatMethod
- private static void throwNullPointerException(final String message, final Object... args) {
- throw new NullPointerException(message.formatted(args)); // NOPMD
- }
-
private Ensure() {
// prevents instantiation
}
@@ -212,12 +198,79 @@ public IterableCondition(@CheckForNull final Iterable> value) {
* that each element of the iterable is not {@code null}.
*
* @throws AssertionError
- * if the iterable is empty (or {@code null}), or at least one iterable element is {@code null}.
+ * if the iterable is empty (or {@code null}), or at least one iterable element is {@code null}
*/
public void isNotEmpty() {
isNotEmpty("Iterable is empty or NULL");
}
+ /**
+ * Ensures that the given iterable is not {@code null} but empty.
+ *
+ * @throws AssertionError
+ * if the iterable is not empty (or {@code null})
+ */
+ public void isEmpty() {
+ isEmpty("Iterable '%s' is not empty or NULL", renderValue());
+ }
+
+ /**
+ * Ensures that the given iterable is not {@code null} and contains the specified number of elements.
+ * Additionally, ensures that each element of the iterable is not {@code null}.
+ *
+ * @param expectedSize
+ * the expected number of elements in the iterable
+ *
+ * @throws AssertionError
+ * if the iterable does not contain the expected number of elements (or is {@code null}), or at least
+ * one iterable element is {@code null}
+ */
+ public void hasSize(final int expectedSize) {
+ hasSize(expectedSize,
+ "Iterable does not contain the expected number of elements. "
+ + "Actual value: %s. Expected size: %d",
+ renderValue(), expectedSize);
+ }
+
+ /**
+ * Ensures that the given iterable is not {@code null} and contains the specified number of elements.
+ * Additionally, ensures that each element of the iterable is not {@code null}.
+ *
+ * @param expectedSize
+ * the expected number of elements in the iterable
+ * @param explanation
+ * a {@link Formatter formatted message} explaining the assertion
+ * @param args
+ * Arguments referenced by the format specifiers in the formatted explanation. If there are more
+ * arguments than format specifiers, the extra arguments are ignored. The number of arguments is
+ * variable and may be zero.
+ *
+ * @throws AssertionError
+ * if the iterable does not contain the expected number of elements (or is {@code null}), or at least
+ * one iterable element is {@code null}
+ */
+ @FormatMethod
+ public void hasSize(final int expectedSize, final String explanation, final Object... args) {
+ isNotNull(explanation, args);
+
+ var size = computeSize(explanation, args);
+ if (size != expectedSize) {
+ throwException(explanation, args);
+ }
+ }
+
+ @FormatMethod
+ private int computeSize(final String explanation, final Object... args) {
+ int size = 0;
+ for (Object object : getValue()) {
+ if (object == null) {
+ throwException(explanation, args);
+ }
+ size++;
+ }
+ return size;
+ }
+
/**
* Ensures that the given iterable is not {@code null} and contains at least one element. Additionally, ensures
* that each element of the iterable is not {@code null}.
@@ -230,20 +283,35 @@ public void isNotEmpty() {
* variable and may be zero.
*
* @throws AssertionError
- * if the iterable is empty (or {@code null}), or at least one iterable element is {@code null}.
+ * if the iterable is empty (or {@code null}), or at least one iterable element is {@code null}
*/
@FormatMethod
public void isNotEmpty(final String explanation, final Object... args) {
isNotNull(explanation, args);
- if (getValue().iterator().hasNext()) {
- for (Object object : getValue()) {
- if (object == null) {
- throwException(explanation, args);
- }
- }
+ if (computeSize(explanation, args) == 0) {
+ throwException(explanation, args);
}
- else {
+ }
+
+ /**
+ * Ensures that the given iterable is not {@code null} but has no elements.
+ *
+ * @param explanation
+ * a {@link Formatter formatted message} explaining the assertion
+ * @param args
+ * Arguments referenced by the format specifiers in the formatted explanation. If there are more
+ * arguments than format specifiers, the extra arguments are ignored. The number of arguments is
+ * variable and may be zero.
+ *
+ * @throws AssertionError
+ * if the iterable is not empty (or {@code null})
+ */
+ @FormatMethod
+ public void isEmpty(final String explanation, final Object... args) {
+ isNotNull(explanation, args);
+
+ if (computeSize(explanation, args) != 0) {
throwException(explanation, args);
}
}
@@ -278,7 +346,7 @@ Collection> getValue() {
* if the collection is {@code null} or if the specified element is not found
*/
public void contains(final Object element) {
- contains(element, "Collection %s does not contain element '%s'", getValue(), element);
+ contains(element, "Collection '%s' does not contain element '%s'", renderValue(), element);
}
/**
@@ -315,7 +383,7 @@ public void contains(final Object element, final String explanation, final Objec
* if the collection is {@code null} or if the specified element is part of the collection
*/
public void doesNotContain(final Object element) {
- doesNotContain(element, "Collection '%s' contains element '%s'", getValue(), element);
+ doesNotContain(element, "Collection '%s' contains element '%s' but should not", renderValue(), element);
}
/**
@@ -363,12 +431,62 @@ public ArrayCondition(@CheckForNull final Object[] values) {
* that each element of the array is not {@code null}.
*
* @throws AssertionError
- * if the array is empty (or {@code null}), or at least one array element is {@code null}.
+ * if the array is empty (or {@code null}), or at least one array element is {@code null}
*/
public void isNotEmpty() {
isNotEmpty("Array is empty or NULL");
}
+ /**
+ * Ensures that the given array is not {@code null} but empty.
+ *
+ * @throws AssertionError
+ * if the array is not empty (or {@code null})
+ */
+ public void isEmpty() {
+ isEmpty("Array '%s' is not empty or NULL", renderValue());
+ }
+
+ /**
+ * Ensures that the given array is not {@code null} and has the specified number of elements. Additionally,
+ * ensures that each element of the array is not {@code null}.
+ *
+ * @param expectedSize
+ * the expected number of elements in the array
+ *
+ * @throws AssertionError
+ * if the array does not contain the expected number of elements (or is {@code null}), or at least one
+ * array element is {@code null}
+ */
+ public void hasSize(final int expectedSize) {
+ hasSize(expectedSize,
+ "Array does not contain the expected number of elements. "
+ + "Actual value: %s. Expected size: %d",
+ renderValue(), expectedSize);
+ }
+
+ /**
+ * Ensures that the given array is not {@code null} but empty.
+ *
+ * @param explanation
+ * a {@link Formatter formatted message} explaining the assertion
+ * @param args
+ * Arguments referenced by the format specifiers in the formatted explanation. If there are more
+ * arguments than format specifiers, the extra arguments are ignored. The number of arguments is
+ * variable and may be zero.
+ *
+ * @throws AssertionError
+ * if the array is empty (or {@code null}), or at least one array element is {@code null}.
+ */
+ @FormatMethod
+ public void isEmpty(final String explanation, final Object... args) {
+ isNotNull(explanation, args);
+
+ if (getValue().length != 0) {
+ throwException(explanation, args);
+ }
+ }
+
/**
* Ensures that the given array is not {@code null} and contains at least one element. Additionally, ensures
* that each element of the array is not {@code null}.
@@ -391,15 +509,46 @@ public void isNotEmpty(final String explanation, final Object... args) {
throwException(explanation, args);
}
else {
- for (Object object : getValue()) {
- if (object == null) {
- throwException(explanation, args);
- }
- }
+ doesNotContainNull(explanation, args);
}
}
- // FIXME: add isEmpty() methods
+ /**
+ * Ensures that the given array is not {@code null} and contains the specified number of elements. Additionally,
+ * ensures that each element of the array is not {@code null}.
+ *
+ * @param expectedSize
+ * the expected number of elements in the array
+ * @param explanation
+ * a {@link Formatter formatted message} explaining the assertion
+ * @param args
+ * Arguments referenced by the format specifiers in the formatted explanation. If there are more
+ * arguments than format specifiers, the extra arguments are ignored. The number of arguments is
+ * variable and may be zero.
+ *
+ * @throws AssertionError
+ * if the array is empty (or {@code null}), or at least one array element is {@code null}.
+ */
+ @FormatMethod
+ public void hasSize(final int expectedSize, final String explanation, final Object... args) {
+ isNotNull(explanation, args);
+
+ if (getValue().length == expectedSize) {
+ doesNotContainNull(explanation, args);
+ }
+ else {
+ throwException(explanation, args);
+ }
+ }
+
+ @FormatMethod
+ private void doesNotContainNull(final String explanation, final Object... args) {
+ for (Object object : getValue()) {
+ if (object == null) {
+ throwException(explanation, args);
+ }
+ }
+ }
}
/**
@@ -556,25 +705,25 @@ public void isNotNull() {
*/
@FormatMethod
public void isNotNull(final String explanation, final Object... args) {
+ var nullPointerException = new NullPointerException(explanation.formatted(args));
if (value == null || additionalValues == null) {
- throwNullPointerException(explanation, args);
+ throw nullPointerException; // NOPMD
}
else {
for (Object additionalValue : additionalValues) {
if (additionalValue == null) {
- throwNullPointerException(explanation, args);
+ throw nullPointerException; // NOPMD
}
}
}
}
- @SuppressFBWarnings("NP")
- @SuppressWarnings("PMD.AvoidThrowingNullPointerException")
T getValue() {
- if (value == null) {
- throw new NullPointerException("Value is null");
- }
- return value;
+ return Objects.requireNonNull(value, "Value is NULL");
+ }
+
+ String renderValue() {
+ return value + ", " + StringUtils.join(additionalValues, ", ");
}
/**
diff --git a/src/test/java/edu/hm/hafner/util/EnsureTest.java b/src/test/java/edu/hm/hafner/util/EnsureTest.java
index 2565be6d2..974c863e6 100644
--- a/src/test/java/edu/hm/hafner/util/EnsureTest.java
+++ b/src/test/java/edu/hm/hafner/util/EnsureTest.java
@@ -3,6 +3,8 @@
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
+import java.nio.file.Path;
+import java.util.Collection;
import java.util.Collections;
import java.util.Set;
@@ -30,10 +32,16 @@ void shouldNotThrowExceptionIfContractIsValid() {
Ensure.that("", "").isNotNull();
Ensure.that(null, (Object) null).isNull();
Ensure.that(new String[]{""}).isNotEmpty();
+ Ensure.that(new String[]{""}).hasSize(1);
+ Ensure.that(new String[0]).isEmpty();
+ Ensure.that(Path.of("")).isNotEmpty();
+ Ensure.that(new String[0]).hasSize(0);
Ensure.that(SOME_STRING).isNotEmpty();
Ensure.that(SOME_STRING).isNotBlank();
Ensure.that("").isInstanceOf(String.class);
+ Ensure.that(Set.of()).isEmpty();
Ensure.that(Set.of("")).isNotEmpty();
+ Ensure.that(Set.of("")).hasSize(1);
Ensure.that(Set.of("")).contains("");
Ensure.that(Set.of("")).doesNotContain(SOME_STRING);
}).doesNotThrowAnyException();
@@ -71,6 +79,20 @@ void shouldThrowExceptionIfContractIsViolated() {
.isInstanceOf(AssertionError.class);
assertThatThrownBy(() -> Ensure.that(Set.of("")).doesNotContain(""))
.isInstanceOf(AssertionError.class);
+ assertThatThrownBy(() -> Ensure.that(Set.of("")).isEmpty())
+ .isInstanceOf(AssertionError.class);
+ assertThatThrownBy(() -> Ensure.that(Set.of()).isNotEmpty())
+ .isInstanceOf(AssertionError.class);
+ assertThatThrownBy(() -> Ensure.that(Set.of("")).hasSize(0))
+ .isInstanceOf(AssertionError.class);
+ assertThatThrownBy(() -> Ensure.that(Set.of("")).hasSize(2))
+ .isInstanceOf(AssertionError.class);
+ assertThatThrownBy(() -> Ensure.that(new String[]{"not-empty"}).isEmpty())
+ .isInstanceOf(AssertionError.class);
+ assertThatThrownBy(() -> Ensure.that(new String[]{"not-empty"}).hasSize(0))
+ .isInstanceOf(AssertionError.class);
+ assertThatThrownBy(() -> Ensure.that(new String[]{"not-empty"}).hasSize(2))
+ .isInstanceOf(AssertionError.class);
}
/**
@@ -83,12 +105,18 @@ void shouldThrowNpeIfContractIsViolated() {
.isInstanceOf(NullPointerException.class).hasMessage(ERROR_MESSAGE);
assertThatThrownBy(() -> Ensure.that(SOME_STRING, (Object) null).isNotNull(ERROR_MESSAGE))
.isInstanceOf(NullPointerException.class).hasMessage(ERROR_MESSAGE);
+ assertThatThrownBy(() -> Ensure.that(SOME_STRING, (Object[]) null).isNotNull(ERROR_MESSAGE))
+ .isInstanceOf(NullPointerException.class).hasMessage(ERROR_MESSAGE);
assertThatThrownBy(() -> Ensure.that(null, SOME_STRING).isNotNull(ERROR_MESSAGE))
.isInstanceOf(NullPointerException.class).hasMessage(ERROR_MESSAGE);
assertThatThrownBy(() -> Ensure.that(null, (Object[]) null).isNotNull(ERROR_MESSAGE))
.isInstanceOf(NullPointerException.class).hasMessage(ERROR_MESSAGE);
assertThatThrownBy(() -> Ensure.that((Object) null).isNotNull())
.isInstanceOf(NullPointerException.class);
+ assertThatThrownBy(() -> Ensure.that((Collection>) null).isNotNull())
+ .isInstanceOf(NullPointerException.class);
+ assertThatThrownBy(() -> Ensure.that((Iterable>) null).isNotNull())
+ .isInstanceOf(NullPointerException.class);
assertThatThrownBy(() -> Ensure.that(SOME_STRING, (Object) null).isNotNull())
.isInstanceOf(NullPointerException.class);
assertThatThrownBy(() -> Ensure.that(null, SOME_STRING).isNotNull())
@@ -101,6 +129,10 @@ void shouldThrowNpeIfContractIsViolated() {
.isInstanceOf(NullPointerException.class).hasMessage(ERROR_MESSAGE);
assertThatThrownBy(() -> Ensure.that((Object[]) null).isNotEmpty())
.isInstanceOf(NullPointerException.class);
+ assertThatThrownBy(() -> Ensure.that((Collection>) null).isNotEmpty())
+ .isInstanceOf(NullPointerException.class);
+ assertThatThrownBy(() -> Ensure.that((Iterable>) null).isNotEmpty())
+ .isInstanceOf(NullPointerException.class);
assertThatThrownBy(() -> Ensure.that((String) null).isNotEmpty())
.isInstanceOf(NullPointerException.class);
}
@@ -121,6 +153,8 @@ void shouldThrowExceptionIfEmpty() {
Ensure.that("").isNotEmpty(ERROR_MESSAGE)).isInstanceOf(AssertionError.class);
assertThatThrownBy(() ->
Ensure.that(" ").isNotBlank(ERROR_MESSAGE)).isInstanceOf(AssertionError.class);
+ assertThatThrownBy(() ->
+ Ensure.that("").isNotBlank(ERROR_MESSAGE)).isInstanceOf(AssertionError.class);
assertThatThrownBy(() ->
Ensure.that("").isInstanceOf(Integer.class, ERROR_MESSAGE)).isInstanceOf(AssertionError.class);
assertThatThrownBy(() ->