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)
);
}
}