diff --git a/dough-common/src/main/java/io/github/bakedlibs/dough/common/Validate.java b/dough-common/src/main/java/io/github/bakedlibs/dough/common/Validate.java new file mode 100644 index 00000000..c4145fb0 --- /dev/null +++ b/dough-common/src/main/java/io/github/bakedlibs/dough/common/Validate.java @@ -0,0 +1,722 @@ +/* + * 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 io.github.bakedlibs.dough.common; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Pattern; + +/** + *
This class is modified from Apache's commons-lang3 package. + * Removed default messages.
+ * + *This class assists in validating arguments. The validation methods are + * based along the following principles: + *
All exceptions messages are + * format strings + * as defined by the Java platform. For example:
+ * + *+ * Validate.isTrue(i > 0, "The value must be greater than zero: %d", i); + * Validate.notNull(surname, "The surname must not be %s", null); + *+ * + *
#ThreadSafe#
+ * @see java.lang.String#format(String, Object...) + */ +@SuppressWarnings("unused") +public class Validate { + /** + * Constructor. This class should not normally be instantiated. + */ + private Validate() { + throw new UnsupportedOperationException("Utility class"); + } + + // isTrue + //--------------------------------------------------------------------------------- + + /** + *Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.
+ * + *Validate.isTrue(i > 0.0, "The value must be greater than zero: %d", i);+ * + *
For performance reasons, the long value is passed as a separate parameter and + * appended to the exception message only in the case of an error.
+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param value the value to append to the message when invalid + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean, String, double) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(final boolean expression, final String message, final long value) { + if (!expression) { + throw new IllegalArgumentException(String.format(message, Long.valueOf(value))); + } + } + + /** + *Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.
+ * + *Validate.isTrue(d > 0.0, "The value must be greater than zero: %s", d);+ * + *
For performance reasons, the double value is passed as a separate parameter and + * appended to the exception message only in the case of an error.
+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param value the value to append to the message when invalid + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(final boolean expression, final String message, final double value) { + if (!expression) { + throw new IllegalArgumentException(String.format(message, Double.valueOf(value))); + } + } + + /** + *Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.
+ * + *+ * Validate.isTrue(i >= min && i <= max, "The value must be between %d and %d", min, max); + * Validate.isTrue(myObject.isOk(), "The object is not okay");+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, double) + */ + public static void isTrue(final boolean expression, final String message, final Object... values) { + if (!expression) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // notNull + //--------------------------------------------------------------------------------- + + /** + *
Validate that the specified argument is not {@code null}; + * otherwise throwing an exception with the specified message. + * + *
Validate.notNull(myObject, "The object must not be null");+ * + * @param
Validate that the specified argument array is neither {@code null} + * nor a length of zero (no elements); otherwise throwing an exception + * with the specified message. + * + *
Validate.notEmpty(myArray, "The array must not be empty");+ * + * @param
Validate that the specified argument collection is neither {@code null} + * nor a size of zero (no elements); otherwise throwing an exception + * with the specified message. + * + *
Validate.notEmpty(myCollection, "The collection must not be empty");+ * + * @param
Validate that the specified argument map is neither {@code null} + * nor a size of zero (no elements); otherwise throwing an exception + * with the specified message. + * + *
Validate.notEmpty(myMap, "The map must not be empty");+ * + * @param
Validate that the specified argument character sequence is + * neither {@code null} nor a length of zero (no characters); + * otherwise throwing an exception with the specified message. + * + *
Validate.notEmpty(myString, "The string must not be empty");+ * + * @param
Validate that the specified argument character sequence is + * neither {@code null}, a length of zero (no characters), empty + * nor whitespace; otherwise throwing an exception with the specified + * message. + * + *
Validate.notBlank(myString, "The string must not be blank");+ * + * @param
Validate that the specified argument array is neither + * {@code null} nor contains any elements that are {@code null}; + * otherwise throwing an exception with the specified message. + * + *
Validate.noNullElements(myArray, "The array contain null at position %d");+ * + *
If the array is {@code null}, then the message in the exception + * is "The validated object is null".
+ * + *If the array has a {@code null} element, then the iteration + * index of the invalid element is appended to the {@code values} + * argument.
+ * + * @paramValidate that the specified argument iterable is neither + * {@code null} nor contains any elements that are {@code null}; + * otherwise throwing an exception with the specified message. + * + *
Validate.noNullElements(myCollection, "The collection contains null at position %d");+ * + *
If the iterable is {@code null}, then the message in the exception + * is "The validated object is null".
+ * + *If the iterable has a {@code null} element, then the iteration + * index of the invalid element is appended to the {@code values} + * argument.
+ * + * @paramValidates that the index is within the bounds of the argument + * array; otherwise throwing an exception with the specified message.
+ * + *Validate.validIndex(myArray, 2, "The array index is invalid: ");+ * + *
If the array is {@code null}, then the message of the exception + * is "The validated object is null".
+ * + * @paramValidates that the index is within the bounds of the argument + * collection; otherwise throwing an exception with the specified message.
+ * + *Validate.validIndex(myCollection, 2, "The collection index is invalid: ");+ * + *
If the collection is {@code null}, then the message of the + * exception is "The validated object is null".
+ * + * @paramValidates that the index is within the bounds of the argument + * character sequence; otherwise throwing an exception with the + * specified message.
+ * + *Validate.validIndex(myStr, 2, "The string index is invalid: ");+ * + *
If the character sequence is {@code null}, then the message + * of the exception is "The validated object is null".
+ * + * @paramValidate that the stateful condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.
+ * + *Validate.validState(this.isOk(), "The state is not OK: %s", myObject);+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalStateException if expression is {@code false} + */ + public static void validState(final boolean expression, final String message, final Object... values) { + if (!expression) { + throw new IllegalStateException(String.format(message, values)); + } + } + + // matchesPattern + //--------------------------------------------------------------------------------- + + /** + *
Validate that the specified argument character sequence matches the specified regular + * expression pattern; otherwise throwing an exception with the specified message.
+ * + *Validate.matchesPattern("hi", "[a-z]*", "%s does not match %s", "hi" "[a-z]*");+ * + *
The syntax of the pattern is the one used in the {@link Pattern} class.
+ * + * @param input the character sequence to validate, not null + * @param pattern the regular expression pattern, not null + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if the character sequence does not match the pattern + */ + public static void matchesPattern(final CharSequence input, final String pattern, final String message, final Object... values) { + // TODO when breaking BC, consider returning input + if (!Pattern.matches(pattern, input)) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // notNaN + //--------------------------------------------------------------------------------- + + /** + *Validates that the specified argument is not {@code NaN}; otherwise + * throwing an exception with the specified message.
+ * + *Validate.notNaN(myDouble, "The value must be a number");+ * + * @param value the value to validate + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message + * @throws IllegalArgumentException if the value is not a number + */ + public static void notNaN(final double value, final String message, final Object... values) { + if (Double.isNaN(value)) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // finite + //--------------------------------------------------------------------------------- + + /** + *
Validates that the specified argument is not infinite or {@code NaN}; + * otherwise throwing an exception with the specified message.
+ * + *Validate.finite(myDouble, "The argument must contain a numeric value");+ * + * @param value the value to validate + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message + * @throws IllegalArgumentException if the value is infinite or {@code NaN} + */ + public static void finite(final double value, final String message, final Object... values) { + if (Double.isNaN(value) || Double.isInfinite(value)) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // inclusiveBetween + //--------------------------------------------------------------------------------- + + /** + *
Validate that the specified argument object fall between the two + * inclusive values specified; otherwise, throws an exception with the + * specified message.
+ * + *Validate.inclusiveBetween(0, 2, 1, "Not in boundaries");+ * + * @param
Validate.inclusiveBetween(0, 2, 1, "Not in range");+ * + * @param start the inclusive start value + * @param end the inclusive end value + * @param value the value to validate + * @param message the exception message if invalid, not null + * + * @throws IllegalArgumentException if the value falls outside the boundaries + */ + public static void inclusiveBetween(final long start, final long end, final long value, final String message) { + // TODO when breaking BC, consider returning value + if (value < start || value > end) { + throw new IllegalArgumentException(message); + } + } + + /** + * Validate that the specified primitive value falls between the two + * inclusive values specified; otherwise, throws an exception with the + * specified message. + * + *
Validate.inclusiveBetween(0.1, 2.1, 1.1, "Not in range");+ * + * @param start the inclusive start value + * @param end the inclusive end value + * @param value the value to validate + * @param message the exception message if invalid, not null + * + * @throws IllegalArgumentException if the value falls outside the boundaries + */ + public static void inclusiveBetween(final double start, final double end, final double value, final String message) { + // TODO when breaking BC, consider returning value + if (value < start || value > end) { + throw new IllegalArgumentException(message); + } + } + + // exclusiveBetween + //--------------------------------------------------------------------------------- + + /** + *
Validate that the specified argument object fall between the two + * exclusive values specified; otherwise, throws an exception with the + * specified message.
+ * + *Validate.exclusiveBetween(0, 2, 1, "Not in boundaries");+ * + * @param
Validate.exclusiveBetween(0, 2, 1, "Not in range");+ * + * @param start the exclusive start value + * @param end the exclusive end value + * @param value the value to validate + * @param message the exception message if invalid, not null + * + * @throws IllegalArgumentException if the value falls outside the boundaries + */ + public static void exclusiveBetween(final long start, final long end, final long value, final String message) { + // TODO when breaking BC, consider returning value + if (value <= start || value >= end) { + throw new IllegalArgumentException(message); + } + } + + /** + * Validate that the specified primitive value falls between the two + * exclusive values specified; otherwise, throws an exception with the + * specified message. + * + *
Validate.exclusiveBetween(0.1, 2.1, 1.1, "Not in range");+ * + * @param start the exclusive start value + * @param end the exclusive end value + * @param value the value to validate + * @param message the exception message if invalid, not null + * + * @throws IllegalArgumentException if the value falls outside the boundaries + */ + public static void exclusiveBetween(final double start, final double end, final double value, final String message) { + // TODO when breaking BC, consider returning value + if (value <= start || value >= end) { + throw new IllegalArgumentException(message); + } + } + + // isInstanceOf + //--------------------------------------------------------------------------------- + + /** + *
Validate that the argument is an instance of the specified class; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary class
+ * + *Validate.isInstanceOf(OkClass.class, object, "Wrong class, object is of class %s", + * object.getClass().getName());+ * + * @param type the class the object must be validated against, not null + * @param obj the object to check, null throws an exception + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if argument is not of specified class + */ + public static void isInstanceOf(final Class> type, final Object obj, final String message, final Object... values) { + // TODO when breaking BC, consider returning obj + if (!type.isInstance(obj)) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // isAssignableFrom + //--------------------------------------------------------------------------------- + + /** + * Validates that the argument can be converted to the specified class, if not throws an exception. + * + *
This method is useful when validating if there will be no casting errors.
+ * + *Validate.isAssignableFrom(SuperClass.class, object.getClass());+ * + *
The message of the exception is "The validated object can not be converted to the" + * followed by the name of the class and "class"
+ * + * @param superType the class the class must be validated against, not null + * @param type the class to check, not null + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if argument can not be converted to the specified class + */ + public static void isAssignableFrom(final Class> superType, final Class> type, final String message, final Object... values) { + // TODO when breaking BC, consider returning type + if (!superType.isAssignableFrom(type)) { + throw new IllegalArgumentException(String.format(message, values)); + } + } +}