All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.dakusui.crest.core.Matcher Maven / Gradle / Ivy

package com.github.dakusui.crest.core;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;

import static java.util.Objects.requireNonNull;

public interface Matcher {
  boolean matches(T value, Session session, List exceptions);

  void describeExpectation(Session session);

  void describeMismatch(T value, Session session);

  interface Composite extends Matcher {
    default void describeExpectation(Session session) {
      session.describeExpectation(this);
    }

    default void describeMismatch(T value, Session session) {
      session.describeMismatch(value, this);
    }

    boolean isTopLevel();

    List> children();

    String name();

    abstract class Base implements Composite {
      private final List> children;
      private final boolean          topLevel;

      @SuppressWarnings("unchecked")
      protected Base(boolean topLevel, List> children) {
        this.children = (List>) Collections.unmodifiableList((List) requireNonNull(children));
        this.topLevel = topLevel;
      }

      @Override
      public boolean matches(T value, Session session, List exceptions) {
        List work = new LinkedList<>();
        boolean ret = first();
        for (Matcher eachChild : children())
          ret = op(ret, eachChild.matches(value, session, work));
        exceptions.addAll(work);
        return ret && work.isEmpty();
      }

      @Override
      public boolean isTopLevel() {
        return this.topLevel;
      }

      @Override
      public List> children() {
        return this.children;
      }

      abstract protected boolean first();

      abstract protected boolean op(boolean current, boolean next);

    }
  }

  interface Conjunctive extends Composite {
    @SuppressWarnings("unchecked")
    static  Matcher create(boolean topLevel, List> matchers) {
      return new Conjunctive.Base(topLevel, matchers) {
        @Override
        public String name() {
          return "and";
        }

        @Override
        protected boolean first() {
          return true;
        }

        @Override
        protected boolean op(boolean current, boolean next) {
          return current && next;
        }
      };
    }
  }

  interface Disjunctive extends Composite {
    @SuppressWarnings("unchecked")
    static  Matcher create(boolean topLevel, List> matchers) {
      return new Composite.Base(topLevel, matchers) {

        @Override
        public String name() {
          return "or";
        }

        @Override
        protected boolean first() {
          return false;
        }

        @Override
        protected boolean op(boolean current, boolean next) {
          return current || next;
        }
      };
    }
  }

  interface Negative extends Composite {
    static  Matcher create(Matcher matcher) {
      return new Composite.Base(true, Collections.singletonList(matcher)) {
        @Override
        public String name() {
          return "not";
        }

        @Override
        protected boolean first() {
          return true;
        }

        @Override
        protected boolean op(boolean current, boolean next) {
          return current && !next;
        }
      };
    }
  }

  interface Leaf extends Matcher {
    default void describeExpectation(Session session) {
      session.describeExpectation(this);
    }

    default void describeMismatch(T value, Session session) {
      session.describeMismatch(value, this);
    }

    Predicate p();

    Function func();

    static  Leaf create(Predicate p, Function function) {
      return new Leaf() {
        @Override
        public Predicate p() {
          return p;
        }

        @SuppressWarnings("unchecked")
        @Override
        public Function func() {
          return (Function) function;
        }

        @Override
        public boolean matches(I value, Session session, List exceptions) {
          try {
            return session.matches(this, value, exceptions::add) && exceptions.isEmpty();
          } catch (Throwable e) {
            return false;
          }
        }
      };
    }
  }
}