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

org.fryske_akademy.jpa.OPERATOR Maven / Gradle / Ivy

The newest version!
package org.fryske_akademy.jpa;

/*-
 * #%L
 * jpaservices
 * %%
 * Copyright (C) 2018 - 2021 Fryske Akademy
 * %%
 * 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.
 * #L%
 */

import org.fryske_akademy.Util;

import java.util.Arrays;
import java.util.stream.Collectors;

/**
 * A series of operators supported by this library, encapsulates all
 * intelligence around them. Some of the operators may be determined from user
 * input, for these {@link #getUserInput()} } has a value.
 *
 * @see org.fryske_akademy.jpa.Param.Builder
 */
public enum OPERATOR {
    EQ("=", null),
    GT(">", ">"),
    LT("<", "<"),
    GE(">=", ">="),
    LE("<=", "<="),
    IN("IN", null),
    LIKE("LIKE", null),
    NE("<>", null),
    MEMBEROF("MEMBER OF", null),
    BETWEEN("BETWEEN", " <=> "),
    /**
     * case-insensitive user input
     */
    ISNULL("IS NULL", "IS NULL"),
    /**
     * case-insensitive user input
     */
    ISNOTNULL("IS NOT NULL", "!IS NULL"),
    /**
     * case-insensitive user input
     */
    ISBLANK("= ''", "IS BLANK"),
    /**
     * case-insensitive user input
     */
    ISNOTBLANK("<> ''", "!IS BLANK"),
    /**
     * for empty collection!
     * case-insensitive user input
     */
    ISEMPTY("IS EMPTY", "IS EMPTY"),
    /**
     * for empty collection!
     * case-insensitive user input
     */
    ISNOTEMPTY("IS NOT EMPTY", "!IS EMPTY"),
    /**
     * Not an operator, it is here to support " AND " syntax in user values
     */
    AND("AND", " AND "),
    /**
     * Not an operator, it is here to support " OR " syntax in user values
     */
    OR("OR", " OR ");

    public static final char NEGATION = '!';

    OPERATOR(String token, String userInput) {
        this.token = token;
        this.userInput = userInput == null ? null : name().equals("AND") || name().equals("OR") ? userInput : userInput.toLowerCase();
    }

    private final String token, userInput;

    /**
     * check if a string (user value) indicates a negation
     *
     * @see #NEGATION
     * @param value
     * @return
     */
    public static boolean negation(String value) {
        return Util.isIndex(value, NEGATION, 0);
    }

    /**
     * Strip !, <, >, = at the beginning of a value in order to get the raw user supplied value.
     * @param value
     * @return 
     */
    public static String stripSyntax(String value) {
        int syntaxChars = 0;
        if (negation(value)) {
            syntaxChars++;
        }
        if (smaller(value) || greater(value)) {
            syntaxChars++;
        }
        if (eq(value)) {
            syntaxChars++;
        }
        return value.substring(syntaxChars);
    }

    /**
     * check if a string (user value) indicates a greater/smaller than or equal
     * comparison
     *
     * @see #GE
     * @see #LE
     * @param value
     * @return
     */
    public static boolean eq(String value) {
        boolean b = smaller(value) || greater(value);
        if (b) {
            return negation(value)
                    ? Util.isIndex(value, EQ.token.charAt(0), 2)
                    : Util.isIndex(value, EQ.token.charAt(0), 1);
        }
        return false;
    }

    /**
     * check if a string (user value) indicates a greater than comparison
     *
     * @see #GT
     * @param value
     * @return
     */
    public static boolean greater(String value) {
        return Util.isIndex(value, GT.token.charAt(0), 0) || (negation(value) && Util.isIndex(value, GT.token.charAt(0), 1));
    }

    /**
     * check if a string (user value) indicates a smaller than comparison.
     *
     * @see #LT
     * @param value
     * @return
     */
    public static boolean smaller(String value) {
        return Util.isIndex(value, LT.token.charAt(0), 0) || (negation(value) && Util.isIndex(value, LT.token.charAt(0), 1));
    }

    /**
     * users may input "(!)is null", "(!)is empty", "(!)is blank", in that case
     * there is no parameter value to be set for a key.
     *
     * @param s
     * @param syntaxInValue
     * @return
     */
    public static boolean valueIsOperator(String s, boolean syntaxInValue) {
        return syntaxInValue && (nullComp(s) || emptyComp(s) || blankComp(s));
    }

    /**
     * check if a string (user value) is a null comparison
     *
     * @see #valueIsOperator(String, boolean)
     * @param s
     * @return
     */
    private static boolean nullComp(String s) {
        if (s == null || s.isEmpty()) {
            return false;
        }
        return s.equalsIgnoreCase(ISNULL.userInput) || s.equalsIgnoreCase(ISNOTNULL.userInput);
    }

    /**
     * check if a string (user value) is a blank comparison
     *
     * @see #valueIsOperator(String, boolean)
     * @param s
     * @return
     */
    private static boolean blankComp(String s) {
        if (s == null || s.isEmpty()) {
            return false;
        }
        return s.equalsIgnoreCase(ISBLANK.userInput) || s.equalsIgnoreCase(ISNOTBLANK.userInput);
    }

    /**
     * check if a string (user value) is a empty comparison
     *
     * @see #valueIsOperator(String, boolean)
     * @param s
     * @return
     */
    private static boolean emptyComp(String s) {
        if (s == null || s.isEmpty()) {
            return false;
        }
        return s.equalsIgnoreCase(ISEMPTY.userInput) || s.equalsIgnoreCase(ISNOTEMPTY.userInput);
    }

    public static boolean isBetween(String value) {
        return value != null && value.split(BETWEEN.userInput).length == 2;
    }

    /**
     * true when " AND " is found in the given string and it comes before an optional " OR " and there are at least two terms
     * @param value
     * @return 
     */
    public static boolean isAnd(String value) {
        return value != null
                && value.contains(AND.userInput)
                && (!value.contains(OR.userInput) || value.indexOf(AND.userInput) < value.indexOf(OR.userInput))
                && value.split(AND.userInput,2).length == 2;
    }

    /**
     * true when " OR " is found in the given string and it comes before an optional " AND " and there are at least two terms
     * @param value
     * @return 
     */
    public static boolean isOr(String value) {
        return value != null
                && value.contains(OR.userInput)
                && (!value.contains(AND.userInput) || value.indexOf(OR.userInput) < value.indexOf(AND.userInput))
                && value.split(OR.userInput,2).length == 2;
    }

    /**
     * Returns an operator from the value when syntaxInValue is true and the
     * value contains one of the supported operators, otherwise the operator is
     * determined from the operator argument
     *
     * @param operator
     * @param value
     * @param syntaxInValue
     * @return
     */
    public static OPERATOR operator(String operator, String value, boolean syntaxInValue) {
        if (syntaxInValue) {
            return operatorInUserInput(value)
                    ? fromUserInput(value)
                    : fromToken(operator);
        } else {
            return fromToken(operator);
        }
    }

    /**
     * returns the token wrapped in spaces, so string concatenation in query
     * building can be used.
     *
     * @return
     */
    @Override
    public String toString() {
        return " " + token + " ";
    }

    public String getToken() {

        return token;
    }

    /**
     * When null the OPERATOR cannot be determined from user input
     *
     * @return
     */
    public String getUserInput() {
        return userInput;
    }

    /**
     * Is this operator in user input
     *
     * @param input
     * @return
     */
    public boolean isOperatorInUserInput(String input) {
        switch (this) {
            case GT -> {
                return greater(input) && !eq(input);
            }
            case GE -> {
                return greater(input) && eq(input);
            }
            case LT -> {
                return smaller(input) && !eq(input);
            }
            case LE -> {
                return smaller(input) && eq(input);
            }
            default -> {
                return input.equalsIgnoreCase(userInput);
            }
        }
    }

    public static OPERATOR fromToken(String token) {
        return Arrays.stream(OPERATOR.values()).filter(
                operator -> token.equalsIgnoreCase(operator.token)
        ).findFirst().orElseThrow(
                () -> new IllegalArgumentException(String.format("%s invalid, only %s supported", token, Arrays.asList(OPERATOR.values()))));
    }

    public static OPERATOR fromUserInput(String input) {
        return Arrays.stream(OPERATOR.values()).filter(
                t -> t.isOperatorInUserInput(input)
        ).findFirst().orElseThrow(
                () -> new IllegalArgumentException(String.format("%s invalid, only %s supported", input,
                        Arrays.stream(OPERATOR.values()).map(o -> o.userInput).collect(Collectors.toList())))
        );
    }

    /**
     * Does user input contain an operator
     *
     * @see OPERATOR#isOperatorInUserInput(String)
     * @param input
     * @return
     */
    public static boolean operatorInUserInput(String input) {
        return Arrays.stream(OPERATOR.values()).anyMatch(
                t -> t.isOperatorInUserInput(input)
        );
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy