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

nl.vpro.domain.api.Matchers Maven / Gradle / Ivy

Go to download

Contains the objects used by the Frontend API, like forms and result objects

There is a newer version: 8.3.3
Show newest version
package nl.vpro.domain.api;


import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import org.apache.commons.lang3.StringUtils;


/**
 * Utilities related to matchers.
 *
 * @author Michiel Meeuwissen
 * @since 2.0
 */
public class Matchers {

    static final MatchType TEXT = new MatchType() {
        @Override
        public boolean eval (String value, String input, boolean caseSensitive){
            return value == null ? input == null : (caseSensitive ? value.equals(input) : value.equalsIgnoreCase(input));
        }

        @Override
        public Validation valid (String value){
            return Validation.valid();
        }
    };
    static final MatchType REGEX = new MatchType() {

        @Override
        public boolean eval (String value, String input,boolean caseSensitive) {
            return value == null ? input == null : input != null && Pattern.compile(value, (caseSensitive ? 0 : Pattern.CASE_INSENSITIVE)).matcher(input).matches();
        }

        @Override
        public Validation valid (String value) {
            if (StringUtils.isNotBlank(value)) {
                try {
                    Pattern.compile(value);
                } catch (PatternSyntaxException pe) {
                    return Validation.invalid("Invalid regex: " + value + " " + pe.getMessage());
                }
                java.util.regex.Matcher matcher = REGEX_PATTERN.matcher(value);
                if (matcher.find()) {
                    return Validation.invalid("Regular expression can not start with a special character");
                }
            }
            return Validation.valid();
        }
    };
    static final MatchType WILDCARD = new MatchType() {
        @Override
        public boolean eval(String value, String input, boolean caseSensitive) {
            return value == null ? input == null : input != null && getWildcardPattern(value, caseSensitive).matcher(input).matches();

        }

        @Override
        public Validation valid(String value) {
            if (StringUtils.isNotBlank(value)) {
                if (value.startsWith("?") || value.startsWith("*")) {
                    return Validation.invalid("Wildcard can not start with either ? or *");
                }
            }
            return Validation.valid();
        }
    };

    private Matchers() {
    }

    public static Predicate tokenizedPredicate(final AbstractTextMatcher m) {
        return new Predicate() {
            private Set tokenizedValue;


            @Override
            public boolean test(@Nullable String s) {
                final String value = m.getValue();
                if (s == null) {
                    return value == null;
                }
                Set tokens = new HashSet<>(tokenize(s));
                for(String token : getTokenizedValue()) {
                    if(tokens.contains(token)) {
                        return m.getMatch() != Match.NOT;
                    }
                }
                return m.getMatch() == Match.NOT;
            }

            @Override
            public String toString() {
                return (m.getMatch() == Match.NOT ? "!=" : "=") + tokenize(m.getValue());
            }

            protected Set getTokenizedValue() {
                if(tokenizedValue == null) {
                    tokenizedValue = new HashSet<>(tokenize(m.getValue()));
                }
                return tokenizedValue;
            }

            protected Collection tokenize(String string) {
                if(string == null) {
                    return Collections.emptyList();
                }
                List list = new ArrayList<>(Arrays.asList(string.toLowerCase().split("\\s*\\b\\s*")));
                list.removeIf(StringUtils::isEmpty);
                return list;

            }

        };
    }


    protected static  Predicate listPredicate(
        final Iterable> textMatchers, final boolean tokenized) {
        return listPredicate(textMatchers, input -> {
            if(tokenized) {
                return Matchers.tokenizedPredicate(input);
            } else {
                return input;
            }
        });
    }

    public static  Predicate listPredicate(
        final Iterable>  textMatchers) {
        return listPredicate(textMatchers, false);
    }

    public static  Predicate tokenizedListPredicate(
        final Iterable>  textMatchers) {
        return listPredicate(textMatchers, true);
    }

    protected static  Predicate listPredicate(
        final Iterable> textMatchers,
        final Function, Predicate> predicater) {
        if(textMatchers == null) {
            return a -> true;
        }
        return input -> {
            boolean hasShould = false;
            boolean shouldResult = false;

            for(AbstractTextMatcher t : textMatchers) {
                boolean match = predicater.apply(t).test(input);
                switch(t.getMatch()) {
                    case NOT:
                        // fall through
                    case MUST:
                        if (! match) {
                            return false;
                        }
                        break;
                    case SHOULD:
                        hasShould = true;
                        shouldResult |= match;
                        break;
                }
            }
            return ! hasShould || shouldResult;
        };
    }


    /**
     * Creates a Predicate for a collection of values.
     */
    public static  Predicate> toCollectionPredicate(
        @Nullable final Iterable> matchers,
        @NonNull  final Function valueGetter) {
        if(matchers == null) {
            return i -> true;
        }
        return collection -> {
            if(collection == null) {
                collection = Collections.emptyList();
            }
            boolean hasShould = false;
            boolean shouldResult = false;

            for (Matcher t : matchers) {
                boolean match;
                switch (t.getMatch()) {
                    case NOT:
                        match = matchesAll(collection, t, valueGetter);
                        if (! match) {
                            return false;
                        }
                        break;
                    case MUST:
                        match = matchesOne(collection, t, valueGetter);
                        if (!match) {
                            return false;
                        }
                        break;
                    case SHOULD:
                        match = matchesOne(collection, t, valueGetter);
                        hasShould = true;
                        shouldResult |= match;
                        break;
                }
            }
            return ! hasShould || shouldResult;

        };
    }

    private static  boolean matchesOne(Collection collection, Matcher t,  Function valueGetter) {
        for (C item : collection) {
            V value = valueGetter.apply(item);
            if (t.test(value)) {
                return true;
            }
        }
        return false;
    }
    private static  boolean matchesAll(Collection collection, Matcher t,  Function valueGetter) {
        for (C item : collection) {
            V  value = valueGetter.apply(item);
            if (!t.test(value)) {
                return false;
            }
        }
        return true;
    }


}