From c6c371819c6c3932370b29b1e0ece4a0c9be381a Mon Sep 17 00:00:00 2001 From: Scott Leberknight <174812+sleberknight@users.noreply.github.com> Date: Mon, 1 Jul 2024 16:33:24 -0400 Subject: [PATCH] Add new collection argument check methods in KiwiPreconditions (#1157) * Add overloaded checkArgumentContainsOnlyNotNull methods in KiwiPreconditions to check collection contains only non-null elements * Add overloaded checkArgumentContainsOnlyNotBlank methods in KiwiPreconditions to check that a collection of String contains only non-blank elements Closes #1151 Closes #1152 --- .../kiwiproject/base/KiwiPreconditions.java | 132 +++++++- .../base/KiwiPreconditionsTest.java | 282 ++++++++++++++++++ 2 files changed, 413 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/kiwiproject/base/KiwiPreconditions.java b/src/main/java/org/kiwiproject/base/KiwiPreconditions.java index 0127fb4f..7ceab542 100644 --- a/src/main/java/org/kiwiproject/base/KiwiPreconditions.java +++ b/src/main/java/org/kiwiproject/base/KiwiPreconditions.java @@ -15,9 +15,11 @@ import com.google.common.base.Preconditions; import lombok.SneakyThrows; import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.StringUtils; import java.util.Collection; import java.util.Map; +import java.util.Objects; import java.util.function.IntSupplier; import java.util.function.Supplier; @@ -424,6 +426,134 @@ public static void checkArgumentNotEmpty(Collection collection, } } + /** + * Ensures that the collection passed as a parameter to the calling method is not null or empty, + * and that none of its elements are null. + *

+ * Throws an {@link IllegalArgumentException} if the collection is null or empty, + * or if any elements are null. + * + * @param collection a collection, possibly null + * @param the type of object in the collection + */ + public static void checkArgumentContainsOnlyNotNull(Collection collection) { + checkCollectionNotNullOrEmpty(collection); + var anyNull = anyNullElementsIn(collection); + Preconditions.checkArgument(!anyNull, "collection must not contain null elements"); + } + + /** + * Ensures that the collection passed as a parameter to the calling method is not null or empty, + * and that none of its elements are null. + *

+ * Throws an {@link IllegalArgumentException} if the collection is null or empty, + * or if any elements are null. + * + * @param collection a collection, possibly null + * @param errorMessage the error message for the exception + * @param the type of object in the collection + */ + public static void checkArgumentContainsOnlyNotNull(Collection collection, + String errorMessage) { + checkCollectionNotNullOrEmpty(collection); + var anyNull = anyNullElementsIn(collection); + Preconditions.checkArgument(!anyNull, errorMessage); + } + + /** + * Ensures that the collection passed as a parameter to the calling method is not null or empty, + * and that none of its elements are null. + *

+ * Throws an {@link IllegalArgumentException} if the collection is null or empty, + * or if any elements are null. + * + * @param collection a collection, possibly null + * @param errorMessageTemplate a template for the exception message should the check fail, according to how + * {@link KiwiStrings#format(String, Object...)} handles placeholders + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to Strings using {@link String#valueOf(Object)}. + * @param the type of object in the collection + */ + public static void checkArgumentContainsOnlyNotNull(Collection collection, + String errorMessageTemplate, + Object... errorMessageArgs) { + checkCollectionNotNullOrEmpty(collection); + if (anyNullElementsIn(collection)) { + throw newIllegalArgumentException(errorMessageTemplate, errorMessageArgs); + } + } + + // This uses anyMatch to return as soon as a null element is found. + private static boolean anyNullElementsIn(Collection collection) { + return collection.stream().anyMatch(Objects::isNull); + } + + /** + * Ensures that the collection passed as a parameter to the calling method is not null or empty, + * and that none of its elements are blank strings. + *

+ * Throws an {@link IllegalArgumentException} if the collection is null or empty, + * or if any elements are blank strings. + * + * @param collection a collection, possibly null + * @implNote uses {@link StringUtils#isBlank(CharSequence)} to check for blank elements + */ + public static void checkArgumentContainsOnlyNotBlank(Collection collection) { + checkCollectionNotNullOrEmpty(collection); + var anyBlank = anyBlankElementsIn(collection); + Preconditions.checkArgument(!anyBlank, "collection must not contain blank elements"); + } + + /** + * Ensures that the collection passed as a parameter to the calling method is not null or empty, + * and that none of its elements are blank strings. + *

+ * Throws an {@link IllegalArgumentException} if the collection is null or empty, + * or if any elements are blank strings. + * + * @param collection a collection, possibly null + * @param errorMessage the error message for the exception + * @implNote uses {@link StringUtils#isBlank(CharSequence)} to check for blank elements + */ + public static void checkArgumentContainsOnlyNotBlank(Collection collection, + String errorMessage) { + checkCollectionNotNullOrEmpty(collection); + var anyBlank = anyBlankElementsIn(collection); + Preconditions.checkArgument(!anyBlank, errorMessage); + } + + /** + * Ensures that the collection passed as a parameter to the calling method is not null or empty, + * and that none of its elements are blank strings. + *

+ * Throws an {@link IllegalArgumentException} if the collection is null or empty, + * or if any elements are blank strings. + * + * @param collection a collection, possibly null + * @param errorMessageTemplate a template for the exception message should the check fail, according to how + * {@link KiwiStrings#format(String, Object...)} handles placeholders + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to Strings using {@link String#valueOf(Object)}. + * @implNote uses {@link StringUtils#isBlank(CharSequence)} to check for blank elements + */ + public static void checkArgumentContainsOnlyNotBlank(Collection collection, + String errorMessageTemplate, + Object... errorMessageArgs) { + checkCollectionNotNullOrEmpty(collection); + if (anyBlankElementsIn(collection)) { + throw newIllegalArgumentException(errorMessageTemplate, errorMessageArgs); + } + } + + private static void checkCollectionNotNullOrEmpty(Collection collection) { + checkArgumentNotEmpty(collection, "collection must not be null or empty"); + } + + // This uses anyMatch to return as soon as a blank element is found. + private static boolean anyBlankElementsIn(Collection collection) { + return collection.stream().anyMatch(StringUtils::isBlank); + } + /** * Ensures that the map passed as a parameter to the calling method is not null or empty. * Throws an {@link IllegalArgumentException} if the map is null or empty. @@ -1134,7 +1264,7 @@ private static IllegalArgumentException newIllegalArgumentException(String error } private static IllegalStateException newIllegalStateException(String errorMessageTemplate, - Object... errorMessageArgs) { + Object... errorMessageArgs) { var errorMessage = format(errorMessageTemplate, errorMessageArgs); return new IllegalStateException(errorMessage); } diff --git a/src/test/java/org/kiwiproject/base/KiwiPreconditionsTest.java b/src/test/java/org/kiwiproject/base/KiwiPreconditionsTest.java index a245af3b..6469a3ff 100644 --- a/src/test/java/org/kiwiproject/base/KiwiPreconditionsTest.java +++ b/src/test/java/org/kiwiproject/base/KiwiPreconditionsTest.java @@ -20,6 +20,7 @@ import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -27,6 +28,7 @@ import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; +import org.kiwiproject.collect.KiwiLists; import org.kiwiproject.util.BlankStringSource; import java.math.BigInteger; @@ -497,6 +499,286 @@ void shouldThrowWhenSetArgument_IsNullOrEmpty(Set set) { } } + @Nested + class CheckArgumentContainsOnlyNotNull { + + @Nested + class WithNoMessage { + + @ParameterizedTest + @NullAndEmptySource + void shouldThrowWhenListArgument_IsNullOrEmpty(List list) { + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotNull(list)) + .withMessage("collection must not be null or empty"); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldThrowWhenSetArgument_IsNullOrEmpty(Set set) { + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotNull(set)) + .withMessage("collection must not be null or empty"); + } + + @RepeatedTest(5) + void shouldThrowWhenArgumentContainsNullElement() { + var collection = KiwiLists.shuffledArrayListOf(1, 2, 3, 4, 5, null); + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotNull(collection)) + .withMessage("collection must not contain null elements"); + } + + @RepeatedTest(5) + void shouldNotThrow_WhenListHasAllNonNullElements() { + var list = KiwiLists.shuffledArrayListOf(1, 2, 3, 4, 5, 6); + assertThatCode(() -> KiwiPreconditions.checkArgumentContainsOnlyNotNull(list)) + .doesNotThrowAnyException(); + } + + @RepeatedTest(5) + void shouldNotThrow_WhenSetHasAllNonNullElements() { + var list = Set.of(1, 2, 3, 4, 5, 6); + assertThatCode(() -> KiwiPreconditions.checkArgumentContainsOnlyNotNull(list)) + .doesNotThrowAnyException(); + } + } + + @Nested + class WithMessage { + + @ParameterizedTest + @NullAndEmptySource + void shouldThrowWhenListArgument_IsNullOrEmpty(List list) { + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotNull(list, "will not be used")) + .withMessage("collection must not be null or empty"); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldThrowWhenSetArgument_IsNullOrEmpty(Set set) { + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotNull(set, "will not be used")) + .withMessage("collection must not be null or empty"); + } + + @RepeatedTest(5) + void shouldThrowWhenArgumentContainsNullElement() { + var collection = KiwiLists.shuffledArrayListOf(1, 2, 3, 4, 5, null); + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotNull(collection, "nope, can't have null elements")) + .withMessage("nope, can't have null elements"); + } + + @RepeatedTest(5) + void shouldNotThrow_WhenListHasAllNonNullElements() { + var collection = KiwiLists.shuffledArrayListOf(1, 2, 3, 4, 5, 6); + assertThatCode(() -> KiwiPreconditions.checkArgumentContainsOnlyNotNull(collection, "nope, can't have null elements")) + .doesNotThrowAnyException(); + } + + @RepeatedTest(5) + void shouldNotThrow_WhenSetHasAllNonNullElements() { + var collection = Set.of(1, 2, 3, 4, 5, 6); + assertThatCode(() -> KiwiPreconditions.checkArgumentContainsOnlyNotNull(collection, "nope, can't have null elements")) + .doesNotThrowAnyException(); + } + } + + @Nested + class WithMessageTemplate { + + @ParameterizedTest + @NullAndEmptySource + void shouldThrowWhenListArgument_IsNullOrEmpty(List list) { + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotNull(list, "will not be used %s %s", "arg1", "arg2")) + .withMessage("collection must not be null or empty"); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldThrowWhenSetArgument_IsNullOrEmpty(Set set) { + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotNull(set, "will not be used %s %s", "arg1", "arg2")) + .withMessage("collection must not be null or empty"); + } + + @RepeatedTest(5) + void shouldThrowWhenArgumentContainsNullElement() { + var collection = KiwiLists.shuffledArrayListOf(1, 2, 3, 4, 5, null); + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotNull(collection, "nope, can't have null elements in {} of {}", "ArrayList", "Integer")) + .withMessage("nope, can't have null elements in ArrayList of Integer"); + } + + @RepeatedTest(5) + void shouldNotThrow_WhenListHasAllNonNullElements() { + var collection = KiwiLists.shuffledArrayListOf(1, 2, 3, 4, 5, 6); + assertThatCode(() -> KiwiPreconditions.checkArgumentContainsOnlyNotNull(collection, "nope, can't have null elements in {} of {}", "ArrayList", "Integer")) + .doesNotThrowAnyException(); + } + + @RepeatedTest(5) + void shouldNotThrow_WhenSetHasAllNonNullElements() { + var collection = Set.of(1, 2, 3, 4, 5, 6); + assertThatCode(() -> KiwiPreconditions.checkArgumentContainsOnlyNotNull(collection, "nope, can't have null elements in {} of {}", "ArrayList", "Integer")) + .doesNotThrowAnyException(); + } + } + } + + @Nested + class CheckArgumentContainsOnlyNotBlank { + + @Nested + class WithNoMessage { + + @ParameterizedTest + @NullAndEmptySource + void shouldThrowWhenListArgument_IsNullOrEmpty(List list) { + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotBlank(list)) + .withMessage("collection must not be null or empty"); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldThrowWhenSetArgument_IsNullOrEmpty(Set set) { + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotBlank(set)) + .withMessage("collection must not be null or empty"); + } + + @RepeatedTest(5) + void shouldThrowWhenArgumentContainsBlankElement() { + var blankValue = random().nextBoolean() ? null : ""; + var collection = KiwiLists.shuffledArrayListOf("a", "b", "c", "d", blankValue); + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotBlank(collection)) + .withMessage("collection must not contain blank elements"); + } + + @RepeatedTest(5) + void shouldNotThrow_WhenListHasAllNonBlankElements() { + var collection = KiwiLists.shuffledArrayListOf("a", "b", "c", "d", "e"); + assertThatCode(() -> KiwiPreconditions.checkArgumentContainsOnlyNotBlank(collection)) + .doesNotThrowAnyException(); + } + + @RepeatedTest(5) + void shouldNotThrow_WhenSetHasAllNonBlankElements() { + var collection = Set.of("a", "b", "c", "d", "e"); + assertThatCode(() -> KiwiPreconditions.checkArgumentContainsOnlyNotBlank(collection)) + .doesNotThrowAnyException(); + } + } + + @Nested + class WithMessage { + + @ParameterizedTest + @NullAndEmptySource + void shouldThrowWhenListArgument_IsNullOrEmpty(List list) { + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotBlank(list, "won't be used")) + .withMessage("collection must not be null or empty"); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldThrowWhenSetArgument_IsNullOrEmpty(Set set) { + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotBlank(set, "won't be used")) + .withMessage("collection must not be null or empty"); + } + + @RepeatedTest(5) + void shouldThrowWhenArgumentContainsBlankElement() { + var blankValue = random().nextBoolean() ? null : ""; + var collection = KiwiLists.shuffledArrayListOf("a", "b", "c", "d", blankValue); + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotBlank(collection, "nope, no blanks!")) + .withMessage("nope, no blanks!"); + } + + @RepeatedTest(5) + void shouldNotThrow_WhenListHasAllNonBlankElements() { + var collection = KiwiLists.shuffledArrayListOf("a", "b", "c", "d", "e"); + assertThatCode(() -> KiwiPreconditions.checkArgumentContainsOnlyNotBlank(collection, "nope, no blanks!")) + .doesNotThrowAnyException(); + } + + @RepeatedTest(5) + void shouldNotThrow_WhenSetHasAllNonBlankElements() { + var collection = Set.of("a", "b", "c", "d", "e"); + assertThatCode(() -> KiwiPreconditions.checkArgumentContainsOnlyNotBlank(collection, "nope, no blanks!")) + .doesNotThrowAnyException(); + } } + + @Nested + class WithMessageTemplate { + + @ParameterizedTest + @NullAndEmptySource + void shouldThrowWhenListArgument_IsNullOrEmpty(List list) { + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotBlank(list, "won't be used for {} of String", "List")) + .withMessage("collection must not be null or empty"); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldThrowWhenSetArgument_IsNullOrEmpty(Set set) { + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotBlank(set, "won't be used for {} of String", "Set")) + .withMessage("collection must not be null or empty"); + } + + @RepeatedTest(5) + void shouldThrowWhenArgumentContainsBlankElement() { + var blankValue = random().nextBoolean() ? null : ""; + var collection = KiwiLists.shuffledArrayListOf("a", "b", "c", "d", blankValue); + assertThatIllegalArgumentException() + .isThrownBy(() -> + KiwiPreconditions.checkArgumentContainsOnlyNotBlank(collection, "nope, no blanks in {} of String!", "List")) + .withMessage("nope, no blanks in List of String!"); + } + + @RepeatedTest(5) + void shouldNotThrow_WhenListHasAllNonBlankElements() { + var collection = KiwiLists.shuffledArrayListOf("a", "b", "c", "d", "e"); + assertThatCode(() -> KiwiPreconditions.checkArgumentContainsOnlyNotBlank(collection, "nope, no blanks in {} of String!", "List")) + .doesNotThrowAnyException(); + } + + @RepeatedTest(5) + void shouldNotThrow_WhenSetHasAllNonBlankElements() { + var collection = Set.of("a", "b", "c", "d", "e"); + assertThatCode(() -> KiwiPreconditions.checkArgumentContainsOnlyNotBlank(collection, "nope, no blanks in {} of String!", "Set")) + .doesNotThrowAnyException(); + } + } + } + @Nested class CheckMapArgumentNotEmpty {