package be.raildelays.delays; import java.time.LocalTime; /** * @author Almex * @since 2.0 */ public interface DelayMatcher<T> extends Matcher<T> { static OperatorMatcher<Long> is(OperatorMatcher<Long> matcher) { return matcher; } static OperatorMatcher<Long> is(Long value) { return is(equalsTo(value)); } static OperatorMatcher<Long> equalsTo(Long value) { return OperatorMatcher.operator(Operator.EQUAL, ValueMatcher.value(value)); } static OperatorMatcher<Long> zero() { return OperatorMatcher.operator(Operator.EQUAL, ValueMatcher.value(0L)); } static OperatorMatcher<Long> greaterThan(Long value) { return OperatorMatcher.operator(Operator.GREATER, ValueMatcher.value(value)); } static OperatorMatcher<Long> greaterThanOrEqual(Long value) { return OperatorMatcher.operator(Operator.GREATER_OR_EQUAL, ValueMatcher.value(value)); } static OperatorMatcher<Long> after() { return greaterThan(0L); } static OperatorMatcher<Long> lessThan(Long value) { return OperatorMatcher.operator(Operator.LESS, ValueMatcher.value(value)); } static OperatorMatcher<Long> lessThanOrEqual(Long value) { return OperatorMatcher.operator(Operator.LESS_OR_EQUAL, ValueMatcher.value(value)); } static OperatorMatcher<Long> before() { return lessThan(0L); } static boolean difference(OrderingComparison comparison, OperatorMatcher<Long> matcher) { comparison.setOperator(matcher.getOperator()); return comparison.match(matcher.getValueMatcher().getValue()); } static boolean duration(OrderingComparison comparison, OperatorMatcher<Long> matcher) { // A duration is the opposite of a difference return difference(comparison, opposite(matcher)); } /** * Create a {@link OperatorMatcher} containing the {@link ValueMatcher} with an opposite {@code value} and the * opposite {@link be.raildelays.delays.DelayMatcher.Operator}. * * @param matcher the {@link OperatorMatcher} to clone * @return a {@link OperatorMatcher} containing the {@link ValueMatcher} with an opposite {@code value} and the * opposite {@link be.raildelays.delays.DelayMatcher.Operator}. * @throws UnsupportedOperationException if the {@link OperatorMatcher#operator} is not supported by this * implementation. */ static OperatorMatcher<Long> opposite(OperatorMatcher<Long> matcher) { Operator operator = matcher.getOperator(); ValueMatcher<Long> valueMatcher = ValueMatcher.value(-matcher.getValueMatcher().getValue()); switch (operator) { case GREATER: operator = Operator.LESS; break; case LESS: operator = Operator.GREATER; break; case GREATER_OR_EQUAL: operator = Operator.LESS_OR_EQUAL; break; case LESS_OR_EQUAL: operator = Operator.GREATER_OR_EQUAL; break; case EQUAL: operator = Operator.EQUAL; break; default: throw new UnsupportedOperationException(String.format("The '%s' operator is not supported", operator)); } return OperatorMatcher.operator(operator, valueMatcher); } static OrderingComparison between(TimeDelay from) { return new OrderingComparison(from); } static OrderingComparison between(LocalTime from) { return new OrderingComparison(TimeDelay.of(from)); } @Override default boolean match(T object) { return false; } enum Operator { GREATER, LESS, EQUAL, GREATER_OR_EQUAL, LESS_OR_EQUAL } class OrderingComparison implements Matcher<Long> { private TimeDelay from; private TimeDelay to; private Operator operator; protected OrderingComparison(TimeDelay from) { this.from = from; } @Override public boolean match(Long value) { boolean result; switch (operator) { case GREATER: result = Delays.compareTimeAndDelay(from, to) > value; break; case GREATER_OR_EQUAL: result = Delays.compareTimeAndDelay(from, to) >= value; break; case LESS: result = Delays.compareTimeAndDelay(from, to) < value; break; case LESS_OR_EQUAL: result = Delays.compareTimeAndDelay(from, to) <= value; break; case EQUAL: default: result = Delays.compareTimeAndDelay(from, to) == value; } return result; } protected void setTo(TimeDelay to) { this.to = to; } protected void setOperator(Operator operator) { this.operator = operator; } public OrderingComparison and(TimeDelay to) { this.setTo(to); return this; } public OrderingComparison and(LocalTime to) { this.setTo(TimeDelay.of(to)); return this; } } class ValueMatcher<V> implements Matcher<V> { private V value; private ValueMatcher(V value) { this.value = value; } public static <V> ValueMatcher<V> value(V value) { return new ValueMatcher<>(value); } @Override public boolean match(V target) { boolean result = false; if (value != null) { result = value.equals(target); } else if (target == null) { result = true; } return result; } public V getValue() { return value; } public void setValue(V value) { this.value = value; } } class OperatorMatcher<V> implements Matcher<Operator> { private Operator operator; private ValueMatcher<V> valueMatcher; private OperatorMatcher(Operator operator, ValueMatcher<V> valueMatcher) { this.operator = operator; this.valueMatcher = valueMatcher; } public static <V> OperatorMatcher<V> operator(Operator operator, ValueMatcher<V> matcher) { return new OperatorMatcher<>(operator, matcher); } @Override public boolean match(Operator target) { return operator.equals(target); } public ValueMatcher<V> getValueMatcher() { return valueMatcher; } public Operator getOperator() { return operator; } } }