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

io.fluxcapacitor.common.api.search.constraints.QueryConstraint Maven / Gradle / Ivy

There is a newer version: 0.1072.0
Show newest version
/*
 * Copyright (c) Flux Capacitor IP B.V. or its affiliates. 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 io.fluxcapacitor.common.api.search.constraints;

import io.fluxcapacitor.common.api.search.Constraint;
import io.fluxcapacitor.common.api.search.NoOpConstraint;
import io.fluxcapacitor.common.search.Document;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import lombok.Value;
import lombok.With;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static io.fluxcapacitor.common.SearchUtils.letterOrNumber;
import static io.fluxcapacitor.common.api.search.constraints.AllConstraint.all;
import static org.apache.commons.lang3.StringUtils.isBlank;

@Value
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class QueryConstraint extends PathConstraint {
    private static final String operator = "&|()!";
    private static final Pattern termPattern =
            Pattern.compile(String.format("\"[^\"]*\"|[%1$s]|[*%2$s][^\\s%1$s]+[*%2$s]|[*%2$s]+", operator, letterOrNumber), Pattern.MULTILINE);
    private static final Pattern splitOnInnerAsterisk = Pattern.compile(String.format("(?<=[%1$s])\\*(?=[%1$s])", letterOrNumber));

    public static Constraint query(String query, String... paths) {
        return query(query, false, paths);
    }

    public static Constraint query(String query, boolean lookAheadForAllTerms, String... paths) {
        return isBlank(query) ? NoOpConstraint.instance : new QueryConstraint(query, lookAheadForAllTerms, List.of(paths));
    }

    @NonNull String query;
    @With boolean lookAheadForAllTerms;
    @With List paths;

    @Override
    public boolean matches(Document document) {
        return decompose().matches(document);
    }

    @Override
    protected boolean matches(Document.Entry entry) {
        throw new UnsupportedOperationException();
    }

    @ToString.Exclude
    @EqualsAndHashCode.Exclude
    @Getter(lazy = true)
    @Accessors(fluent = true)
    Constraint decompose = AllConstraint.all(createConstraints(splitInTermsAndOperators(getQuery())));

    private List createConstraints(List parts) {
        List result = new ArrayList<>();
        ListIterator iterator = parts.listIterator();
        while (iterator.hasNext()) {
            parsePart(iterator.next(), iterator, result);
        }
        return result;
    }

    private void parsePart(String part, ListIterator iterator, List constraints) {
        switch (part) {
            case "(":
                handleGroupStart(iterator, constraints);
                break;
            case "|":
                handleOr(iterator, constraints);
                break;
            case "!":
                handleNot(iterator, constraints);
                break;
            case "":
                break;
            default:
                handleTerm(part, constraints);
                break;
        }
    }

    private void handleGroupStart(ListIterator iterator, List constraints) {
        List subList = new ArrayList<>();
        while (iterator.hasNext()) {
            String part = iterator.next();
            if (part.equals(")")) {
                break;
            }
            parsePart(part, iterator, subList);
        }
        constraints.add(AllConstraint.all(subList));
    }

    private void handleOr(ListIterator iterator, List constraints) {
        if (iterator.hasNext() && !constraints.isEmpty()) {
            Constraint leftHandConstraint = constraints.remove(constraints.size() - 1);
            List rightHandPart = new ArrayList<>();
            parsePart(iterator.next(), iterator, rightHandPart);
            constraints.add(leftHandConstraint.or(AllConstraint.all(rightHandPart)));
        } else {
            parsePart("OR", iterator, constraints);
        }
    }

    private void handleNot(ListIterator iterator, List constraints) {
        List subList = new ArrayList<>();
        if (iterator.hasNext()) {
            parsePart(iterator.next(), iterator, subList);
        }
        constraints.add(NotConstraint.not(AllConstraint.all(subList)));
    }

    private void handleTerm(String term, List constraints) {
        if (term.startsWith("\"") && term.endsWith("\"")) {
            constraints.add(ContainsConstraint.contains(term.substring(1, term.length() - 1),
                    false, false, paths.toArray(String[]::new)));
            return;
        }

        List result = new ArrayList<>();
        String[] parts = splitOnInnerAsterisk.split(term);
        for (int i = 0; i < parts.length; i++) {
            boolean prefixSearch = i != 0;
            boolean postfixSearch = (i != parts.length - 1) || lookAheadForAllTerms;
            String part = parts[i];
            if (part.endsWith("*")) {
                part = part.substring(0, part.length() - 1);
                postfixSearch = true;
            }
            if (part.startsWith("*")) {
                part = part.substring(1);
                prefixSearch = true;
            }
            result.add(ContainsConstraint.contains(part, prefixSearch, postfixSearch, paths.toArray(String[]::new)));
        }
        constraints.add(all(result));
    }

    private List splitInTermsAndOperators(String query) {
        List parts = new ArrayList<>();

        Matcher matcher = termPattern.matcher(query.trim());
        while (matcher.find()) {
            String group = matcher.group().trim();
            if (!group.isEmpty() && !group.equals("\"") && !group.equals("AND") && !group.equals("&")) {
                parts.add(group.equals("OR") ? "|" : group);
            }
        }
        return parts;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy