com.io7m.jaffirm.core.Preconditions Maven / Gradle / Ivy
/*
* Copyright © 2016 Mark Raynsford https://www.io7m.com
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package com.io7m.jaffirm.core;
import com.io7m.junreachable.UnreachableCodeException;
import java.util.function.DoubleFunction;
import java.util.function.DoublePredicate;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.IntPredicate;
import java.util.function.LongFunction;
import java.util.function.LongPredicate;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static com.io7m.jaffirm.core.SafeApplication.applyDescriberChecked;
import static com.io7m.jaffirm.core.SafeApplication.applyDescriberDChecked;
import static com.io7m.jaffirm.core.SafeApplication.applyDescriberIChecked;
import static com.io7m.jaffirm.core.SafeApplication.applyDescriberLChecked;
import static com.io7m.jaffirm.core.SafeApplication.applySupplierChecked;
import static com.io7m.jaffirm.core.SafeApplication.failedPredicate;
import static com.io7m.jaffirm.core.Violations.innerCheckAll;
import static com.io7m.jaffirm.core.Violations.innerCheckAllDouble;
import static com.io7m.jaffirm.core.Violations.innerCheckAllInt;
import static com.io7m.jaffirm.core.Violations.innerCheckAllLong;
import static com.io7m.jaffirm.core.Violations.singleViolation;
/**
* Functions to check preconditions.
*/
public final class Preconditions
{
private Preconditions()
{
throw new UnreachableCodeException();
}
/**
* Evaluate all of the given {@code conditions} using {@code value} as
* input.
*
* All of the conditions are evaluated and the function throws {@link
* PreconditionViolationException} if any of the conditions are false, or
* raise an exception that is not of type {@link Error}. Exceptions of type
* {@link Error} are propagated immediately, without any further contract
* checking.
*
* @param value The value
* @param conditions The set of conditions
* @param The type of values
*
* @return value
*
* @throws PreconditionViolationException If any of the conditions are false
*/
@SafeVarargs
public static T checkPreconditions(
final T value,
final ContractConditionType... conditions)
throws PreconditionViolationException
{
final Violations violations = innerCheckAll(value, conditions);
if (violations != null) {
throw new PreconditionViolationException(
failedMessage(value, violations), null, violations.count());
}
return value;
}
/**
* An {@code int} specialized version of {@link #checkPreconditions(Object,
* ContractConditionType[])}
*
* @param value The value
* @param conditions The conditions the value must obey
*
* @return value
*
* @throws PreconditionViolationException If any of the conditions are false
*/
public static int checkPreconditionsI(
final int value,
final ContractIntConditionType... conditions)
throws PreconditionViolationException
{
final Violations violations = innerCheckAllInt(value, conditions);
if (violations != null) {
throw new PreconditionViolationException(
failedMessage(Integer.valueOf(value), violations), null, violations.count());
}
return value;
}
/**
* A {@code long} specialized version of {@link #checkPreconditions(Object,
* ContractConditionType[])}
*
* @param value The value
* @param conditions The conditions the value must obey
*
* @return value
*
* @throws PreconditionViolationException If any of the conditions are false
*/
public static long checkPreconditionsL(
final long value,
final ContractLongConditionType... conditions)
throws PreconditionViolationException
{
final Violations violations = innerCheckAllLong(value, conditions);
if (violations != null) {
throw new PreconditionViolationException(
failedMessage(Long.valueOf(value), violations), null, violations.count());
}
return value;
}
/**
* A {@code double} specialized version of {@link #checkPreconditions(Object,
* ContractConditionType[])}
*
* @param value The value
* @param conditions The conditions the value must obey
*
* @return value
*
* @throws PreconditionViolationException If any of the conditions are false
*/
public static double checkPreconditionsD(
final double value,
final ContractDoubleConditionType... conditions)
throws PreconditionViolationException
{
final Violations violations = innerCheckAllDouble(value, conditions);
if (violations != null) {
throw new PreconditionViolationException(
failedMessage(Double.valueOf(value), violations), null, violations.count());
}
return value;
}
/**
* Evaluate the given {@code predicate} using {@code value} as input.
*
* The function throws {@link PreconditionViolationException} if the
* predicate is false.
*
* @param value The value
* @param condition The predicate
* @param The type of values
*
* @return value
*
* @throws PreconditionViolationException If the predicate is false
*/
public static T checkPrecondition(
final T value,
final ContractConditionType condition)
throws PreconditionViolationException
{
return checkPrecondition(
value, condition.predicate(), condition.describer());
}
/**
* Evaluate the given {@code predicate} using {@code value} as input.
*
* The function throws {@link PreconditionViolationException} if the
* predicate is false.
*
* @param value The value
* @param predicate The predicate
* @param describer A describer for the predicate
* @param The type of values
*
* @return value
*
* @throws PreconditionViolationException If the predicate is false
*/
public static T checkPrecondition(
final T value,
final Predicate predicate,
final Function describer)
{
final boolean ok;
try {
ok = predicate.test(value);
} catch (final Throwable e) {
final Violations violations = singleViolation(failedPredicate(e));
throw new PreconditionViolationException(
failedMessage(value, violations), e, violations.count());
}
return innerCheck(value, ok, describer);
}
/**
* Evaluate the given {@code predicate} using {@code value} as input.
*
* The function throws {@link PreconditionViolationException} if the
* predicate is false.
*
* @param value The value
* @param condition The predicate
* @param describer A describer for the predicate
* @param The type of values
*
* @return value
*
* @throws PreconditionViolationException If the predicate is false
*/
public static T checkPrecondition(
final T value,
final boolean condition,
final Function describer)
{
return innerCheck(value, condition, describer);
}
/**
* A specialized version of {@link #checkPrecondition(Object, boolean,
* Function)} that does not mention an input value.
*
* @param condition The predicate
* @param message The predicate description
*
* @throws PreconditionViolationException Iff {@code predicate == false}
*/
public static void checkPrecondition(
final boolean condition,
final String message)
throws PreconditionViolationException
{
if (!condition) {
final Violations violations = singleViolation(message);
throw new PreconditionViolationException(
failedMessage("", violations), null, violations.count());
}
}
/**
* A specialized version of {@link #checkPrecondition(Object, boolean,
* Function)} that does not mention an input value.
*
* @param condition The predicate
* @param message The predicate description supplier
*
* @throws PreconditionViolationException Iff {@code predicate == false}
*/
public static void checkPrecondition(
final boolean condition,
final Supplier message)
throws PreconditionViolationException
{
if (!condition) {
final Violations violations = singleViolation(applySupplierChecked(message));
throw new PreconditionViolationException(
failedMessage("", violations), null, violations.count());
}
}
/**
* A version of {@link #checkPrecondition(boolean, String)} that constructs
* a description message from the given format string and arguments.
*
* Note that the use of variadic arguments may entail allocating memory on
* virtual machines that fail to eliminate the allocations with escape
* analysis.
*
* @param value The value
* @param condition The predicate
* @param format The format string
* @param objects The format string arguments
* @param The precise type of values
*
* @return {@code value}
*
* @since 1.1.0
*/
public static T checkPreconditionV(
final T value,
final boolean condition,
final String format,
final Object... objects)
{
if (!condition) {
final Violations violations = singleViolation(String.format(format, objects));
throw new PreconditionViolationException(
failedMessage(value, violations), null, violations.count());
}
return value;
}
/**
* A version of {@link #checkPrecondition(boolean, String)} that constructs
* a description message from the given format string and arguments.
*
* Note that the use of variadic arguments may entail allocating memory on
* virtual machines that fail to eliminate the allocations with escape
* analysis.
*
* @param condition The predicate
* @param format The format string
* @param objects The format string arguments
*
* @since 1.1.0
*/
public static void checkPreconditionV(
final boolean condition,
final String format,
final Object... objects)
{
checkPreconditionV("", condition, format, objects);
}
/**
* An {@code int} specialized version of {@link #checkPrecondition(Object,
* ContractConditionType)}.
*
* @param value The value
* @param condition The predicate
*
* @return value
*
* @throws PreconditionViolationException If the predicate is false
*/
public static int checkPreconditionI(
final int value,
final ContractIntConditionType condition)
throws PreconditionViolationException
{
return checkPreconditionI(
value, condition.predicate(), condition.describer());
}
/**
* An {@code int} specialized version of {@link #checkPrecondition(Object,
* ContractConditionType)}.
*
* @param value The value
* @param predicate The predicate
* @param describer The describer for the predicate
*
* @return value
*
* @throws PreconditionViolationException If the predicate is false
*/
public static int checkPreconditionI(
final int value,
final IntPredicate predicate,
final IntFunction describer)
{
final boolean ok;
try {
ok = predicate.test(value);
} catch (final Throwable e) {
final Violations violations = singleViolation(failedPredicate(e));
throw new PreconditionViolationException(
failedMessage(Integer.valueOf(value), violations), e, violations.count());
}
return innerCheckI(value, ok, describer);
}
/**
* An {@code int} specialized version of {@link #checkPrecondition(Object,
* boolean, Function)}.
*
* @param value The value
* @param condition The predicate
* @param describer The describer for the predicate
*
* @return value
*
* @throws PreconditionViolationException If the predicate is false
*/
public static int checkPreconditionI(
final int value,
final boolean condition,
final IntFunction describer)
{
return innerCheckI(value, condition, describer);
}
/**
* A {@code long} specialized version of {@link #checkPrecondition(Object,
* ContractConditionType)}.
*
* @param value The value
* @param condition The predicate
*
* @return value
*
* @throws PreconditionViolationException If the predicate is false
*/
public static long checkPreconditionL(
final long value,
final ContractLongConditionType condition)
throws PreconditionViolationException
{
return checkPreconditionL(
value, condition.predicate(), condition.describer());
}
/**
* A {@code long} specialized version of {@link #checkPrecondition(Object,
* Predicate, Function)}
*
* @param value The value
* @param predicate The predicate
* @param describer The describer of the predicate
*
* @return value
*
* @throws PreconditionViolationException If the predicate is false
*/
public static long checkPreconditionL(
final long value,
final LongPredicate predicate,
final LongFunction describer)
{
final boolean ok;
try {
ok = predicate.test(value);
} catch (final Throwable e) {
final Violations violations = singleViolation(failedPredicate(e));
throw new PreconditionViolationException(
failedMessage(Long.valueOf(value), violations), e, violations.count());
}
return innerCheckL(value, ok, describer);
}
/**
* A {@code long} specialized version of {@link #checkPrecondition(Object,
* Predicate, Function)}
*
* @param condition The predicate
* @param value The value
* @param describer The describer of the predicate
*
* @return value
*
* @throws PreconditionViolationException If the predicate is false
*/
public static long checkPreconditionL(
final long value,
final boolean condition,
final LongFunction describer)
{
return innerCheckL(value, condition, describer);
}
/**
* A {@code double} specialized version of {@link #checkPrecondition(Object,
* ContractConditionType)}.
*
* @param value The value
* @param condition The predicate
*
* @return value
*
* @throws PreconditionViolationException If the predicate is false
*/
public static double checkPreconditionD(
final double value,
final ContractDoubleConditionType condition)
throws PreconditionViolationException
{
return checkPreconditionD(
value, condition.predicate(), condition.describer());
}
/**
* A {@code double} specialized version of {@link #checkPrecondition(Object,
* Predicate, Function)}
*
* @param value The value
* @param predicate The predicate
* @param describer The describer of the predicate
*
* @return value
*
* @throws PreconditionViolationException If the predicate is false
*/
public static double checkPreconditionD(
final double value,
final DoublePredicate predicate,
final DoubleFunction describer)
{
final boolean ok;
try {
ok = predicate.test(value);
} catch (final Throwable e) {
final Violations violations = singleViolation(failedPredicate(e));
throw new PreconditionViolationException(
failedMessage(Double.valueOf(value), violations), e, violations.count());
}
return innerCheckD(value, ok, describer);
}
/**
* A {@code double} specialized version of {@link #checkPrecondition(Object,
* boolean, Function)}
*
* @param value The value
* @param condition The predicate
* @param describer The describer of the predicate
*
* @return value
*
* @throws PreconditionViolationException If the predicate is false
*/
public static double checkPreconditionD(
final double value,
final boolean condition,
final DoubleFunction describer)
{
return innerCheckD(value, condition, describer);
}
private static T innerCheck(
final T value,
final boolean condition,
final Function describer)
{
if (!condition) {
final Violations violations = singleViolation(applyDescriberChecked(value, describer));
throw new PreconditionViolationException(
failedMessage(value, violations), null, violations.count());
}
return value;
}
private static int innerCheckI(
final int value,
final boolean condition,
final IntFunction describer)
{
if (!condition) {
final Violations violations = singleViolation(applyDescriberIChecked(value, describer));
throw new PreconditionViolationException(
failedMessage(Integer.valueOf(value), violations), null, violations.count());
}
return value;
}
private static long innerCheckL(
final long value,
final boolean condition,
final LongFunction describer)
{
if (!condition) {
final Violations violations = singleViolation(applyDescriberLChecked(value, describer));
throw new PreconditionViolationException(
failedMessage(Long.valueOf(value), violations), null, violations.count());
}
return value;
}
private static double innerCheckD(
final double value,
final boolean condition,
final DoubleFunction describer)
{
if (!condition) {
final Violations violations = singleViolation(applyDescriberDChecked(value, describer));
throw new PreconditionViolationException(
failedMessage(Double.valueOf(value), violations), null, violations.count());
}
return value;
}
private static String failedMessage(
final T value,
final Violations violations)
{
final String line_separator = System.lineSeparator();
final StringBuilder sb = new StringBuilder(128);
sb.append("Precondition violation.");
sb.append(line_separator);
sb.append(" Received: ");
sb.append(value);
sb.append(line_separator);
sb.append(" Violated conditions: ");
sb.append(line_separator);
final String[] messages = violations.messages();
for (int index = 0; index < messages.length; ++index) {
if (messages[index] != null) {
sb.append(" [");
sb.append(index);
sb.append("]: ");
sb.append(messages[index]);
sb.append(line_separator);
}
}
return sb.toString();
}
}