Compose.java

package org.klojang.check.types;

import org.klojang.check.CommonChecks;

import java.util.Objects;
import java.util.function.IntPredicate;
import java.util.function.Predicate;

/**
 * Utility methods that assist in the creation of new checks by combining multiple
 * individual checks. Note that while the predicates in the {@link CommonChecks}
 * class are, in fact, already either a {@link ComposablePredicate} or a
 * {@link ComposableIntPredicate}, the relational checks obviously are not.
 * Handwritten lambdas and method references (for example: {@code i -> i % 3 == 0})
 * neither are a {@code ComposablePredicate} or {@code ComposableIntPredicate} <i>in
 * and of themselves</i>. The utility methods defined in this class make sure a
 * composition can start with a {@link Relation}, lambda or method reference.
 *
 * @author Ayco Holleman
 */
public final class Compose {

  private Compose() {
    throw new UnsupportedOperationException();
  }


  /**
   * Returns a {@code ComposablePredicate} that evaluates to {@code true} if the
   * value to be tested has the specified value. The two values are compared using
   * {@link Objects#equals(Object, Object) Objects.equals()}.
   *
   * @param value the value to compare the value to be tested with
   * @param <T> the type of the value being tested
   * @return a {@code ComposablePredicate} that evaluates to {@code true} if the
   *     value to be tested has the specified value
   */
  public static <T> ComposablePredicate<T> validIf(T value) {
    return x -> Objects.equals(x, value);
  }

  /**
   * Returns a {@code ComposablePredicate} that evaluates to {@code true} if the
   * value to be tested has the specified value.
   *
   * @param value the value to compare the value to be tested with
   * @return a {@code ComposablePredicate} that evaluates to {@code true} if the
   *     value to be tested has the specified value
   */
  public static ComposableIntPredicate validIntIf(int value) {
    return x -> x == value;
  }

  /**
   * Returns a {@code ComposablePredicate} that evaluates to {@code true} if the
   * value to be tested has the specified value. The two values are compared using
   * {@link Objects#equals(Object, Object) Objects.equals()}.
   *
   * @param value the value to compare the value to be tested with
   * @param <T> the type of the value being tested
   * @return a {@code ComposablePredicate} that evaluates to {@code true} if the
   *     value to be tested has the specified value
   */
  public static <T> ComposablePredicate<T> invalidIf(T value) {
    return x -> !Objects.equals(x, value);
  }

  /**
   * Returns a {@code ComposablePredicate} that evaluates to {@code true} if the
   * value to be tested has the specified value.
   *
   * @param value the value to compare the value to be tested with
   * @return a {@code ComposablePredicate} that evaluates to {@code true} if the
   *     value to be tested has the specified value
   */
  public static ComposableIntPredicate invalidIntIf(int value) {
    return x -> x != value;
  }

  /**
   * Converts a {@code Predicate} to a {@code ComposablePredicate}. This method can
   * be used to convert a predefined {@code Predicate} constant from outside Klojang
   * Check to a {@code ComposablePredicate}, or to hard-cast a lambda or method
   * reference to a {@code ComposablePredicate}. This method is only needed if the
   * {@code Predicate}, lambda or method reference must be the first test of the
   * composition.
   *
   * <blockquote><pre>{@code
   * Check.that(sentence).is(validIf((String s) -> s.contains("to"))
   *    .orElse((String s) -> s.contains("be"));
   *    .orElse((String s) -> s.contains("or"));
   *    .orElse((String s) -> s.contains("not")));
   * }</pre></blockquote>
   *
   * @param test the {@code Predicate} to convert
   * @param <T> the type of the value being tested
   * @return the equivalent {@code ComposablePredicate}
   */
  public static <T> ComposablePredicate<T> validWhen(Predicate<T> test) {
    return test::test;
  }

  /**
   * Converts an {@code IntPredicate} to a {@code ComposableIntPredicate}. This
   * method can be used to convert a predefined {@code IntPredicate} constant from
   * outside Klojang Check to a {@code ComposableIntPredicate}, or to hard-cast a
   * lambda or method reference. This method is only needed if the
   * {@code IntPredicate}, lambda or method reference must be the first test of the
   * composition.
   *
   * @param test the {@code IntPredicate} to convert
   * @return the equivalent {@code ComposableIntPredicate}
   */
  public static ComposableIntPredicate validIntWhen(IntPredicate test) {
    return test::test;
  }

  /**
   * Converts a {@code Relation} to a {@code ComposablePredicate}. More precisely:
   * this method returns a {@code ComposablePredicate} that evaluates to {@code true}
   * if the value being tested has the specified relation to the specified value.
   * This method is only needed if the {@code Relation} must be the first test of the
   * composition.
   *
   * <blockquote><pre>{@code
   * Check.that(Year.now()).is(validIf(GT(), Year.of(2000))
   *    .andAlso(LT(), Year.of(3000));
   * }</pre></blockquote>
   *
   * @param relation the relationship test to execute
   * @param object the object of the relation
   * @param <S> the type of the subject of the relation
   * @param <O> the type of the object of the relation
   * @return a {@code ComposablePredicate} that evaluates to {@code true} if the
   *     value being tested has the specified relation to the specified value
   */
  public static <S, O> ComposablePredicate<S> validWhen(Relation<S, O> relation,
      O object) {
    return s -> relation.exists(s, object);
  }

  /**
   * Converts an {@code IntObjRelation} to a {@code ComposableIntPredicate}. More
   * precisely: this method returns a {@code ComposableIntPredicate} that evaluates
   * to {@code true} if the value being tested has the specified relation to the
   * specified value. This method is only needed if the {@code IntObjRelation} must
   * be the first test of the composition.
   *
   * @param relation the relationship test to execute
   * @param object the object of the relation
   * @param <O> the type of the object of the relation
   * @return a {@code ComposableIntPredicate} that evaluates to {@code true} if the
   *     value being tested has the specified relation to the specified value
   */
  public static <O> ComposableIntPredicate validIntWhen(IntObjRelation<O> relation,
      O object) {
    return s -> relation.exists(s, object);
  }

  /**
   * Converts an {@code Relation} to a {@code ComposableIntPredicate}. More
   * precisely: this method returns a {@code ComposableIntPredicate} that evaluates
   * to {@code true} if the value being tested has the specified relation to the
   * specified value. This method is only needed if the {@code IntRelation} must be
   * the first test of the composition.
   *
   * @param relation the relationship test to execute
   * @param object the object of the relation
   * @return a {@code ComposableIntPredicate} that evaluates to {@code true} if the
   *     value being tested has the specified relation to the specified value
   */
  public static ComposableIntPredicate validIntWhen(IntRelation relation, int object) {
    return s -> relation.exists(s, object);
  }

}