Check.java
package org.klojang.check;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import static org.klojang.check.x.msg.CustomMsgFormatter.formatWithUserArgs;
/**
* The central class of this Java module. All checks start out here. The
* {@code Check} class provides static factory methods for {@link IntCheck} and
* {@link ObjectCheck} instances, which do the actual orchestration of the checks to
* be executed. The {@code Check} class does contain a few validation methods itself,
* like {@link #fromTo(int, int, int) Check.fromTo()} and
* {@link #offsetLength(int, int, int) Check.offsetLength()}. These stand somewhat
* apart from the rest of the Klojang Check. They are included for convenience.
*
* <p>See the <b><a href="https://klojang4j.github.io/klojang-check/index.html">User
* Guide</a></b> for a detailed description of Defensive Programming using Klojang
* Check.
*
* @author Ayco Holleman
*/
public final class Check {
private Check() {
throw new UnsupportedOperationException();
}
private static NullPointerException argumentMustNotBeNull() {
return new NullPointerException("argument must not be null");
}
private static NullPointerException argumentMustNotBeNull(String tag) {
return new NullPointerException(tag + " must not be null");
}
private static final Function<String, IllegalArgumentException> DEF_EXC_FACTORY =
CommonExceptions.ARGUMENT;
/**
* Static factory method. Returns an {@link IntCheck} instance suitable for testing
* {@code int} values.
*
* @param value the value to be validated
* @return an {@code IntCheck} instance suitable for testing {@code int} values
*/
public static IntCheck<IllegalArgumentException> that(int value) {
return new IntCheck<>(value, null, DEF_EXC_FACTORY);
}
/**
* Static factory method. Returns an {@link ObjectCheck} instance suitable for
* validating values of type {@code <T>}.
*
* @param <T> the type of the value to be validated
* @param value the value to be validated
* @return an {@code ObjectCheck} instance suitable for validating values of type
* {@code <T>}.
*/
public static <T> ObjectCheck<T, IllegalArgumentException> that(T value) {
return new ObjectCheck<>(value, null, DEF_EXC_FACTORY);
}
/**
* Static factory method. Returns an {@link IntCheck} instance suitable for
* validating {@code int} values.
*
* @param value the value to be validated
* @param tag a descriptive name for the value (in case the value is a method
* argument probably something close to the parameter name)
* @return an {@code IntCheck} instance suitable for testing {@code int} values
*/
public static IntCheck<IllegalArgumentException> that(int value, String tag) {
return new IntCheck<>(value, tag, DEF_EXC_FACTORY);
}
/**
* Static factory method. Returns an {@link ObjectCheck} instance suitable for
* validating values of type {@code <T>}.
*
* @param <T> the type of the value to be validated
* @param value the value to be validated
* @param tag a descriptive name for the value (in case the value is a method
* argument probably something close to the parameter name)
* @return an {@code ObjectCheck} instance suitable for validating values of type
* {@code <T>}.
*/
public static <T> ObjectCheck<T, IllegalArgumentException> that(T value,
String tag) {
return new ObjectCheck<>(value, tag, DEF_EXC_FACTORY);
}
/**
* Static factory method. Returns an {@link ObjectCheck} instance suitable for
* validating values of type {@code <T>} if the specified value is not
* {@code null}, else throws a {@code NullPointerException}.
* {@linkplain CommonChecks#notNull() null test}. An
* {@code IllegalArgumentException} will be thrown if the specified value fails any
* of the <i>subsequently</i> specified checks.
*
* @param <T> the type of the value to be validated
* @param value the value to be validated
* @return an {@code ObjectCheck} instance suitable for validating values of type
* {@code <T>}.
* @throws NullPointerException if the argument is {@code null}
*/
public static <T> ObjectCheck<T, IllegalArgumentException> notNull(T value)
throws NullPointerException {
if (value != null) {
return new ObjectCheck<>(value, null, DEF_EXC_FACTORY);
}
throw argumentMustNotBeNull();
}
/**
* Static factory method. Returns an {@link ObjectCheck} instance suitable for
* validating values of type {@code <T>} if the specified value is not
* {@code null}, else throws a {@code NullPointerException}.
* {@linkplain CommonChecks#notNull() null test}. An
* {@code IllegalArgumentException} will be thrown if the specified value fails any
* of the <i>subsequently</i> specified checks.
*
* @param <T> the type of the value to be validated
* @param value the value to be validated
* @param tag a descriptive name for the value (in case the value is a method
* argument probably something close to the parameter name)
* @return an {@code ObjectCheck} instance suitable for validating values of type
* {@code <T>}.
* @throws NullPointerException if the argument is {@code null}
*/
public static <T> ObjectCheck<T, IllegalArgumentException> notNull(T value,
String tag)
throws NullPointerException {
if (value != null) {
return new ObjectCheck<>(value, tag, DEF_EXC_FACTORY);
}
throw argumentMustNotBeNull(tag);
}
/**
* Static factory method. Returns an {@link IntCheck} instance suitable for
* validating {@code int} values. If the value fails any of the tests passed to the
* {@code IntCheck} instance, the exception produced by the provided exception
* factory is thrown.
*
* @param excFactory a function that will produce the exception if the value
* fails to pass a test. The function will be pass the exception message and
* must return the exception to be thrown
* @param value the value to be validated
* @param <X> the type of {@code Exception} thrown if the value fails to pass a
* test
* @return an {@code IntCheck} instance suitable for testing {@code int} values
*/
public static <X extends Exception> IntCheck<X> on(Function<String, X> excFactory,
int value) {
return new IntCheck<>(value, null, excFactory);
}
/**
* Static factory method. Returns an {@code ObjectCheck} instance suitable for
* validating values of type {@code <T>}. If the value fails any of the tests
* passed to the {@code ObjectCheck} instance, the exception produced by the
* provided exception factory is thrown.
*
* @param <T> the type of the value to be validated
* @param <X> the type of {@code Exception} thrown if the value fails to pass a
* test
* @param excFactory a function that will produce the exception if the value
* fails to pass a test. The function will be pass the exception message and
* must return the exception to be thrown
* @param value the value to be validated
* @return an {@code ObjectCheck} instance suitable for validating values of type
* {@code <T>}.
*/
public static <T, X extends Exception> ObjectCheck<T, X> on(
Function<String, X> excFactory, T value) {
return new ObjectCheck<>(value, null, excFactory);
}
/**
* Static factory method. Returns an {@link IntCheck} instance suitable for
* validating {@code int} values. If the value fails any of the tests passed to the
* {@code IntCheck} instance, the exception produced by the provided exception
* factory is thrown.
*
* @param excFactory a function that will produce the exception if the value
* fails to pass a test. The function will be pass the exception message and
* must return the exception to be thrown
* @param value the value to be validated
* @param tag a descriptive name for the value (in case the value is a method
* argument probably something close to the parameter name)
* @param <X> the type of {@code Exception} thrown if the value fails to pass a
* test
* @return an {@code IntCheck} instance suitable for testing {@code int} values
*/
public static <X extends Exception> IntCheck<X> on(
Function<String, X> excFactory, int value, String tag) {
return new IntCheck<>(value, tag, excFactory);
}
/**
* Static factory method. Returns a new {@code Check} instance suitable for testing
* the provided argument.
*
* @param <T> the type of the value to be validated
* @param <X> the type of {@code Exception} thrown if the value fails to pass a
* test
* @param excFactory a function that will produce the exception if the value
* fails to pass a test. The function will be pass the exception message and
* must return the exception to be thrown
* @param value the value to be validated
* @param tag a descriptive name for the value (in case the value is a method
* argument probably something close to the parameter name)
* @return an {@code ObjectCheck} instance suitable for validating values of type
* {@code <T>}.
*/
public static <T, X extends Exception> ObjectCheck<T, X> on(
Function<String, X> excFactory, T value, String tag) {
return new ObjectCheck<>(value, tag, excFactory);
}
/**
* <p>All-in-one check for the specified array, offset and length.
*
* <ol>
* <li>throws an {@code NullPointerException} if {@code array} is {@code null}.
* <li>throws an {@code IndexOutOfBoundsException} if {@code offset} or {@code length} is negative
* <li>throws an {@code IndexOutOfBoundsException} if {@code offset+length > array.length}
* </ol>
*
* <p><i>NB The {@code fromTo()} and {@code offsetLength()} checks stand somewhat
* apart from the rest of the check framework. They happen through "ordinary"
* static utility methods and they test multiple things at once. They are included
* for convenience.</i>
*
* @param array the array
* @param offset the offset within the array
* @param length the length of the segment
* @see #offsetLength(int, int, int)
* @see java.io.OutputStream#write(byte[], int, int)
* @see java.io.InputStream#read(byte[], int, int)
*/
public static void offsetLength(byte[] array, int offset, int length) {
if (array == null) {
throw argumentMustNotBeNull("array");
}
if ((offset | length) < 0 || offset + length > array.length) {
throw new IndexOutOfBoundsException();
}
}
/**
* <p>All-in-one check for the provided size, offset and length. The {@code size}
* argument supposedly is the size or length of an array or array-like object.
*
* <ol>
* <li>throws an {@code IndexOutOfBoundsException} if {@code size}, {@code offset} or {@code length} is negative
* <li>throws an {@code IndexOutOfBoundsException} if {@code offset+length} > {@code size}
* </ol>
*
* <p><i>NB The {@code fromTo()} and {@code offsetLength()} checks stand somewhat
* apart from the rest of the check framework. They happen through "ordinary"
* static utility methods and they test multiple things at once. They are included
* for convenience.</i>
*
* @param size the length/size of the array or array-like object
* @param offset the offset
* @param length the length of the segment
*/
public static void offsetLength(int size, int offset, int length) {
if ((size | offset | length) < 0 || size < offset + length) {
throw new IndexOutOfBoundsException();
}
}
/**
* <p>All-in-one check for the provided list, from-index and to-index.
*
* <ol>
* <li>Throws a {@code NullPointerException} if {@code list} is {@code null}
* <li>Throws an {@code IndexOutOfBoundsException} if {@code fromIndex < 0}
* <li>Throws an {@code IndexOutOfBoundsException} if {@code toIndex} < {@code fromIndex}
* <li>Throws an {@code IndexOutOfBoundsException} if {@code toIndex} > {@code list.size()}
* </ol>
*
* <p><i>NB The {@code fromTo()} and {@code offsetLength()} checks stand somewhat
* apart from the rest of the check framework. They happen through "ordinary"
* static utility methods and they test multiple things at once. They are included
* for convenience.</i>
*
* @param list the list
* @param fromIndex the start index of the sublist
* @param toIndex the end index of the sublist
* @return the {@code size} of the sublist
* @see #fromTo(int, int, int)
* @see List#subList(int, int)
*/
public static int fromTo(List<?> list, int fromIndex, int toIndex) {
if (list == null) {
throw argumentMustNotBeNull("list");
}
if (fromIndex < 0 || toIndex < fromIndex || list.size() < toIndex) {
throw new IndexOutOfBoundsException(
"from=" + fromIndex + ";to=" + toIndex + ";size=" + list.size());
}
return toIndex - fromIndex;
}
/**
* <p>All-in-one check for the provided array, from-index and to-index.
*
* <ol>
* <li>Throws a {@code NullPointerException} if the array is {@code null}
* <li>Throws an {@code IndexOutOfBoundsException} if {@code fromIndex} or {@code toIndex} is negative
* <li>Throws an {@code IndexOutOfBoundsException} if {@code toIndex < fromIndex}
* <li>Throws an {@code IndexOutOfBoundsException} if {@code toIndex > array.length}
* </ol>
*
* <p><i>NB The {@code fromTo()} and {@code offsetLength()} checks stand somewhat
* apart from the rest of the check framework. They happen through "ordinary"
* static utility methods and they test multiple things at once. They are included
* for convenience.</i>
*
* @param array the array
* @param fromIndex the start index of the array segment
* @param toIndex the end index of the array segment
* @param <T> the type of the array elements
* @return the {@code length} of the array segment
* @see #fromTo(int, int, int)
* @see Arrays#copyOfRange(Object[], int, int)
*/
public static <T> int fromTo(T[] array, int fromIndex, int toIndex) {
if (array == null) {
throw argumentMustNotBeNull("array");
}
if ((fromIndex | toIndex) < 0 || toIndex < fromIndex || array.length < toIndex) {
throw new IndexOutOfBoundsException();
}
return toIndex - fromIndex;
}
/**
* <p>All-in-one check for the provided string, from-index and to-index.
*
* <ol>
* <li>Throws a {@code NullPointerException} if {@code string} is {@code null}
* <li>Throws an {@code IndexOutOfBoundsException} if {@code fromIndex} or {@code toIndex} is negative
* <li>Throws an {@code IndexOutOfBoundsException} if {@code toIndex < fromIndex}
* <li>Throws an {@code IndexOutOfBoundsException} if {@code toIndex > string.length()}
* </ol>
*
* <p><i>NB The {@code fromTo()} and {@code offsetLength()} checks stand somewhat
* apart from the rest of the check framework. They happen through "ordinary"
* static utility methods and they test multiple things at once. They are included
* for convenience.</i>
*
* @param string the string
* @param fromIndex the start index of the substring
* @param toIndex the end index of the substring
* @return the {@code length} of the substring
* @see #fromTo(int, int, int)
* @see String#substring(int, int)
*/
public static int fromTo(String string, int fromIndex, int toIndex) {
if (string == null) {
throw argumentMustNotBeNull("string");
}
if ((fromIndex | toIndex) < 0
|| toIndex < fromIndex
|| string.length() < toIndex) {
throw new IndexOutOfBoundsException();
}
return toIndex - fromIndex;
}
/**
* <p>All-in-one check for the provided size, from-index and to-index. The
* {@code size} argument supposedly is the size or length of an array or array-like
* object.
*
* <ol>
* <li>Throws an {@code IndexOutOfBoundsException} if {@code size} or {@code fromIndex} or {@code toIndex} is negative
* <li>Throws an {@code IndexOutOfBoundsException} if {@code toIndex < fromIndex}
* <li>Throws an {@code IndexOutOfBoundsException} if {@code toIndex > size}
* </ol>
*
* <p><i>NB The {@code fromTo()} and {@code offsetLength()} checks stand somewhat
* apart from the rest of the check framework. They happen through "ordinary"
* static utility methods and they test multiple things at once. They are included
* for convenience.</i>
*
* @param size the size (or length) of the array, string, list, etc.
* @param fromIndex the start index of the segment
* @param toIndex the end index of the segment
* @return the {@code length} of the segment
*/
public static int fromTo(int size, int fromIndex, int toIndex) {
if ((size | fromIndex | toIndex) < 0 || toIndex < fromIndex || size < toIndex) {
throw new IndexOutOfBoundsException();
}
return toIndex - fromIndex;
}
/**
* Always throws an {@code IllegalArgumentException} with the specified message and
* message arguments. The method is still declared to return a value of type
* {@code <T>} so it can be used as the expression for a {@code return} statement.
*
* @param <T> the desired type of the return value
* @param message the message (pattern)
* @param msgArgs the message arguments. The first message argument within the
* message pattern would be {@code ${0}}; the second would be {@code ${1}},
* etc. For more information, see <a
* href="../../../module-summary.html#custom-error-messages">Custom Error
* Messages</a>.
* @return nothing, but allows {@code fail()} to be used as the expression in a
* {@code return} statement
* @throws IllegalArgumentException always
*/
public static <T> T fail(String message, Object... msgArgs)
throws IllegalArgumentException {
return fail(DEF_EXC_FACTORY, message, msgArgs);
}
/**
* Always throws the exception supplied by the specified {@code Supplier}. The
* method is still declared to return a value of type {@code <T>} so it can be used
* as the expression for a {@code return} statement.
*
* @param excFactory the supplier of the exception
* @param <T> the desired type of the return value
* @param <X> the type of the exception
* @return nothing, but allows {@code fail()} to be used as the expression in a
* {@code return} statement
* @throws X always
*/
public static <T, X extends Throwable> T fail(Supplier<X> excFactory) throws X {
throw excFactory.get();
}
/**
* Always throws the exception produced by the specified exception factory with the
* specified message and message arguments. The method is still declared to return
* a value of type {@code <T>} so it can be used as the expression for a
* {@code return} statement.
*
* @param <T> the type of the object that would have been returned if it had
* passed the checks
* @param <X> the type of the exception
* @param excFactory a function that takes a {@code String} (the exception
* message) and produces an {@code Exception}.
* @param message the message
* @param msgArgs the message arguments. The first message argument within the
* message pattern would be {@code ${0}}; the second would be {@code ${1}},
* etc. For more information, see <a
* href="../../../module-summary.html#custom-error-messages">Custom Error
* Messages</a>.
* @return nothing, but allows {@code fail} to be used as the expression in a
* {@code return} statement
* @throws X always
*/
public static <T, X extends Throwable> T fail(
Function<String, X> excFactory, String message, Object... msgArgs) throws X {
if (msgArgs == null || message == null) {
throw excFactory.apply(message);
}
throw excFactory.apply(formatWithUserArgs(message, msgArgs));
}
}