com.io7m.jaux.Constraints Maven / Gradle / Ivy
/*
* Copyright © 2012 http://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.jaux;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
*
* The Constraints class provides functions for constraining values to given
* ranges and for asserting arbitrary propositions.
*
*
*
* Mainly, it's intended as a way to add assertions to code that cannot be
* accidentally turned off.
*
*
*
* It is intended to be used in the constructors of immutable classes. When
* used in this manner, it becomes possible to enforce invariants to some
* degree: If a type T
can only be constructed with a constructor
* C
protected by constraint P
that does not depend
* on any global mutable state, then P
must be true anywhere that
* a value of type T
is present.
*
*/
public final class Constraints
{
/**
* Exception type raised on constraint violation.
*/
public static class ConstraintError extends Throwable
{
private static final long serialVersionUID = 1L;
public ConstraintError(
final @Nonnull String message)
{
super(message);
}
}
/**
* Ensures an arbitrary expression is true. The function throws
* {@link ConstraintError} iff the expression is false.
*
* The function is analogous to an 'assert' expression that cannot be
* removed at runtime.
*
* @param expression
* The expression to be tested.
* @param message
* A humanly readable string of the input expression.
* @throws ConstraintError
* Iff expression == false
.
*/
public static void constrainArbitrary(
final boolean expression,
final @Nonnull String message)
throws ConstraintError
{
if (expression == false) {
throw new ConstraintError("expression '"
+ message
+ "' evaluated to false");
}
}
/**
* Ensures the value n
is less than the value m
.
* The function throws {@link ConstraintError} iff this is not the case. The
* function returns n
.
*
* @return n
.
* @throws ConstraintError
* Iff m >= n
.
*/
public static double constrainLessThan(
final double n,
final double m)
throws ConstraintError
{
return Constraints.constrainLessThan(n, m, null);
}
/**
* Ensures the value n
is less than the value m
.
* The function throws {@link ConstraintError} iff this is not the case. The
* function returns n
.
*
* @return n
.
* @throws ConstraintError
* Iff m >= n
.
*/
public static double constrainLessThan(
final double n,
final double m,
final @Nonnull String name)
throws ConstraintError
{
if ((n < m) == false) {
final @Nonnull String s =
Double.toString(m) + " >= " + Double.toString(n);
throw new ConstraintError(name == null ? s : name + ": " + s);
}
return n;
}
/**
* Ensures the value n
is less than the value m
.
* The function throws {@link ConstraintError} iff this is not the case. The
* function returns n
.
*
* @return n
.
* @throws ConstraintError
* Iff m >= n
.
*/
public static float constrainLessThan(
final float n,
final float m)
throws ConstraintError
{
return Constraints.constrainLessThan(n, m, null);
}
/**
* Ensures the value n
is less than the value m
.
* The function throws {@link ConstraintError} iff this is not the case. The
* function returns n
.
*
* @return n
.
* @throws ConstraintError
* Iff m >= n
.
*/
public static float constrainLessThan(
final float n,
final float m,
final @Nonnull String name)
throws ConstraintError
{
return (float) Constraints
.constrainLessThan((double) n, (double) m, name);
}
/**
* Ensures the value n
is less than the value m
.
* The function throws {@link ConstraintError} iff this is not the case. The
* function returns n
.
*
* @return n
.
* @throws ConstraintError
* Iff m >= n
.
*/
public static int constrainLessThan(
final int n,
final int m)
throws ConstraintError
{
return Constraints.constrainLessThan(n, m, null);
}
/**
* Ensures the value n
is less than the value m
.
* The function throws {@link ConstraintError} iff this is not the case. The
* function returns n
.
*
* @return n
.
* @throws ConstraintError
* Iff m >= n
.
*/
public static int constrainLessThan(
final int n,
final int m,
final @Nonnull String name)
throws ConstraintError
{
return (int) Constraints.constrainLessThan((long) n, (long) m, name);
}
/**
* Ensures the value n
is less than the value m
.
* The function throws {@link ConstraintError} iff this is not the case. The
* function returns n
.
*
* @return n
.
* @throws ConstraintError
* Iff m >= n
.
*/
public static long constrainLessThan(
final long n,
final long m)
throws ConstraintError
{
return Constraints.constrainLessThan(n, m, null);
}
/**
* Ensures the value n
is less than the value m
.
* The function throws {@link ConstraintError} iff this is not the case. The
* function returns n
.
*
* @return n
.
* @throws ConstraintError
* Iff m >= n
.
*/
public static long constrainLessThan(
final long n,
final long m,
final @Nonnull String name)
throws ConstraintError
{
if ((n < m) == false) {
final @Nonnull String s = Long.toString(m) + " >= " + Long.toString(n);
throw new ConstraintError(name == null ? s : name + ": " + s);
}
return n;
}
/**
* Ensures an arbitrary value is not null. The function throws
* {@link ConstraintError} iff the given value is null.
*
* @param value
* The value to be checked.
* @param message
* A humanly readable string description of the input value.
*
* @throws ConstraintError
* Iff value == null
.
*/
public static T constrainNotNull(
final T value,
final @Nonnull String message)
throws ConstraintError
{
if (value == null) {
throw new ConstraintError("value '" + message + "' is null");
}
return value;
}
/**
* Constrains a value n
to the inclusive range
* first
..last
. The function throws
* {@link ConstraintError} iff the value lies outside of this range.
*
* The function returns the input value.
*
* @param n
* The input value.
* @param first
* The lower bound.
* @param last
* The upper bound.
* @return The input value.
* @throws ConstraintError
*/
public static double constrainRange(
final double n,
final double first,
final double last)
throws ConstraintError
{
return Constraints.constrainRange(n, first, last, null);
}
/**
* Constrains a value n
to the inclusive range
* first
..last
. The function throws
* {@link ConstraintError} iff the value lies outside of this range.
*
* The function returns the input value.
*
* @param n
* The input value.
* @param first
* The lower bound.
* @param last
* The upper bound.
* @return The input value.
* @throws ConstraintError
*/
public static double constrainRange(
final double n,
final double first,
final double last,
final @CheckForNull String name)
throws ConstraintError
{
if (n < first) {
final @Nonnull String m =
Double.toString(n) + " < " + Double.toString(first);
throw new ConstraintError(name == null ? m : name + ": " + m);
}
if (n > last) {
final @Nonnull String m =
Double.toString(n) + " > " + Double.toString(last);
throw new ConstraintError(name == null ? m : name + ": " + m);
}
return n;
}
/**
* Constrains a value n
to the inclusive range
* first
..last
. The function throws
* {@link ConstraintError} iff the value lies outside of this range.
*
* The function returns the input value.
*
* @param n
* The input value.
* @param first
* The lower bound.
* @param last
* The upper bound.
* @return The input value.
* @throws ConstraintError
*/
public static float constrainRange(
final float n,
final float first,
final float last)
throws ConstraintError
{
return Constraints.constrainRange(n, first, last, null);
}
/**
* Constrains a value n
to the inclusive range
* first
..last
. The function throws
* {@link ConstraintError} iff the value lies outside of this range.
*
* The function returns the input value.
*
* @param n
* The input value.
* @param first
* The lower bound.
* @param last
* The upper bound.
* @return The input value.
* @throws ConstraintError
*/
public static float constrainRange(
final float n,
final float first,
final float last,
final @CheckForNull String name)
throws ConstraintError
{
if (n < first) {
final @Nonnull String m =
Float.toString(n) + " < " + Float.toString(first);
throw new ConstraintError(name == null ? m : name + ": " + m);
}
if (n > last) {
final @Nonnull String m =
Float.toString(n) + " > " + Float.toString(last);
throw new ConstraintError(name == null ? m : name + ": " + m);
}
return n;
}
/**
* Constrains a value n
to the inclusive range
* first
..last
. The function throws
* {@link ConstraintError} iff the value lies outside of this range.
*
* The function returns the input value.
*
* @param n
* The input value.
* @param first
* The lower bound.
* @param last
* The upper bound.
* @return The input value.
* @throws ConstraintError
*/
public static long constrainRange(
final int n,
final int first,
final int last)
throws ConstraintError
{
return Constraints.constrainRange(n, first, last, null);
}
/**
* Constrains a value n
to the inclusive range
* first
..last
. The function throws
* {@link ConstraintError} iff the value lies outside of this range.
*
* The function returns the input value.
*
* @param n
* The input value.
* @param first
* The lower bound.
* @param last
* The upper bound.
* @return The input value.
* @throws ConstraintError
*/
public static int constrainRange(
final int n,
final int first,
final int last,
final @CheckForNull String name)
throws ConstraintError
{
if (n < first) {
final @Nonnull String m =
Integer.toString(n) + " < " + Integer.toString(first);
throw new ConstraintError(name == null ? m : name + ": " + m);
}
if (n > last) {
final @Nonnull String m =
Integer.toString(n) + " > " + Integer.toString(last);
throw new ConstraintError(name == null ? m : name + ": " + m);
}
return n;
}
/**
* Constrains a value n
to the inclusive range
* first
..last
. The function throws
* {@link ConstraintError} iff the value lies outside of this range.
*
* The function returns the input value.
*
* @param n
* The input value.
* @param first
* The lower bound.
* @param last
* The upper bound.
* @return The input value.
* @throws ConstraintError
*/
public static long constrainRange(
final long n,
final long first,
final long last)
throws ConstraintError
{
return Constraints.constrainRange(n, first, last, null);
}
/**
* Constrains a value n
to the inclusive range
* first
..last
. The function throws
* {@link ConstraintError} iff the value lies outside of this range, with
* name
, if present, prefixing the failure message.
*
* The function returns the input value.
*
* @param n
* The input value.
* @param first
* The lower bound.
* @param last
* The upper bound.
* @param name
* A prefix for the message passed to {@link ConstraintError}.
* @return The input value.
* @throws ConstraintError
*/
public static long constrainRange(
final long n,
final long first,
final long last,
final @CheckForNull String name)
throws ConstraintError
{
if (n < first) {
final @Nonnull String m =
Long.toString(n) + " < " + Long.toString(first);
throw new ConstraintError(name == null ? m : name + ": " + m);
}
if (n > last) {
final @Nonnull String m =
Long.toString(n) + " > " + Long.toString(last);
throw new ConstraintError(name == null ? m : name + ": " + m);
}
return n;
}
/**
* Ensures a string matches a regular expression. The function throws
* {@link ConstraintError} iff the given string does not match or is null.
*
* @param value
* The input string.
* @param regex
* The regex to match against.
* @param message
* The name of the value used in error messages.
* @throws ConstraintError
* Iff value
does not match regex
.
*/
public static String constrainStringMatch(
final @Nonnull String value,
final @Nonnull Pattern regex,
final @Nonnull String message)
throws ConstraintError
{
if (value == null) {
throw new ConstraintError("value '" + message + "' is null");
}
if (regex == null) {
throw new ConstraintError("regex for matching value '"
+ message
+ "' is null");
}
final @Nonnull Matcher matcher = regex.matcher(value);
if (matcher.find() == false) {
throw new ConstraintError("value '" + message + "' fails pattern match");
}
return value;
}
private Constraints()
{
// Unused.
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy