Match.java

/*
 * (c) Copyright 2021 Hasan Selman Kara. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package li.selman.jpbe.classifier;

import java.text.MessageFormat;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
import li.selman.jpbe.dsl.token.Token;
import li.selman.jpbe.dsl.token.TokenSequence;

/**
 * Matches if the TokenSequence {@code r} occurs {@code k} times in a given string or
 * inverse if positive is false.
 *
 * @author Hasan Selman Kara
 * @see li.selman.jpbe.dsl.token.TokenSequence
 */
public final class Match implements Predicate {

    private final TokenSequence r;
    private final int k;
    private final boolean positive;

    private Match(TokenSequence r, int k, boolean positive) {
        if (r.getNumberOfTokens() == 0) throw new IllegalArgumentException("TokenSequence can't be empty.");
        // TODO(#bug): does the possibility '0' matches make sense?
        if (k < 0) throw new IllegalArgumentException("Number of matches can't be negative");


        this.r = r;
        this.k = k;
        this.positive = positive;
    }

    static Match positive(TokenSequence r, int k) {
        return new Match(r, k, true);
    }

    static Match negative(TokenSequence r, int k) {
        return new Match(r, k, false);
    }

    @Override
    public boolean matches(String s) {
        int numberOfMatches = getNumberOfMatches(s);

        if (positive) {
            return numberOfMatches >= k;
        } else {
            return !(numberOfMatches >= k);
        }
    }

    private int getNumberOfMatches(String s) {
        Matcher matcher = r.getMergedPattern().matcher(s);
        int numberOfMatches = 0;
        while (matcher.find()) {
            numberOfMatches++;
        }
        return numberOfMatches;
    }

    @Override
    public String toString() {
        String text = positive ? "P" : "N";
        String tokenSeqStr = r.getTokens().stream().map(Token::toString).collect(Collectors.joining(", ", "{", "}"));
        return MessageFormat.format("({0}, {1}, {2})", text, k, tokenSeqStr);
    }
}