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

io.inbot.utils.PatternEvaluator Maven / Gradle / Ivy

Go to download

Misc utility classes we use at Inbot that are probably useful for a broader public.

There is a newer version: 1.28
Show newest version
package io.inbot.utils;

import java.util.Optional;
import java.util.function.Function;

/**
 * Simple Java lambda based implementation of Kotlin and Scala style pattern matching. This provides a nice alternative to the if else chaos that and a more
 * powerful alternative to switch statements. It's not perfect of course but it should allow for some limited verbosity when doing this kind of stuff.
 *
 * Inspired by https://kerflyn.wordpress.com/2012/05/09/towards-pattern-matching-in-java/.
 *
 * Because the evaluate returns an Optional, you can emulate the otherwise bit with a simple call to orElse(...).
 *
 * @param  Input
 * @param  Output
 */
public class PatternEvaluator {

    private final Pattern[] patterns;

    /**
     * Minimal interface for a pattern that takes an input and produces an output.
     * @param  Input
     * @param  Output
     */
    interface Pattern {
        boolean matches(I input);
        Optional apply(I input);
    }

    /**
     * Allows you to produce patterns that simply compare the input to the value and produce the output when they are equal.
     *
     * @param  Input
     * @param  Output
     */
    public static class EqualsPattern implements Pattern {
        private final I val;
        private final Function function;

        public EqualsPattern(I val, Function function) {
            this.val = val;
            this.function = function;
        }

        @Override
        public boolean matches(I input) {
            return val.equals(input);
        }

        @Override
        public Optional apply(I input) {
            return Optional.of(function.apply(input));
        }
    }

    /**
     * If you want to match using a lambda function that produces a boolean this is what you use.
     *
     * @param  Input
     * @param  Output
     */
    public static class BoolExprPattern implements Pattern {
        private final Function evalFunction;
        private final Function function;

        public BoolExprPattern(Function evalFunction, Function function) {
            this.evalFunction = evalFunction;
            this.function = function;
        }

        @Override
        public boolean matches(I e) {
            return evalFunction.apply(e);
        }

        @Override
        public Optional apply(I input) {
            return Optional.of(function.apply(input));
        }
    }

    @SafeVarargs
    public PatternEvaluator(Pattern...ps) {
        this.patterns = ps;
    }

    public Optional evaluate(Input matchVal) {
        for(Pattern p:patterns) {
            if(p.matches(matchVal)) {
                return p.apply(matchVal);
            }
        }
        return Optional.empty();
    }

    /**
     * Allows you use a lambda to match on an input.
     * @param expr expression that evaluates to true/false
     * @param f function that produces O from I
     * @param  Input
     * @param  Output
     * @return pattern that matches on the expression
     */
    public static  BoolExprPattern matches(Function expr, Function f) {
        return new BoolExprPattern(expr, f);
    }

    /**
     * Allows you to match on a value using the equals method
     * @param value the object to compare I to
     * @param f function that produces O from I
     * @param  Input
     * @param  Output
     * @return O
     */
    public static  EqualsPattern equals(I value, Function f) {
        return new EqualsPattern(value, f);
    }

    /**
     * @param patterns zero or more patterns
     * @param  Input
     * @param  Output
     * @return evaluator for the given patterns
     */
    @SafeVarargs
    public static  PatternEvaluator evaluator(Pattern...patterns) {
        return new PatternEvaluator<>(patterns);
    }

    /**
     * Creates an evaluator and then evaluates the value right away.
     * @param input input value
     * @param patterns zero or more patterns
     * @param  Input
     * @param  Output
     * @return O
     */
    @SafeVarargs
    public static  Optional evaluate(I input, Pattern...patterns) {
        return new PatternEvaluator<>(patterns).evaluate(input);
    }
}