From ebbeb4595286da72f0700a138813376b35f421cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Gr=C3=BCn?= Date: Tue, 14 Jan 2025 22:25:07 +0100 Subject: [PATCH 1/2] change to gradle, add list operators, code refactoring --- .gitignore | 5 +- .travis.yml | 8 - build.gradle.kts | 39 ++ gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 +++++++++++++ gradlew.bat | 92 +++++ pom.xml | 83 ----- settings.gradle.kts | 1 + .../de/alexgruen/query/generated/Query.g4 | 37 +- .../de/alexgruen/query/DefaultCreator.java | 345 ------------------ .../de/alexgruen/query/LogicalOperator.java | 5 +- .../de/alexgruen/query/LogicalOperators.java | 2 +- .../java/de/alexgruen/query/Operator.java | 17 +- .../java/de/alexgruen/query/PrintQuery.java | 3 +- .../de/alexgruen/query/PrintQueryCreator.java | 69 +++- .../java/de/alexgruen/query/QueryCreator.java | 170 +++++++++ .../java/de/alexgruen/query/QueryNode.java | 31 +- .../java/de/alexgruen/query/QueryPrinter.java | 38 +- .../java/de/alexgruen/query/QueryTree.java | 2 +- .../query/compiler/QueryCompiler.java | 47 ++- .../query/compiler/QueryCompilerBuilder.java | 39 +- .../compiler/QueryCompilerErrorListener.java | 16 +- .../compiler/QueryCompilerException.java | 2 +- .../query/compiler/QueryContext.java | 22 +- .../query/compiler/QueryTreeCompiler.java | 9 +- .../query/compiler/RegexTermVisitor.java | 18 +- .../query/compiler/TermQueryVisitor.java | 14 +- .../alexgruen/query/compiler/TermVisitor.java | 33 +- .../query/compiler/TextSearchVisitor.java | 18 +- .../query/compiler/parser/Parser.java | 9 +- .../query/compiler/parser/ParserUtil.java | 20 +- .../alexgruen/query/creator/LogicCreator.java | 5 +- .../query/creator/OperatorCreator.java | 3 +- .../query/creator/OperatorCreatorMap.java | 34 +- .../alexgruen/query/creator/TermCreator.java | 3 +- .../query/optimization/QueryOptimization.java | 1 + .../optimization/RemoveRedundantBrackets.java | 24 +- .../java/de/alexgruen/query/term/Field.java | 30 +- .../java/de/alexgruen/query/term/Term.java | 12 +- .../de/alexgruen/query/term/TermOperator.java | 8 +- .../alexgruen/query/term/TermOperators.java | 35 +- .../java/de/alexgruen/query/term/Value.java | 150 +++++--- .../de/alexgruen/query/util/CompilerUtil.java | 29 +- .../de/alexgruen/query/util/StringUtil.java | 38 +- .../alexgruen/querycompiler/CompileTest.java | 73 ++-- .../querycompiler/CompilerUtilTest.java | 60 +++ .../QueryCompilerBuilderTest.java | 185 ++++++++++ .../querycompiler/QueryCompilerTest.java | 121 ++++++ .../querycompiler/QueryCreatorTest.java | 217 +++++++++++ .../querycompiler/RegexTermVisitorTest.java | 64 ++++ .../querycompiler/TermCreatorTest.java | 90 +++++ .../de/alexgruen/querycompiler/ValueTest.java | 125 +++++++ 52 files changed, 1983 insertions(+), 774 deletions(-) delete mode 100644 .travis.yml create mode 100644 build.gradle.kts create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat delete mode 100644 pom.xml create mode 100644 settings.gradle.kts rename src/main/{antlr4 => antlr}/de/alexgruen/query/generated/Query.g4 (85%) delete mode 100644 src/main/java/de/alexgruen/query/DefaultCreator.java create mode 100644 src/main/java/de/alexgruen/query/QueryCreator.java create mode 100644 src/test/java/de/alexgruen/querycompiler/CompilerUtilTest.java create mode 100644 src/test/java/de/alexgruen/querycompiler/QueryCompilerBuilderTest.java create mode 100644 src/test/java/de/alexgruen/querycompiler/QueryCompilerTest.java create mode 100644 src/test/java/de/alexgruen/querycompiler/QueryCreatorTest.java create mode 100644 src/test/java/de/alexgruen/querycompiler/RegexTermVisitorTest.java create mode 100644 src/test/java/de/alexgruen/querycompiler/TermCreatorTest.java create mode 100644 src/test/java/de/alexgruen/querycompiler/ValueTest.java diff --git a/.gitignore b/.gitignore index 55c315d..8b804e5 100644 --- a/.gitignore +++ b/.gitignore @@ -88,4 +88,7 @@ hs_err_pid* target .idea -querycompiler.iml \ No newline at end of file +querycompiler.iml + +build +.gradle \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 79bee2e..0000000 --- a/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: java - -jdk: - - oraclejdk8 - - -after_success: - - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..84e492b --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + java + antlr + id("maven-publish") +} + +group = "dev.gruen" +version = System.getenv("VERSION") ?: "0.0.1-SNAPSHOT" +description = "Simple QueryCompiler for Java" + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} + +repositories { + mavenCentral() +} + +dependencies { + antlr("org.antlr:antlr4:4.13.2") + implementation("org.antlr:antlr4-runtime:4.13.2") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.11.4") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.11.4") +} + +tasks.withType { + options.encoding = "UTF-8" +} + +tasks.test { + useJUnitPlatform() +} + + +tasks.generateGrammarSource { + maxHeapSize = "64m" + arguments = arguments + listOf("-visitor", "-long-messages") +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..9355b41 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 03118ee..0000000 --- a/pom.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - 4.0.0 - - de.alexgruen - querycompiler - 0.3-SNAPSHOT - - UTF-8 - 1.8 - 1.8 - - SimpleQueryCompiler - Simple QueryCompiler for Java - https://github.com/nRo/SimpleQueryCompiler - - - MIT License - http://www.opensource.org/licenses/mit-license.php - - - - https://github.com/nRo/SimpleQueryCompiler - scm:git:https://github.com/nRo/SimpleQueryCompiler.git - HEAD - - - - Alexander Grün - al.gruen@gmx.de - alexgruen - - - - - - - - org.antlr - antlr4-runtime - 4.9.2 - - - org.junit.jupiter - junit-jupiter-api - 5.7.2 - test - - - - - - org.antlr - antlr4-maven-plugin - 4.9.2 - - true - - - - - antlr4 - - - - - - - - - central - maven.alexgruen.de-releases - http://maven.alexgruen.de/artifactory/public-release - - - snapshots - maven.alexgruen.de-snapshots - http://maven.alexgruen.de/artifactory/public-snapshot - - - \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..66fb61a --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "query-compiler" \ No newline at end of file diff --git a/src/main/antlr4/de/alexgruen/query/generated/Query.g4 b/src/main/antlr/de/alexgruen/query/generated/Query.g4 similarity index 85% rename from src/main/antlr4/de/alexgruen/query/generated/Query.g4 rename to src/main/antlr/de/alexgruen/query/generated/Query.g4 index 17ef866..be57750 100644 --- a/src/main/antlr4/de/alexgruen/query/generated/Query.g4 +++ b/src/main/antlr/de/alexgruen/query/generated/Query.g4 @@ -21,13 +21,12 @@ */ grammar Query; - -/* - * Parser Rules - */ - @header { - import java.util.*; - } + +@header { + package de.alexgruen.query.generated; + import java.util.*; +} + @members { Set customOperators = new HashSet<>(); @@ -38,18 +37,22 @@ grammar Query; return customOperators.contains(id); } } + compilationUnit : (query|full_search) EOF ; term : -NEGATE? OPEN_BRACKET term CLOSE_BRACKET| -variable term_operation value| +NEGATE? OPEN_BRACKET term CLOSE_BRACKET | +variable term_operation value | +variable term_list_operation value_list | regex_term ; - regex_term : variable MATCH REGEX; +value_list: +OPEN_BRACKET value (COMMA value)* CLOSE_BRACKET; + query: NEGATE? OPEN_BRACKET query CLOSE_BRACKET | OPEN_BRACKET query LOGICAL_OPERATOR query CLOSE_BRACKET| @@ -61,6 +64,9 @@ value: (NUMBER | BOOLEAN_VALUE | TEXT_VALUE | NULL); variable: VAR | COLUMN | TEXT_VALUE; term_operation: TERM_OPERATOR | custom_operator; + +term_list_operation: TERM_LIST_OPERATOR; + custom_operator: VAR {validOperator($VAR.getText())}?; full_search: full_search_part+; @@ -76,7 +82,7 @@ fragment COL_PREFIX : '.'; fragment CHAR : [a-zA-Z]; fragment STRING : '\'' (~('\'')|'\\\'') * '\''|'"' (~('"')|'\\"')* '"'; -fragment UNESCAPED_STRING :~('.' | ' '|')' | '(' | '!' | '-' | '+' | '"' | '\'') ~('\''|' '|')' | '(')*; +fragment UNESCAPED_STRING :~('.' | ' '|')' | '(' | '!' | '-' | '+' | '"' | '\'' | ',') ~('\''|' '|')' | '(' | ',')*; fragment EQ : ('EQ' | 'eq' | '=' | '=='); fragment NE : ('NE' | 'ne' | '!='); fragment LE : ('LE' | 'le' | '<='); @@ -87,16 +93,19 @@ fragment TM : ('TEXT' | 'text' | '*='); REGEX : '/' (~('/') | '\\/')+ '/'; MATCH : ('~=' | '~' ); - +fragment IN : ('IN' | 'in'); +fragment NOT_IN : ('!IN' | '!in'); fragment AND : ('AND' | 'and' | '&' | '&&'); fragment OR : ('OR' | 'or' | '|' | '||'); fragment NOR : ('NOR' | 'nor') ; +fragment XOR : ('XOR' | 'xor') ; fragment TEXT : UNESCAPED_STRING|STRING; fragment VAR_NAME : TEXT ('.' TEXT)*; OPEN_BRACKET: '('; CLOSE_BRACKET: ')'; +COMMA : ','; NEGATE: ('!'|'-'); POSITIVE: '+'; @@ -105,7 +114,9 @@ LOGICAL_OPERATOR : AND | OR | NOR; TERM_OPERATOR : EQ | NE | LE | LT | GT | GE | TM; -NUMBER : '-'? DIGIT+([.,]DIGIT+)?; +TERM_LIST_OPERATOR : IN | NOT_IN; + +NUMBER : '-'? DIGIT+([.]DIGIT+)?; BOOLEAN_VALUE: 'true' | 'false'; TEXT_VALUE : STRING; NULL: 'null' | 'NULL' | 'NA' | 'na'; diff --git a/src/main/java/de/alexgruen/query/DefaultCreator.java b/src/main/java/de/alexgruen/query/DefaultCreator.java deleted file mode 100644 index 3cd0026..0000000 --- a/src/main/java/de/alexgruen/query/DefaultCreator.java +++ /dev/null @@ -1,345 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 Alexander Grün - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package de.alexgruen.query; - -import de.alexgruen.query.compiler.QueryCompilerException; -import de.alexgruen.query.term.Field; -import de.alexgruen.query.term.Value; - -public abstract class DefaultCreator { - - /** - * Query creator for != operator - * - * @param queryNode current query node - * @param field input field - * @param value input value - * @return created query object - */ - public T ne(QueryNode queryNode, Field field, Value value) { - return ne(field, value); - } - - /** - * Query creator for != operator - * - * @param field input field - * @param value input value - * @return created query object - */ - public T ne(Field field, Value value) { - throw new QueryCompilerException("'ne' operator is not allowed"); - } - - /** - * Query creator for == operator - * - * @param field input field - * @param value input value - * @return created query object - */ - public T eq(Field field, Value value) { - throw new QueryCompilerException("'eq' operator is not allowed"); - } - - - /** - * Query creator for == operator - * - * @param queryNode current query node - * @param field input field - * @param value input value - * @return created query object - */ - public T eq(QueryNode queryNode, Field field, Value value) { - return eq(field, value); - } - - /** - * Query creator for >= operator - * - * @param queryNode current query node - * @param field input field - * @param value input value - * @return created query object - */ - public T ge(QueryNode queryNode, Field field, Value value) { - return ge(field, value); - } - - /** - * Query creator for >= operator - * - * @param field input field - * @param value input value - * @return created query object - */ - public T ge(Field field, Value value) { - throw new QueryCompilerException("'ge' operator is not allowed"); - } - - /** - * Query creator for > operator - * - * @param queryNode current query node - * @param field input field - * @param value input value - * @return created query object - */ - public T gt(QueryNode queryNode, Field field, Value value) { - return gt(field, value); - } - - /** - * Query creator for > operator - * - * @param field input field - * @param value input value - * @return created query object - */ - public T gt(Field field, Value value) { - throw new QueryCompilerException("'gt' operator is not allowed"); - } - - /** - * Query creator for < operator - * - * @param queryNode current query node - * @param field input field - * @param value input value - * @return created query object - */ - public T lt(QueryNode queryNode, Field field, Value value) { - return lt(field, value); - } - - /** - * Query creator for < operator - * - * @param field input field - * @param value input value - * @return created query object - */ - public T lt(Field field, Value value) { - throw new QueryCompilerException("'lt' operator is not allowed"); - } - - /** - * Query creator for <= operator - * - * @param queryNode current query node - * @param field input field - * @param value input value - * @return created query object - */ - public T le(QueryNode queryNode, Field field, Value value) { - return le(field, value); - } - - - /** - * Query creator for <= operator - * - * @param field input field - * @param value input value - * @return created query object - */ - public T le(Field field, Value value) { - throw new QueryCompilerException("'le' operator is not allowed"); - } - - /** - * Query creator for the regex operator - * - * @param queryNode current query node - * @param field input field - * @param value input value - * @return created query object - */ - public T regex(QueryNode queryNode, Field field, Value value) { - return regex(field, value); - } - - - /** - * Query creator for the regex operator - * - * @param field input field - * @param value input value - * @return created query object - */ - public T regex(Field field, Value value) { - throw new QueryCompilerException("'regex' operator is not allowed"); - } - - /** - * Query creator for the text operator - * - * @param queryNode current query node - * @param field input field - * @param value input value - * @return created query object - */ - public T text(QueryNode queryNode, Field field, Value value) { - return text(field, value); - } - - /** - * Query creator for the text operator - * - * @param field input field - * @param value input value - * @return created query object - */ - public T text(Field field, Value value) { - throw new QueryCompilerException("'text' operator is not allowed"); - } - - /** - * Logic creator for the negation of a query - * - * @param queryNode current query node - * @param v input query - * @return created query object - */ - public T not(QueryNode queryNode, T v) { - return not(v); - } - - /** - * Logic creator for the negation of a query - * - * @param v input query - * @return created query object - */ - public T not(T v) { - throw new QueryCompilerException("'not' operator is not allowed"); - } - - /** - * Logic creator for the AND concatenation of queries - * - * @param queryNode current query node - * @param v input queries - * @return created query object - */ - public T and(QueryNode queryNode, T... v) { - return and(v); - } - - /** - * Logic creator for the AND concatenation of queries - * - * @param v input queries - * @return created query object - */ - public T and(T... v) { - throw new QueryCompilerException("'and' operator is not allowed"); - } - - /** - * Logic creator for the OR concatenation of queries - * - * @param queryNode current query node - * @param v input queries - * @return created query object - */ - public T or(QueryNode queryNode, T... v) { - return or(v); - } - - /** - * Logic creator for the OR concatenation of queries - * - * @param v input queries - * @return created query object - */ - public T or(T... v) { - throw new QueryCompilerException("'or' operator is not allowed"); - } - - /** - * Logic creator for the XOR concatenation of queries - * - * @param queryNode current query node - * @param v input queries - * @return created query object - */ - public T xor(QueryNode queryNode, T... v) { - return xor(v); - } - - /** - * Logic creator for the XOR concatenation of queries - * - * @param v input queries - * @return created query object - */ - public T xor(T... v) { - throw new QueryCompilerException("'xor' operator is not allowed"); - } - - /** - * Logic creator for the NOR concatenation of queries - * - * @param queryNode current query node - * @param v input queries - * @return created query object - */ - public T nor(QueryNode queryNode, T... v) { - return nor(v); - } - - /** - * Logic creator for the NOR concatenation of queries - * - * @param v input queries - * @return created query object - */ - public T nor(T... v) { - throw new QueryCompilerException("'nor' operator is not allowed"); - } - - /** - * Term creator for fulltext search queries - * - * @param value input value - * @return created query object - */ - public T fullSearch(Value value) { - throw new QueryCompilerException("fullSearch is not allowed"); - } - - /** - * Term creator for empty queries (match all) - * - * @return created query object - */ - public T empty() { - throw new QueryCompilerException("empty is not defined"); - } - - -} diff --git a/src/main/java/de/alexgruen/query/LogicalOperator.java b/src/main/java/de/alexgruen/query/LogicalOperator.java index ba5d45b..a63076e 100644 --- a/src/main/java/de/alexgruen/query/LogicalOperator.java +++ b/src/main/java/de/alexgruen/query/LogicalOperator.java @@ -27,11 +27,12 @@ /** * Logic operators are used to concatenate multiple array (e.g. AND, OR, XOR,...) */ -public class LogicalOperator extends Operator{ +public class LogicalOperator extends Operator { /** * Creates a logic operator from a name and array of aliases - * @param name operator name + * + * @param name operator name * @param aliases operator aliases */ public LogicalOperator(String name, String... aliases) { diff --git a/src/main/java/de/alexgruen/query/LogicalOperators.java b/src/main/java/de/alexgruen/query/LogicalOperators.java index a8da12c..b4ab08e 100644 --- a/src/main/java/de/alexgruen/query/LogicalOperators.java +++ b/src/main/java/de/alexgruen/query/LogicalOperators.java @@ -67,9 +67,9 @@ public class LogicalOperators { NOT }; - /** * Returns a list with the default operators + * * @return default operators */ public static List getDefaultOperations() { diff --git a/src/main/java/de/alexgruen/query/Operator.java b/src/main/java/de/alexgruen/query/Operator.java index a1628ea..9d6c829 100644 --- a/src/main/java/de/alexgruen/query/Operator.java +++ b/src/main/java/de/alexgruen/query/Operator.java @@ -32,17 +32,18 @@ */ public class Operator { //Name of the operator - private String name; + private final String name; //Aliases of the operator - private String[] aliases; + private final String[] aliases; /** * Creates an operator from a name and array of aliases - * @param name operator name + * + * @param name operator name * @param aliases operator aliases */ - public Operator(String name, String... aliases){ + public Operator(String name, String... aliases) { this.name = name; this.aliases = aliases; } @@ -50,6 +51,7 @@ public Operator(String name, String... aliases){ /** * Returns the aliases + * * @return aliases */ public String[] getAliases() { @@ -58,6 +60,7 @@ public String[] getAliases() { /** * Returns the name + * * @return name */ public String getName() { @@ -66,7 +69,8 @@ public String getName() { /** * True if o equals this operator - * @param o other operator + * + * @param o another operator * @return true if equal */ @Override @@ -75,11 +79,12 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; Operator operator = (Operator) o; return Objects.equals(name, operator.name) && - Arrays.equals(aliases, operator.aliases); + Arrays.equals(aliases, operator.aliases); } /** * Calculates the hashcode of this operator object + * * @return hashcode */ @Override diff --git a/src/main/java/de/alexgruen/query/PrintQuery.java b/src/main/java/de/alexgruen/query/PrintQuery.java index e3b10c6..89c464b 100644 --- a/src/main/java/de/alexgruen/query/PrintQuery.java +++ b/src/main/java/de/alexgruen/query/PrintQuery.java @@ -27,10 +27,11 @@ /** * Simple queries that create a string representation of the query */ -public abstract class PrintQuery implements Query{ +public abstract class PrintQuery implements Query { /** * Returns the string representation of this query + * * @return string representation */ @Override diff --git a/src/main/java/de/alexgruen/query/PrintQueryCreator.java b/src/main/java/de/alexgruen/query/PrintQueryCreator.java index 0542a82..2d6cb25 100644 --- a/src/main/java/de/alexgruen/query/PrintQueryCreator.java +++ b/src/main/java/de/alexgruen/query/PrintQueryCreator.java @@ -27,19 +27,21 @@ import de.alexgruen.query.term.Field; import de.alexgruen.query.term.Value; +import java.util.List; + /** * Default creator for {@link PrintQuery} */ -public class PrintQueryCreator extends DefaultCreator { +public class PrintQueryCreator implements QueryCreator { public static String value2string(Value value) { if (value.isNull()) { return null; } - return value.isString() ? String.format("'%s'", value.toString()) : value.toString(); + return value.isString() ? String.format("'%s'", value) : value.toString(); } @Override - public PrintQuery ne(Field field, Value value) { + public PrintQuery ne(QueryNode node, Field field, Value value) { return new PrintQuery() { @Override public String toString() { @@ -49,7 +51,7 @@ public String toString() { } @Override - public PrintQuery eq(Field field, Value value) { + public PrintQuery eq(QueryNode node, Field field, Value value) { return new PrintQuery() { @Override public String toString() { @@ -59,7 +61,7 @@ public String toString() { } @Override - public PrintQuery gt(Field field, Value value) { + public PrintQuery gt(QueryNode node, Field field, Value value) { return new PrintQuery() { @Override public String toString() { @@ -69,7 +71,7 @@ public String toString() { } @Override - public PrintQuery ge(Field field, Value value) { + public PrintQuery ge(QueryNode node, Field field, Value value) { return new PrintQuery() { @Override public String toString() { @@ -79,7 +81,7 @@ public String toString() { } @Override - public PrintQuery lt(Field field, Value value) { + public PrintQuery lt(QueryNode node, Field field, Value value) { return new PrintQuery() { @Override public String toString() { @@ -89,7 +91,7 @@ public String toString() { } @Override - public PrintQuery le(Field field, Value value) { + public PrintQuery le(QueryNode node, Field field, Value value) { return new PrintQuery() { @Override public String toString() { @@ -99,7 +101,7 @@ public String toString() { } @Override - public PrintQuery regex(Field field, Value value) { + public PrintQuery regex(QueryNode node, Field field, Value value) { return new PrintQuery() { @Override public String toString() { @@ -109,7 +111,7 @@ public String toString() { } @Override - public PrintQuery text(Field field, Value value) { + public PrintQuery text(QueryNode node, Field field, Value value) { return new PrintQuery() { @Override public String toString() { @@ -119,19 +121,40 @@ public String toString() { } @Override - public PrintQuery not(PrintQuery a) { + public PrintQuery in(QueryNode node, Field field, Value list) { return new PrintQuery() { @Override public String toString() { - return String.format("!%s", a.toString()); + String values = String.join(", ", list.getList().stream().map(PrintQueryCreator::value2string).toList()); + return String.format("(%s in [%s])", field, values); + } + }; + } + + @Override + public PrintQuery notIn(QueryNode node, Field field, Value list) { + return new PrintQuery() { + @Override + public String toString() { + String values = String.join(", ", list.getList().stream().map(PrintQueryCreator::value2string).toList()); + return String.format("(%s !in [%s])", field, values); } }; } + @Override + public PrintQuery not(QueryNode node, PrintQuery a) { + return new PrintQuery() { + @Override + public String toString() { + return String.format("!%s", a.toString()); + } + }; + } @Override - public PrintQuery and(PrintQuery... p) { + public PrintQuery and(QueryNode node, PrintQuery... p) { return new PrintQuery() { @Override @@ -151,7 +174,7 @@ public String toString() { } @Override - public PrintQuery or(PrintQuery... p) { + public PrintQuery or(QueryNode node, PrintQuery... p) { return new PrintQuery() { @Override public String toString() { @@ -170,7 +193,7 @@ public String toString() { } @Override - public PrintQuery xor(PrintQuery... p) { + public PrintQuery xor(QueryNode node, PrintQuery... p) { return new PrintQuery() { @Override public String toString() { @@ -189,7 +212,7 @@ public String toString() { } @Override - public PrintQuery nor(PrintQuery... p) { + public PrintQuery nor(QueryNode node, PrintQuery... p) { return new PrintQuery() { @Override public String toString() { @@ -217,7 +240,13 @@ public String toString() { }; } - - - -} \ No newline at end of file + @Override + public PrintQuery empty() { + return new PrintQuery() { + @Override + public String toString() { + return "*"; + } + }; + } +} diff --git a/src/main/java/de/alexgruen/query/QueryCreator.java b/src/main/java/de/alexgruen/query/QueryCreator.java new file mode 100644 index 0000000..89263d4 --- /dev/null +++ b/src/main/java/de/alexgruen/query/QueryCreator.java @@ -0,0 +1,170 @@ +package de.alexgruen.query; + +import de.alexgruen.query.term.Field; +import de.alexgruen.query.term.Value; + +import java.util.List; + +public interface QueryCreator { + /** + * Query creator for != operator + * + * @param queryNode current query node + * @param field input field + * @param value input value + * @return created query object + */ + T ne(QueryNode queryNode, Field field, Value value); + + /** + * Query creator for == operator + * + * @param queryNode current query node + * @param field input field + * @param value input value + * @return created query object + */ + T eq(QueryNode queryNode, Field field, Value value); + + /** + * Query creator for >= operator + * + * @param queryNode current query node + * @param field input field + * @param value input value + * @return created query object + */ + T ge(QueryNode queryNode, Field field, Value value); + + /** + * Query creator for > operator + * + * @param queryNode current query node + * @param field input field + * @param value input value + * @return created query object + */ + T gt(QueryNode queryNode, Field field, Value value); + + /** + * Query creator for < operator + * + * @param queryNode current query node + * @param field input field + * @param value input value + * @return created query object + */ + T lt(QueryNode queryNode, Field field, Value value); + + /** + * Query creator for <= operator + * + * @param queryNode current query node + * @param field input field + * @param value input value + * @return created query object + */ + T le(QueryNode queryNode, Field field, Value value); + + /** + * Query creator for the regex operator + * + * @param queryNode current query node + * @param field input field + * @param value input value + * @return created query object + */ + T regex(QueryNode queryNode, Field field, Value value); + + /** + * Query creator for the text operator + * + * @param queryNode current query node + * @param field input field + * @param value input value + * @return created query object + */ + T text(QueryNode queryNode, Field field, Value value); + + + /** + * Query creator for the in operator + * + * @param queryNode current query node + * @param field input field + * @param list input value list + * @return created query object + */ + T in(QueryNode queryNode, Field field, Value list); + + /** + * Query creator for the not in operator + * + * @param queryNode current query node + * @param field input field + * @param list input value list + * @return created query object + */ + T notIn(QueryNode queryNode, Field field, Value list); + + /** + * Logic creator for the negation of a query + * + * @param queryNode current query node + * @param v input query + * @return created query object + */ + T not(QueryNode queryNode, T v); + + /** + * Logic creator for the AND concatenation of queries + * + * @param queryNode current query node + * @param v input queries + * @return created query object + */ + T and(QueryNode queryNode, T[] v); + + /** + * Logic creator for the OR concatenation of queries + * + * @param queryNode current query node + * @param v input queries + * @return created query object + */ + T or(QueryNode queryNode, T[] v); + + /** + * Logic creator for the XOR concatenation of queries + * + * @param queryNode current query node + * @param v input queries + * @return created query object + */ + T xor(QueryNode queryNode, T[] v); + + /** + * Logic creator for the NOR concatenation of queries + * + * @param queryNode current query node + * @param v input queries + * @return created query object + */ + T nor(QueryNode queryNode, T[] v); + + /** + * Term creator for fulltext search queries + * + * @param value input value + * @return created query object + */ + T fullSearch(Value value); + + /** + * Term creator for empty queries (match all) + * + * @return created query object + */ + T empty(); + +} diff --git a/src/main/java/de/alexgruen/query/QueryNode.java b/src/main/java/de/alexgruen/query/QueryNode.java index d435d01..980e6ce 100644 --- a/src/main/java/de/alexgruen/query/QueryNode.java +++ b/src/main/java/de/alexgruen/query/QueryNode.java @@ -33,16 +33,17 @@ public class QueryNode { private LogicalOperator operator; private Term term; private List children = new ArrayList<>(); - private Map attributes = new HashMap<>(); + private final Map attributes = new HashMap<>(); public QueryNode() { } /** * Creates a node form all possible variables - * @param negate negate + * + * @param negate negate * @param operator operator - * @param term term + * @param term term * @param children children */ public QueryNode(boolean negate, LogicalOperator operator, Term term, List children) { @@ -54,6 +55,7 @@ public QueryNode(boolean negate, LogicalOperator operator, Term term, List getAttributes() { @@ -79,21 +83,23 @@ public Map getAttributes() { /** * Returns an attribute value or null if no attribute with the specified name is found. + * * @param name attribute name * @return value */ - public Object getAttribute(String name){ + public Object getAttribute(String name) { return attributes.get(name); } /** * Sets the value of an attribute. Returns the previously set value or null - * @param name attribute name + * + * @param name attribute name * @param value attribute value * @return previous value or null */ - public Object setAttribute(String name, Object value){ - return attributes.put(name,value); + public Object setAttribute(String name, Object value) { + return attributes.put(name, value); } /** * Creates a new leaf node from a term @@ -102,6 +108,7 @@ public Object setAttribute(String name, Object value){ /** * Returns true if this node is negated + * * @return true if negated */ public boolean isNegate() { @@ -111,6 +118,7 @@ public boolean isNegate() { /** * Negates this node + * * @param negate negate */ public void setNegate(boolean negate) { @@ -119,6 +127,7 @@ public void setNegate(boolean negate) { /** * Returns the operator {@link LogicalOperator} of this node + * * @return operator */ public LogicalOperator getOperator() { @@ -127,6 +136,7 @@ public LogicalOperator getOperator() { /** * Sets the operator {@link LogicalOperator} of this node + * * @param operator oprator */ public void setOperator(LogicalOperator operator) { @@ -136,6 +146,7 @@ public void setOperator(LogicalOperator operator) { /** * Returns the term {@link Term} for this node + * * @return term */ public Term getTerm() { @@ -145,6 +156,7 @@ public Term getTerm() { /** * Sets the term {@link Term} for this node + * * @param term term */ public void setTerm(Term term) { @@ -154,6 +166,7 @@ public void setTerm(Term term) { /** * Returns all child nodes + * * @return all child nodes */ public List getChildren() { @@ -163,6 +176,7 @@ public List getChildren() { /** * Sets the child nodes + * * @param children child nodes */ public void setChildren(List children) { @@ -171,10 +185,11 @@ public void setChildren(List children) { /** * Returns the label (in the query tree) for this node. + * * @return label of the node */ public String getLabel() { - if(term == null){ + if (term == null) { return operator.getName(); } return term.toString(); diff --git a/src/main/java/de/alexgruen/query/QueryPrinter.java b/src/main/java/de/alexgruen/query/QueryPrinter.java index ee4a38b..2edbfae 100644 --- a/src/main/java/de/alexgruen/query/QueryPrinter.java +++ b/src/main/java/de/alexgruen/query/QueryPrinter.java @@ -39,29 +39,30 @@ public class QueryPrinter { private String labelPrefix = ""; private String labelPostfix = ""; private String lineSeparator = "\n"; - private Function indentFunction; - private QueryPrinter(){ + private Function indentFunction; + + private QueryPrinter() { } - public static QueryPrinter.Builder create(){ + public static QueryPrinter.Builder create() { return Builder.create(); } public String toString(QueryNode root) { StringBuilder sb = new StringBuilder(); - printRec("",true,root,sb,false); + printRec("", true, root, sb, false); return sb.toString(); } - private void printRec(String prefix,boolean isRoot, QueryNode node, StringBuilder sb, boolean lastChild){ + private void printRec(String prefix, boolean isRoot, QueryNode node, StringBuilder sb, boolean lastChild) { StringBuilder nodeLine = new StringBuilder(); - if(!isRoot){ + if (!isRoot) { nodeLine.append(lastChild ? cornerLast : tLine); } nodeLine.append(horizontalLine); - if(!node.getChildren().isEmpty()){ + if (!node.getChildren().isEmpty()) { nodeLine.append(cornerNext); } nodeLine.append(" "); @@ -72,29 +73,28 @@ private void printRec(String prefix,boolean isRoot, QueryNode node, StringBuilde sb.append(prefix); sb.append(nodeLine); sb.append(lineSeparator); - String verticalConnection = String.format("%s%s",verticalLine, + String verticalConnection = String.format("%s%s", verticalLine, StringUtil.repeatChar( - cornerNext.length()+horizontalLine.length()-verticalLine.length(), ' ' + cornerNext.length() + horizontalLine.length() - verticalLine.length(), ' ' )); int i = 0; - for(QueryNode child : node.getChildren()){ - printRec(prefix + (lastChild || isRoot ? indentation : verticalConnection), false,child,sb, i++ == node.getChildren().size() - 1); + for (QueryNode child : node.getChildren()) { + printRec(prefix + (lastChild || isRoot ? indentation : verticalConnection), false, child, sb, i++ == node.getChildren().size() - 1); } } - public static final class Builder { - private String horizontalLine = "──"; - private String verticalLine = "│"; - private String tLine = "├"; + private String horizontalLine = "──"; + private String verticalLine = "│"; + private String tLine = "├"; private String cornerLast = "└"; private String cornerNext = "┐"; private String labelPrefix = ""; private String labelPostfix = ""; private String lineSeparator = "\n"; - private Function indentFunction = (l) -> StringUtil.repeatChar(l.length() - 2,' '); + private Function indentFunction = (l) -> StringUtil.repeatChar(l.length() - 2, ' '); private Builder() { } @@ -122,14 +122,17 @@ public Builder withCornerNext(String cornerNext) { this.cornerNext = cornerNext; return this; } + public Builder withCornerLast(String cornerLast) { this.cornerLast = cornerLast; return this; } - public Builder witIndentFunction(Function indentFunction) { + + public Builder witIndentFunction(Function indentFunction) { this.indentFunction = indentFunction; return this; } + public Builder withLabelPrefix(String labelPrefix) { this.labelPrefix = labelPrefix; return this; @@ -146,7 +149,6 @@ public Builder withLineSeparator(String lineSeparator) { } - public QueryPrinter build() { QueryPrinter queryPrinter = new QueryPrinter(); queryPrinter.labelPrefix = this.labelPrefix; diff --git a/src/main/java/de/alexgruen/query/QueryTree.java b/src/main/java/de/alexgruen/query/QueryTree.java index ffb9b66..1773d0d 100644 --- a/src/main/java/de/alexgruen/query/QueryTree.java +++ b/src/main/java/de/alexgruen/query/QueryTree.java @@ -28,7 +28,7 @@ public class QueryTree { private QueryNode root; - public QueryTree(QueryNode root){ + public QueryTree(QueryNode root) { this.root = root; } diff --git a/src/main/java/de/alexgruen/query/compiler/QueryCompiler.java b/src/main/java/de/alexgruen/query/compiler/QueryCompiler.java index b5b6748..bcc987c 100644 --- a/src/main/java/de/alexgruen/query/compiler/QueryCompiler.java +++ b/src/main/java/de/alexgruen/query/compiler/QueryCompiler.java @@ -35,19 +35,20 @@ public class QueryCompiler { - private QueryContext context; - private List optimizations; - private QueryTreeCompiler queryTreeCompiler; + private final QueryContext context; + private final List optimizations; + private final QueryTreeCompiler queryTreeCompiler; protected QueryCompiler(QueryContext context, List optimizations) { this.context = context; this.optimizations = optimizations; - this.queryTreeCompiler = new QueryTreeCompiler(context); + this.queryTreeCompiler = new QueryTreeCompiler<>(context); } /** * Create a new {@link QueryCompilerBuilder} - * @param cl target class + * + * @param cl target class * @param target type * @return query compiler instance */ @@ -57,18 +58,20 @@ public static QueryCompilerBuilder create(Class cl) { /** - * Create a new {@link QueryCompilerBuilder} using a {@link DefaultCreator}. - * @param cl target class - * @param defaultCreator default operation creator - * @param target type + * Create a new {@link QueryCompilerBuilder} using a {@link QueryCreator}. + * + * @param cl target class + * @param queryCreator default operation creator + * @param target type * @return query compiler instance */ - public static QueryCompilerBuilder createDefault(Class cl, DefaultCreator defaultCreator) { - return QueryCompilerBuilder.createDefault(cl, defaultCreator); + public static QueryCompilerBuilder createDefault(Class cl, QueryCreator queryCreator) { + return QueryCompilerBuilder.createDefault(cl, queryCreator); } /** * Returns the {@link QueryContext} + * * @return context */ public QueryContext getContext() { @@ -77,6 +80,7 @@ public QueryContext getContext() { /** * Returns all assigned optimizations ({@link QueryOptimization} + * * @return optimizations */ public List getOptimizations() { @@ -85,6 +89,7 @@ public List getOptimizations() { /** * Compiles an input string to the target class + * * @param str input string * @return object of target class */ @@ -95,6 +100,7 @@ public T compile(String str) { /** * Compiles an input string to a query tree ({@link QueryTree} + * * @param str input string * @return query tree */ @@ -107,6 +113,7 @@ public QueryTree compileTree(String str) { /** * Converts a query tree to the target class + * * @param tree input query tree * @return object of target class */ @@ -116,15 +123,16 @@ public T compile(QueryTree tree) { /** * Recursive function for converting query trees to target objects + * * @param node current node * @return target object */ private T compileTreeRec(QueryNode node) { - if(node.getChildren().isEmpty() && node.getTerm() == null){ - if(context.getEmptyCreator() == null){ + if (node.getChildren().isEmpty() && node.getTerm() == null) { + if (context.getEmptyCreator() == null) { throw new QueryCompilerException("no empty creator defined"); } - return context.getEmptyCreator().create(node,null,null); + return context.getEmptyCreator().create(node, null, null); } //return if node contains term if (node.getTerm() != null) { @@ -136,7 +144,7 @@ private T compileTreeRec(QueryNode node) { return t; } - //Create array for results of child terms + //Create an array for results of child terms T[] terms = CompilerUtil.createArray(context.getCl(), node.getChildren().size()); //recursive calculation of child terms @@ -144,7 +152,7 @@ private T compileTreeRec(QueryNode node) { terms[i] = compileTreeRec(node.getChildren().get(i)); } - //Create new term using child terms and logical operation + //Create a new term using child terms and logical operation LogicCreator logicCreator = context.getLogicCreator(node.getOperator()); T t = logicCreator.create(node, terms); if (node.isNegate()) { @@ -154,7 +162,8 @@ private T compileTreeRec(QueryNode node) { } /** - * Use {@link LogicCreator} to negate target object + * Use {@link LogicCreator} to negate a target object + * * @param t input object * @return negated object */ @@ -165,7 +174,8 @@ private T negate(QueryNode node, T t) { } /** - * Create object of target class for a term using the creator ({@link TermCreator}) specified in the {@link QueryContext} + * Create an object of target class for a term using the creator ({@link TermCreator}) specified in the {@link QueryContext} + * * @param term input term * @return object of target class */ @@ -176,7 +186,6 @@ private T createTerm(QueryNode node, Term term) { /** * Apply all optimizations ({@link QueryOptimization}) specified in the {@link QueryContext} to a {@link QueryTree} - * @param tree */ private void optimize(QueryTree tree) { for (QueryOptimization optimization : optimizations) { diff --git a/src/main/java/de/alexgruen/query/compiler/QueryCompilerBuilder.java b/src/main/java/de/alexgruen/query/compiler/QueryCompilerBuilder.java index 832c66f..03e33fb 100644 --- a/src/main/java/de/alexgruen/query/compiler/QueryCompilerBuilder.java +++ b/src/main/java/de/alexgruen/query/compiler/QueryCompilerBuilder.java @@ -24,10 +24,7 @@ package de.alexgruen.query.compiler; -import de.alexgruen.query.DefaultCreator; -import de.alexgruen.query.LogicalOperator; -import de.alexgruen.query.LogicalOperators; -import de.alexgruen.query.Query; +import de.alexgruen.query.*; import de.alexgruen.query.creator.LogicCreator; import de.alexgruen.query.creator.OperatorCreatorMap; import de.alexgruen.query.creator.TermCreator; @@ -40,10 +37,10 @@ import java.util.List; public class QueryCompilerBuilder { - private OperatorCreatorMap> termCreators = new OperatorCreatorMap<>(); - private OperatorCreatorMap> logicCreators = new OperatorCreatorMap<>(); - private Class cl; - private List optimizations = new ArrayList<>(); + private final OperatorCreatorMap> termCreators = new OperatorCreatorMap<>(); + private final OperatorCreatorMap> logicCreators = new OperatorCreatorMap<>(); + private final Class cl; + private final List optimizations = new ArrayList<>(); private TermCreator emptyCreator; private QueryCompilerBuilder(Class cl) { @@ -62,33 +59,33 @@ public static QueryCompilerBuilder create(Class cl) { } /** - * Create a new {@link QueryCompilerBuilder} for a specified query type using a {@link DefaultCreator}. - * All default logic and term operations can be defined using the {@link DefaultCreator}. + * Create a new {@link QueryCompilerBuilder} for a specified query type using a {@link QueryCreator}. + * All default logic and term operations can be defined using the {@link QueryCreator}. * * @param cl query class - * @param defaultCreator default creator + * @param queryCreator default creator * @param query type * @return query compiler builder */ - public static QueryCompilerBuilder createDefault(Class cl, DefaultCreator defaultCreator) { + public static QueryCompilerBuilder createDefault(Class cl, QueryCreator queryCreator) { QueryCompilerBuilder queryCompilerBuilder = create(cl); - queryCompilerBuilder.withDefaultCreator(defaultCreator); + queryCompilerBuilder.withQueryCreator(queryCreator); queryCompilerBuilder.withOptimization(new RemoveRedundantBrackets()); return queryCompilerBuilder; } /** - * Use a {@link DefaultCreator} to define all default logic and term operations + * Use a {@link QueryCreator} to define all default logic and term operations * * @param dc default creator * @return self for method chaining */ - public QueryCompilerBuilder withDefaultCreator(DefaultCreator dc) { - withANDCreator((dc::and)); - withORCreator((dc::or)); - withXORCreator((dc::xor)); - withNORCreator((dc::nor)); - withNOTCreator((node, children) -> dc.not(children[0])); + public QueryCompilerBuilder withQueryCreator(QueryCreator dc) { + withANDCreator(dc::and); + withORCreator(dc::or); + withXORCreator(dc::xor); + withNORCreator(dc::nor); + withNOTCreator((node, children) -> dc.not(node, children[0])); withTermCreator(TermOperators.EQ, dc::eq); withTermCreator(TermOperators.NE, dc::ne); withTermCreator(TermOperators.LT, dc::lt); @@ -97,6 +94,8 @@ public QueryCompilerBuilder withDefaultCreator(DefaultCreator dc) { withTermCreator(TermOperators.GE, dc::ge); withTermCreator(TermOperators.REGEX, dc::regex); withTermCreator(TermOperators.TEXT, dc::text); + withTermCreator(TermOperators.IN, dc::in); + withTermCreator(TermOperators.NOT_IN, dc::notIn); withTermCreator(TermOperators.FULL_TEXT, (n, f, v) -> dc.fullSearch(v)); withEmptyCreator((n, f, v) -> dc.empty()); return this; diff --git a/src/main/java/de/alexgruen/query/compiler/QueryCompilerErrorListener.java b/src/main/java/de/alexgruen/query/compiler/QueryCompilerErrorListener.java index b725ffc..446cb2f 100644 --- a/src/main/java/de/alexgruen/query/compiler/QueryCompilerErrorListener.java +++ b/src/main/java/de/alexgruen/query/compiler/QueryCompilerErrorListener.java @@ -34,7 +34,7 @@ */ public class QueryCompilerErrorListener extends BaseErrorListener { //Query String that lead to an error - private String queryString; + private final String queryString; public QueryCompilerErrorListener(String queryString) { this.queryString = queryString; @@ -43,12 +43,13 @@ public QueryCompilerErrorListener(String queryString) { /** * Listener method that gets evoked if an error occurs during query compilation. * This method throws {@link QueryCompilerException} if called by the compiler - * @param recognizer recognizer - * @param offendingSymbol offending symbol - * @param line line in input string - * @param charPositionInLine char position in input line - * @param msg error message - * @param e exception + * + * @param recognizer recognizer + * @param offendingSymbol offending symbol + * @param line line in input string + * @param charPositionInLine char position in the input line + * @param msg error message + * @param e exception */ @Override public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { @@ -57,6 +58,7 @@ public void syntaxError(Recognizer recognizer, Object offendingSymbol, int /** * Returns the query string that caused an error during compilation + * * @return */ public String getQueryString() { diff --git a/src/main/java/de/alexgruen/query/compiler/QueryCompilerException.java b/src/main/java/de/alexgruen/query/compiler/QueryCompilerException.java index 00a35df..ddf0733 100644 --- a/src/main/java/de/alexgruen/query/compiler/QueryCompilerException.java +++ b/src/main/java/de/alexgruen/query/compiler/QueryCompilerException.java @@ -26,7 +26,7 @@ /** * Created by Alex on 21.05.2017. - * + *

* Exception that is thrown if an error occurs during query compilation */ diff --git a/src/main/java/de/alexgruen/query/compiler/QueryContext.java b/src/main/java/de/alexgruen/query/compiler/QueryContext.java index a8d66de..22ae4ff 100644 --- a/src/main/java/de/alexgruen/query/compiler/QueryContext.java +++ b/src/main/java/de/alexgruen/query/compiler/QueryContext.java @@ -35,13 +35,14 @@ /** * Contains all creators an information required by the query compiler + * * @param type of compiled query object */ public class QueryContext { - private OperatorCreatorMap> termCreators; - private OperatorCreatorMap> logicCreators; - private TermCreator emptyCreator; - private Class cl; + private final OperatorCreatorMap> termCreators; + private final OperatorCreatorMap> logicCreators; + private final TermCreator emptyCreator; + private final Class cl; protected QueryContext(OperatorCreatorMap> termCreators, OperatorCreatorMap> logicCreators, @@ -55,6 +56,7 @@ protected QueryContext(OperatorCreatorMap> termCrea /** * Returns the {@link TermOperator} defined by the input name or alias + * * @param op input operator name or alias * @return term operator */ @@ -64,6 +66,7 @@ public TermOperator getTermOperator(String op) { /** * Returns the {@link LogicalOperator} defined by the input name or alias + * * @param op input operator name or alias * @return logic operator */ @@ -74,33 +77,37 @@ public LogicalOperator getLogicalOperator(String op) { /** * Returns the {@link TermCreator} associated with the respective {@link TermOperator}. * Returns null if no creator is found + * * @param operator input operator * @return term creator or null if no creator found */ - public TermCreator getTermCreator(TermOperator operator){ + public TermCreator getTermCreator(TermOperator operator) { return termCreators.getCreator(operator); } /** * Returns the {@link LogicCreator} associated with the respective {@link LogicalOperator}. * Returns null if no creator is found + * * @param operator input operator * @return logic creator or null if no creator found */ - public LogicCreator getLogicCreator(LogicalOperator operator){ + public LogicCreator getLogicCreator(LogicalOperator operator) { return logicCreators.getCreator(operator); } /** * Return all names and aliases of available {@link TermOperator} + * * @return list of names and aliases */ - public List getAllTermOperatorAliases(){ + public List getAllTermOperatorAliases() { return termCreators.getAllAliases(); } /** * Returns the {@link TermCreator} assigned for empty terms (match all) + * * @return term creator */ public TermCreator getEmptyCreator() { @@ -109,6 +116,7 @@ public TermCreator getEmptyCreator() { /** * Returns the class of resulting queries + * * @return class of resulting queries */ public Class getCl() { diff --git a/src/main/java/de/alexgruen/query/compiler/QueryTreeCompiler.java b/src/main/java/de/alexgruen/query/compiler/QueryTreeCompiler.java index 8d332ad..b8078bb 100644 --- a/src/main/java/de/alexgruen/query/compiler/QueryTreeCompiler.java +++ b/src/main/java/de/alexgruen/query/compiler/QueryTreeCompiler.java @@ -39,17 +39,18 @@ /** * Created by Alex on 18.05.2017. */ -public class QueryTreeCompiler { +public class QueryTreeCompiler { - private QueryContext context; + private final QueryContext context; - public QueryTreeCompiler(QueryContext context) { + public QueryTreeCompiler(QueryContext context) { this.context = context; } /** * Compiles an input query string into a {@link QueryTree}. * A {@link QueryCompilerException} is thrown if an error occurs. + * * @param queryString input string * @return resulting query tree */ @@ -57,7 +58,7 @@ public QueryTree compile(String queryString) { if (context == null) { throw new RuntimeException("context required"); } - + queryString = queryString.trim(); //return "empty" query tree if input string is empty if (queryString.isEmpty()) { diff --git a/src/main/java/de/alexgruen/query/compiler/RegexTermVisitor.java b/src/main/java/de/alexgruen/query/compiler/RegexTermVisitor.java index 7815ec1..153869f 100644 --- a/src/main/java/de/alexgruen/query/compiler/RegexTermVisitor.java +++ b/src/main/java/de/alexgruen/query/compiler/RegexTermVisitor.java @@ -42,7 +42,8 @@ public class RegexTermVisitor extends QueryBaseVisitor { /** * Creates a {@link QueryNode} that represents a regex term. - * An exception is thrown if the context can not be converted to a node + * An exception is thrown if the context cannot be converted to a node + * * @param ctx input regex context * @return query node */ @@ -52,25 +53,22 @@ public QueryNode visitRegex_term(QueryParser.Regex_termContext ctx) { Pattern pattern = convertPattern(ctx.REGEX().getText()); Value value = new Value(pattern); - return new QueryNode(new Term(field,TermOperators.REGEX, value)); + return new QueryNode(new Term(field, TermOperators.REGEX, value)); } /** * Converts an input string to a {@link Pattern}. * A {@link QueryCompilerException} is thrown if the input string is not in the right format + * * @param text input string * @return compiled pattern */ - private static Pattern convertPattern(String text){ + private static Pattern convertPattern(String text) { String regex = text; - if(!regex.startsWith("/") || ! regex.endsWith("/")){ - throw new QueryCompilerException(String.format("wrong pattern format: %s",text)); + if (!regex.startsWith("/") || !regex.endsWith("/")) { + throw new QueryCompilerException(String.format("wrong pattern format: %s", text)); } - regex = regex.substring(1,regex.length()-1); + regex = regex.substring(1, regex.length() - 1); return Pattern.compile(regex); - } - - - } diff --git a/src/main/java/de/alexgruen/query/compiler/TermQueryVisitor.java b/src/main/java/de/alexgruen/query/compiler/TermQueryVisitor.java index f49557e..740c6f5 100644 --- a/src/main/java/de/alexgruen/query/compiler/TermQueryVisitor.java +++ b/src/main/java/de/alexgruen/query/compiler/TermQueryVisitor.java @@ -33,15 +33,16 @@ */ public class TermQueryVisitor extends QueryBaseVisitor { - private QueryContext context; + private final QueryContext context; - public TermQueryVisitor(QueryContext context) { + public TermQueryVisitor(QueryContext context) { this.context = context; } /** * Converts the root compilation context to the root query node + * * @param ctx root context * @return root node */ @@ -59,6 +60,7 @@ public QueryNode visitCompilationUnit(QueryParser.CompilationUnitContext ctx) { /** * Uses a {@link TextSearchVisitor} to convert a full text search context to a query node + * * @param ctx full search context * @return query node */ @@ -68,10 +70,15 @@ public QueryNode visitFull_search(QueryParser.Full_searchContext ctx) { return textSearchVisitor.visitFull_search(ctx); } + @Override + public QueryNode visitValue_list(QueryParser.Value_listContext ctx) { + return super.visitValue_list(ctx); + } /** * Creates a query node from a query context. * Recursively creates all child query nodes for the root query context. + * * @param ctx root query context * @return query node */ @@ -103,6 +110,7 @@ public QueryNode visitQuery(QueryParser.QueryContext ctx) { /** * Recursive function to create query nodes from a query context + * * @param ctx query context * @return query node with all child query nodes */ @@ -117,7 +125,7 @@ private QueryNode visitQueryRecursive(QueryParser.QueryContext ctx) { } if (ctx.query().size() == 1) { - QueryNode n = visitQueryRecursive(ctx.query(0)); + QueryNode n = visitQueryRecursive(ctx.query(0)); if (ctx.NEGATE() != null) { n.setNegate(true); } diff --git a/src/main/java/de/alexgruen/query/compiler/TermVisitor.java b/src/main/java/de/alexgruen/query/compiler/TermVisitor.java index b113d10..a73c77e 100644 --- a/src/main/java/de/alexgruen/query/compiler/TermVisitor.java +++ b/src/main/java/de/alexgruen/query/compiler/TermVisitor.java @@ -39,9 +39,9 @@ */ public class TermVisitor extends QueryBaseVisitor { - private QueryContext context; + private final QueryContext context; - public TermVisitor(QueryContext context) { + public TermVisitor(QueryContext context) { this.context = context; } @@ -49,14 +49,18 @@ public TermVisitor(QueryContext context) { /** * Creates a query node from a term context. * Term query nodes represent the leafs in the query tree + * * @param ctx term context * @return query node */ @Override public QueryNode visitTerm(QueryParser.TermContext ctx) { QueryNode queryNode; - if (ctx.regex_term() != null) { - //use regex visitor if necessary + if(ctx.value_list() != null) { + queryNode = createFieldListFilterNode(ctx); + } + else if (ctx.regex_term() != null) { + //use a regex visitor if necessary RegexTermVisitor regexTermVisitor = new RegexTermVisitor(); queryNode = regexTermVisitor.visitRegex_term(ctx.regex_term()); } else { @@ -70,6 +74,7 @@ public QueryNode visitTerm(QueryParser.TermContext ctx) { /** * Creates a query node from a single term (field OPERATOR value) + * * @param ctx term context * @return query node */ @@ -80,10 +85,24 @@ private QueryNode createFieldFilterNode(QueryParser.TermContext ctx) { return createFieldFilterNode(field, value, operation); } + /** + * Creates a query node from a single term (field OPERATOR value) + * + * @param ctx term context + * @return query node + */ + private QueryNode createFieldListFilterNode(QueryParser.TermContext ctx) { + Field field = CompilerUtil.createField(ctx.variable()); + String operation = ctx.term_list_operation().getText(); + Value value = CompilerUtil.createListValue(ctx.value_list()); + return createFieldFilterNode(field, value, operation); + } + /** * Creates a query node from a {@link Field}, operator and {@link Value} - * @param field term field - * @param value term value + * + * @param field term field + * @param value term value * @param operation term operator * @return query node */ @@ -94,6 +113,4 @@ private QueryNode createFieldFilterNode(Field field, Value value, String operati } return new QueryNode(new Term(field, termOperator, value)); } - - } diff --git a/src/main/java/de/alexgruen/query/compiler/TextSearchVisitor.java b/src/main/java/de/alexgruen/query/compiler/TextSearchVisitor.java index a1addff..7665bf4 100644 --- a/src/main/java/de/alexgruen/query/compiler/TextSearchVisitor.java +++ b/src/main/java/de/alexgruen/query/compiler/TextSearchVisitor.java @@ -44,9 +44,10 @@ public class TextSearchVisitor extends QueryBaseVisitor { /** - * Creates the root query node from a full text search context - * A text search query tree only consists of a root node and leafs. + * Creates the root query node from a full-text search context + * A text search query tree only consists of a root node and leaves. * All children are joined using the AND operation. + * * @param ctx text search context * @return root query node */ @@ -71,28 +72,27 @@ public QueryNode visitFull_search(QueryParser.Full_searchContext ctx) { node.setOperator(LogicalOperators.AND); - if (required.size() > 0) { - for (int i = 0; i < required.size(); i++) { + if (!required.isEmpty()) { + for (Value value : required) { node.getChildren().add( new QueryNode( new Term( Field.ALL_FIELDS, TermOperators.FULL_TEXT, - required.get(i) + value ) ) ); } } - if (forbidden.size() > 0) { - for (int i = 0; i < forbidden.size(); i++) { - + if (!forbidden.isEmpty()) { + for (Value value : forbidden) { QueryNode childNode = new QueryNode( new Term( Field.ALL_FIELDS, TermOperators.FULL_TEXT, - forbidden.get(i) + value ) ); childNode.setNegate(true); diff --git a/src/main/java/de/alexgruen/query/compiler/parser/Parser.java b/src/main/java/de/alexgruen/query/compiler/parser/Parser.java index f1723de..32e6eec 100644 --- a/src/main/java/de/alexgruen/query/compiler/parser/Parser.java +++ b/src/main/java/de/alexgruen/query/compiler/parser/Parser.java @@ -33,6 +33,7 @@ public abstract class Parser { /** * Parse input String and returns the an object of type T + * * @param s input string * @return resulting object * @throws ParseException throw if there was an error during parsing @@ -42,14 +43,14 @@ public abstract class Parser { /** * Parse input String and returns the an object of type T. * If parsing doesn't work, null is returned + * * @param s input string * @return resulting object */ - public T parseOrNull(String s){ - try{ + public T parseOrNull(String s) { + try { return parse(s); - } - catch (Exception e){ + } catch (Exception e) { return null; } } diff --git a/src/main/java/de/alexgruen/query/compiler/parser/ParserUtil.java b/src/main/java/de/alexgruen/query/compiler/parser/ParserUtil.java index b1e4981..70c0b6e 100644 --- a/src/main/java/de/alexgruen/query/compiler/parser/ParserUtil.java +++ b/src/main/java/de/alexgruen/query/compiler/parser/ParserUtil.java @@ -37,7 +37,8 @@ public class ParserUtil { * Contains all parsers assigned for different target types */ private final static Map, Parser> parserMap = new ConcurrentHashMap<>(); - static{ + + static { init(); } @@ -91,16 +92,19 @@ public Long parse(String s) { public Boolean parse(String s) throws ParseException { if (s == null || !( "false".equals((s = s.toLowerCase())) - || "true".equals(s) - || "f".equals(s) - || "t".equals(s) + || "true".equals(s) + || "f".equals(s) + || "t".equals(s) )) { throw new ParseException(String.format("illegal boolean value: %s", s), 0); } - switch (s){ - case "f":return false; - case "t":return true; - default: return Boolean.parseBoolean(s); + switch (s) { + case "f": + return false; + case "t": + return true; + default: + return Boolean.parseBoolean(s); } } }); diff --git a/src/main/java/de/alexgruen/query/creator/LogicCreator.java b/src/main/java/de/alexgruen/query/creator/LogicCreator.java index 09afa8d..88aed32 100644 --- a/src/main/java/de/alexgruen/query/creator/LogicCreator.java +++ b/src/main/java/de/alexgruen/query/creator/LogicCreator.java @@ -31,12 +31,11 @@ public interface LogicCreator extends OperatorCreator { /** * Creates a new term from several input terms. - * @param node query node + * + * @param node query node * @param children input terms * @return new term */ @SuppressWarnings("unchecked") T create(QueryNode node, T... children); - - } diff --git a/src/main/java/de/alexgruen/query/creator/OperatorCreator.java b/src/main/java/de/alexgruen/query/creator/OperatorCreator.java index 86f58fc..a6385a2 100644 --- a/src/main/java/de/alexgruen/query/creator/OperatorCreator.java +++ b/src/main/java/de/alexgruen/query/creator/OperatorCreator.java @@ -27,6 +27,5 @@ /** * Interface that is implemented by LogicCreators and TermCreators */ -public interface OperatorCreator -{ +public interface OperatorCreator { } diff --git a/src/main/java/de/alexgruen/query/creator/OperatorCreatorMap.java b/src/main/java/de/alexgruen/query/creator/OperatorCreatorMap.java index ca9cc98..412970b 100644 --- a/src/main/java/de/alexgruen/query/creator/OperatorCreatorMap.java +++ b/src/main/java/de/alexgruen/query/creator/OperatorCreatorMap.java @@ -35,23 +35,26 @@ /** * Map that links Operators to Creators. * Can be used for {@link de.alexgruen.query.term.TermOperator} and {@link de.alexgruen.query.LogicalOperator} + * * @param operator type ({@link de.alexgruen.query.term.TermOperator}, {@link de.alexgruen.query.LogicalOperator}) * @param creator type ({@link TermCreator}, {@link LogicCreator}) */ public class OperatorCreatorMap { - private Map operatorMap = new HashMap<>(); - private Map operatorCreatorMap = new HashMap<>(); + private final Map operatorMap = new HashMap<>(); + private final Map operatorCreatorMap = new HashMap<>(); /** * Returns all names and aliases of the operators saved in this map + * * @return list of names and aliases */ - public List getAllAliases(){ + public List getAllAliases() { return new ArrayList<>(operatorMap.keySet()); } /** * Internal function that adds an operator + * * @param op added operator */ private void add(O op) { @@ -64,33 +67,34 @@ private void add(O op) { /** * Adds an operator and the corresponding creator to this map + * * @param operator operator - * @param creator creator + * @param creator creator */ - public void add(O operator, H creator){ + public void add(O operator, H creator) { add(operator); - operatorCreatorMap.put(operator,creator); + operatorCreatorMap.put(operator, creator); } /** * Returns the creator associated with the input operator. - * null is returned if no creator is found + * Null is returned if no creator is found + * * @param operator input operator * @return creator */ - public H getCreator(O operator){ + public H getCreator(O operator) { return operatorCreatorMap.get(operator); } /** * Returns the creator associated with the input operator specified by its name or an alias. * null is returned if no operator is found - * @param opName - * @return + * */ - public H getCreator(String opName){ + public H getCreator(String opName) { O operator = getOperator(opName); - if(operator != null){ + if (operator != null) { return getCreator(operator); } return null; @@ -98,18 +102,20 @@ public H getCreator(String opName){ /** * Returns the operator for an input name or alias + * * @param name input name or alias * @return operator */ - public O getOperator(String name){ + public O getOperator(String name) { return operatorMap.get(name); } /** * Internal function that adds an alias for an operator. * If another operator with this alias already exists, a {@link QueryCompilerException} is thrown + * * @param alias alias - * @param op operator + * @param op operator */ private void addFieldOperationAlias(String alias, O op) { if (operatorMap.containsKey(alias)) { diff --git a/src/main/java/de/alexgruen/query/creator/TermCreator.java b/src/main/java/de/alexgruen/query/creator/TermCreator.java index 9071210..565c6ea 100644 --- a/src/main/java/de/alexgruen/query/creator/TermCreator.java +++ b/src/main/java/de/alexgruen/query/creator/TermCreator.java @@ -31,7 +31,8 @@ public interface TermCreator extends OperatorCreator { /** * Crates a Query of the desired type from a {@link Field} and {@link Value} - * @param node query node + * + * @param node query node * @param field input field * @param value input value * @return query diff --git a/src/main/java/de/alexgruen/query/optimization/QueryOptimization.java b/src/main/java/de/alexgruen/query/optimization/QueryOptimization.java index 97a768d..b77f223 100644 --- a/src/main/java/de/alexgruen/query/optimization/QueryOptimization.java +++ b/src/main/java/de/alexgruen/query/optimization/QueryOptimization.java @@ -29,6 +29,7 @@ public interface QueryOptimization { /** * Optimizes an input query tree. + * * @param queryTree input query tree */ void apply(QueryTree queryTree); diff --git a/src/main/java/de/alexgruen/query/optimization/RemoveRedundantBrackets.java b/src/main/java/de/alexgruen/query/optimization/RemoveRedundantBrackets.java index 0bea211..c478aa9 100644 --- a/src/main/java/de/alexgruen/query/optimization/RemoveRedundantBrackets.java +++ b/src/main/java/de/alexgruen/query/optimization/RemoveRedundantBrackets.java @@ -31,22 +31,21 @@ import java.util.List; public class RemoveRedundantBrackets implements QueryOptimization { - - /** * Removes redundant brackets from a query tree. - * + *

* ── AND - * ├── (x > 1) - * └── AND - * ├── (y > 2) - * └── (z < 3) + * ├── (x > 1) + * └── AND + * ├── (y > 2) + * └── (z < 3) * ----> - * + *

* ── AND - * ├── (x > 1) - * ├── (y > 2) - * └── (z < 3) + * ├── (x > 1) + * ├── (y > 2) + * └── (z < 3) + * * @param queryTree input query tree */ @Override @@ -56,9 +55,10 @@ public void apply(QueryTree queryTree) { /** * Recursive function used to remove all redundant brackets + * * @param parent parent node */ - private void compressRec(QueryNode parent){ + private void compressRec(QueryNode parent) { List children = new ArrayList<>(parent.getChildren()); int c = -1; for (QueryNode child : children) { diff --git a/src/main/java/de/alexgruen/query/term/Field.java b/src/main/java/de/alexgruen/query/term/Field.java index db7c3f6..6a92967 100644 --- a/src/main/java/de/alexgruen/query/term/Field.java +++ b/src/main/java/de/alexgruen/query/term/Field.java @@ -38,8 +38,9 @@ public class Field { /** * Creates a field from a path string and a path array. * E.g.: "test.field" , ["test","field"] + * * @param fullPath full path - * @param path path array + * @param path path array */ public Field(String fullPath, String... path) { this.fullPath = fullPath; @@ -49,6 +50,7 @@ public Field(String fullPath, String... path) { /** * Creates a field from a single field name. * In this case, the path array has only one entry + * * @param name field name */ public Field(String name) { @@ -57,6 +59,7 @@ public Field(String name) { /** * Returns the full path string (e.g. "test.field") + * * @return path string */ public String getFullPath() { @@ -65,6 +68,7 @@ public String getFullPath() { /** * Returns the path array (e.g. ["test", "field"] + * * @return */ public String[] getPath() { @@ -73,6 +77,7 @@ public String[] getPath() { /** * Sets the full path + * * @param fullPath full path */ public void setFullPath(String fullPath) { @@ -81,6 +86,7 @@ public void setFullPath(String fullPath) { /** * Sets the path array + * * @param path */ public void setPath(String[] path) { @@ -90,13 +96,13 @@ public void setPath(String[] path) { /** * Creates the joinend path string from a path array. * from and to can be used to create the path string only from a certain range in the array - * + *

* e.g. ["test", "field 1"] -> "test.'field 1'" * from : 1, to: 2 : ["test","field 1", "subfield 2", "x"] -> "'field 1'.'subfield 2'" * * @param path path array * @param from from - * @param to to + * @param to to * @return path string */ public static String toJoinedPath(String[] path, int from, int to) { @@ -116,39 +122,44 @@ public static String toJoinedPath(String[] path, int from, int to) { /** * Returns the length of the path array + * * @return */ - public int getLength(){ + public int getLength() { return path.length; } /** * Returns the part of the path array at an specified index + * * @param index path index * @return path part */ - public String getPart(int index){ + public String getPart(int index) { return path[index]; } /** * Returns the joined path for this field ({@link #toJoinedPath(String[], int, int)}) + * * @return joined path string */ - public String getJoinedPath(){ - return getJoinedPath(0,path.length); + public String getJoinedPath() { + return getJoinedPath(0, path.length); } /** * Returns the joined path for this field starting at a specified index ({@link #toJoinedPath(String[], int, int)}) + * * @return joined path string */ - public String getJoinedPath(int from){ - return getJoinedPath(from,path.length); + public String getJoinedPath(int from) { + return getJoinedPath(from, path.length); } /** * Returns the joined path for this field at a specified range from the path array ({@link #toJoinedPath(String[], int, int)}) + * * @return joined path string */ public String getJoinedPath(int from, int to) { @@ -161,6 +172,7 @@ public String getJoinedPath(int from, int to) { /** * Returns the joined path ({@link #getJoinedPath()}) + * * @return joined path */ @Override diff --git a/src/main/java/de/alexgruen/query/term/Term.java b/src/main/java/de/alexgruen/query/term/Term.java index bc1215c..eae7e9f 100644 --- a/src/main/java/de/alexgruen/query/term/Term.java +++ b/src/main/java/de/alexgruen/query/term/Term.java @@ -34,9 +34,10 @@ public class Term { /** * Creates a new term object from a field, operator and value - * @param field field + * + * @param field field * @param termOperator operator - * @param value value + * @param value value */ public Term(Field field, TermOperator termOperator, Value value) { this.field = field; @@ -46,6 +47,7 @@ public Term(Field field, TermOperator termOperator, Value value) { /** * Returns the field from this term + * * @return field */ public Field getField() { @@ -54,6 +56,7 @@ public Field getField() { /** * Sets the field ({@link Field} + * * @param field field */ public void setField(Field field) { @@ -62,6 +65,7 @@ public void setField(Field field) { /** * Returns the operator ({@link TermOperator} from this term + * * @return operator */ public TermOperator getOperator() { @@ -70,6 +74,7 @@ public TermOperator getOperator() { /** * Sets the operator + * * @param operator */ public void setOperator(TermOperator operator) { @@ -78,6 +83,7 @@ public void setOperator(TermOperator operator) { /** * Returns the value ({@link Value} from this term + * * @return value */ public Value getValue() { @@ -86,6 +92,7 @@ public Value getValue() { /** * Sets the value + * * @param value value */ public void setValue(Value value) { @@ -94,6 +101,7 @@ public void setValue(Value value) { /** * Returns a string representation of this term (field operation value) + * * @return */ @Override diff --git a/src/main/java/de/alexgruen/query/term/TermOperator.java b/src/main/java/de/alexgruen/query/term/TermOperator.java index 55e0e67..790aba0 100644 --- a/src/main/java/de/alexgruen/query/term/TermOperator.java +++ b/src/main/java/de/alexgruen/query/term/TermOperator.java @@ -34,13 +34,13 @@ public class TermOperator extends Operator { /** * Creates a new Term operator from a name and array of aliases - * @param name term name + * + * @param name term name * @param aliases term aliases */ - public TermOperator(String name, String... aliases){ - super(name,aliases); + public TermOperator(String name, String... aliases) { + super(name, aliases); } - } diff --git a/src/main/java/de/alexgruen/query/term/TermOperators.java b/src/main/java/de/alexgruen/query/term/TermOperators.java index bbf7899..06eaa1b 100644 --- a/src/main/java/de/alexgruen/query/term/TermOperators.java +++ b/src/main/java/de/alexgruen/query/term/TermOperators.java @@ -30,48 +30,52 @@ public class TermOperators { /** * Equals operator */ - public static final TermOperator EQ = new TermOperator("==","EQ", "eq", "="); + public static final TermOperator EQ = new TermOperator("==", "EQ", "eq", "="); /** * Not equals operator */ - public static final TermOperator NE = new TermOperator("!=","NE", "ne"); + public static final TermOperator NE = new TermOperator("!=", "NE", "ne"); /** * Lower equal operator */ - public static final TermOperator LE = new TermOperator("<=","LE", "le"); + public static final TermOperator LE = new TermOperator("<=", "LE", "le"); /** * Lower than operator */ - public static final TermOperator LT = new TermOperator("<", "LT","lt"); + public static final TermOperator LT = new TermOperator("<", "LT", "lt"); /** * Larger equal operator */ - public static final TermOperator GE = new TermOperator(">=","GE", "ge"); + public static final TermOperator GE = new TermOperator(">=", "GE", "ge"); /** * Larger than operator */ - public static final TermOperator GT = new TermOperator(">","GT", "gt"); + public static final TermOperator GT = new TermOperator(">", "GT", "gt"); /** * Text compare operator */ - public static final TermOperator TEXT = new TermOperator("*=","TEXT", "text"); + public static final TermOperator TEXT = new TermOperator("*=", "TEXT", "text"); /** * Regex operator */ - public static final TermOperator REGEX = new TermOperator("~=","REGEX", "regex"); + public static final TermOperator REGEX = new TermOperator("~=", "REGEX", "regex"); /** * Dummy operator for text search terms */ public static final TermOperator FULL_TEXT = new TermOperator("FULL_TEXT"); + public static final TermOperator IN = new TermOperator("IN", "in"); + + public static final TermOperator NOT_IN = new TermOperator("!IN", "!in"); + /** * Contains all default operators */ @@ -85,12 +89,15 @@ public class TermOperators { GT, TEXT, REGEX, - FULL_TEXT + FULL_TEXT, + IN, + NOT_IN }; /** * Returns a list with all default operators + * * @return default operators */ public static List getDefaultOperators() { @@ -100,11 +107,12 @@ public static List getDefaultOperators() { /** * Contains all names an aliases of the default operators */ - private static Set DEFAULT_ALIASES = new HashSet<>(); - static{ + private static final Set DEFAULT_ALIASES = new HashSet<>(); + + static { //Initializes all default names and aliases - for(TermOperator op : DEFAULT_OPERATORS){ + for (TermOperator op : DEFAULT_OPERATORS) { DEFAULT_ALIASES.add(op.getName()); DEFAULT_ALIASES.addAll(Arrays.asList(op.getAliases())); } @@ -112,10 +120,11 @@ public static List getDefaultOperators() { /** * Returns true if a given string is name or alias of a default operator + * * @param name input name * @return true if name or alias of default operator */ - public static boolean isDefaultAlias(String name){ + public static boolean isDefaultAlias(String name) { return DEFAULT_ALIASES.contains(name); } } diff --git a/src/main/java/de/alexgruen/query/term/Value.java b/src/main/java/de/alexgruen/query/term/Value.java index 8ece0be..8c1b3db 100644 --- a/src/main/java/de/alexgruen/query/term/Value.java +++ b/src/main/java/de/alexgruen/query/term/Value.java @@ -27,6 +27,7 @@ import de.alexgruen.query.compiler.QueryCompilerException; +import java.util.List; import java.util.regex.Pattern; /** @@ -34,18 +35,20 @@ */ public class Value { //Value types - public enum Type{ - Double, Long, String, Boolean, Pattern, Null + public enum Type { + Double, Long, String, Boolean, Pattern, Null, List } + private Object value; private Type type; /** * Creates a new value object from an input object. * The type is reconized automatically, if the type is not supported a {@link QueryCompilerException} is thrown. + * * @param value input object */ - public Value(Object value){ + public Value(Object value) { setValue(value); } @@ -53,6 +56,7 @@ public Value(Object value){ /** * Sets the value object and updates the type. * If the type is not supported a {@link QueryCompilerException} is thrown. + * * @param value input object */ public void setValue(Object value) { @@ -62,6 +66,7 @@ public void setValue(Object value) { /** * Returns the value object + * * @return value object */ public Object getValue() { @@ -70,6 +75,7 @@ public Object getValue() { /** * Returns the type of the value object + * * @return value type */ public Type getType() { @@ -78,146 +84,192 @@ public Type getType() { /** * Returns true if the value is null + * * @return true if null */ - public boolean isNull(){ + public boolean isNull() { return value == null; } /** * Returns true if the value is a number (Double or Long) + * * @return true if number */ - public boolean isNumber(){ + public boolean isNumber() { return type == Type.Double || type == Type.Long; } /** * Returns true if the value is a string + * * @return true if string */ - public boolean isString(){ + public boolean isString() { return type == Type.String; } /** * Returns true if the value is boolean + * * @return true if boolean */ - public boolean isBoolean(){ + public boolean isBoolean() { return type == Type.Boolean; } /** * Returns true if the value is a {@link Pattern}. + * * @return true if pattern */ - public boolean isPattern(){ + public boolean isPattern() { return type == Type.Pattern; } + /** + * Returns true if the value is a {@link List}. + * + * @return true if pattern + */ + public boolean isList() { + return type == Type.List; + } + + /** + * Returns the value as Number. If the value is not a number a {@link QueryCompilerException} is thrown. + * + * @return double value + */ + public Number getNumber() { + if (type == Type.Double || type == Type.Long) { + return (Number) value; + } + throw new QueryCompilerException(String.format("value is not available as number (%s)", type.name())); + } + /** * Returns the value as Double. If the value is neither Long or Double a {@link QueryCompilerException} is thrown. + * * @return double value */ - public Double getDouble(){ - if(type == Type.Double){ - return (Double)value; + public Double getDouble() { + if (type == Type.Double) { + return (Double) value; } - if(type == Type.Long){ - return ((Long)value).doubleValue(); + if (type == Type.Long) { + return ((Long) value).doubleValue(); } - throw new QueryCompilerException(String.format("value is not available as double (%s)",type.name())); + throw new QueryCompilerException(String.format("value is not available as double (%s)", type.name())); } + /** * Returns the value as Long. If the value is neither Long or Double a {@link QueryCompilerException} is thrown. + * * @return long value */ - public Long getLong(){ - if(type == Type.Long){ - return (Long)value; + public Long getLong() { + if (type == Type.Long) { + return (Long) value; } - if(type == Type.Double){ - return ((Double)value).longValue(); + if (type == Type.Double) { + return ((Double) value).longValue(); } - throw new QueryCompilerException(String.format("value is not available as long (%s)",type.name())); + throw new QueryCompilerException(String.format("value is not available as long (%s)", type.name())); } /** * Returns the value as Boolean. If the value is not a Boolean a {@link QueryCompilerException} is thrown. + * * @return boolean value */ - public Boolean getBoolean(){ - if(type == Type.Boolean){ - return (Boolean)value; + public Boolean getBoolean() { + if (type == Type.Boolean) { + return (Boolean) value; } - throw new QueryCompilerException(String.format("value is not available as boolean (%s)",type.name())); + throw new QueryCompilerException(String.format("value is not available as boolean (%s)", type.name())); } /** * Returns the value as Pattern. If the value is not a Pattern a {@link QueryCompilerException} is thrown. + * * @return Pattern value */ - public Pattern getPattern(){ - if(type == Type.Pattern){ - return (Pattern)value; + public Pattern getPattern() { + if (type == Type.Pattern) { + return (Pattern) value; } - throw new QueryCompilerException(String.format("value is not available as pattern (%s)",type.name())); + throw new QueryCompilerException(String.format("value is not available as pattern (%s)", type.name())); } /** * Returns the value as String. If the value is not a String the respective toString() value is returned. + * * @return String value */ - public String getString(){ - if(type == Type.Null){ + public String getString() { + if (type == Type.Null) { return null; } return value.toString(); } + /** + * Returns the value as Double. If the value is neither Long or Double a {@link QueryCompilerException} is thrown. + * + * @return double value + */ + @SuppressWarnings("unchecked") + public List getList() { + if (type == Type.List) { + return (List) value; + } + throw new QueryCompilerException(String.format("value is not available as list (%s)", type.name())); + } + /** * Updates the type of the value. * Supported types are: Double, Long, String, Boolean, Integer, Float and Pattern. */ - private void updateType(){ - if(value == null){ + private void updateType() { + if (value == null) { type = Type.Null; - return; } - if(value instanceof Double){ - type = Type.Double; + else if (value instanceof List) { + ((List) value).forEach((v -> { + if (!(v instanceof Value)) { + throw new QueryCompilerException(String.format("unsupported list value type %s", v.getClass())); + } + })); + type = Type.List; } - else if(value instanceof Long){ + else if (value instanceof Double) { + type = Type.Double; + } else if (value instanceof Long) { type = Type.Long; - } - else if(value instanceof String){ + } else if (value instanceof String) { type = Type.String; - } - else if(value instanceof Boolean){ + } else if (value instanceof Boolean) { type = Type.Boolean; - } - else if(value instanceof Pattern){ + } else if (value instanceof Pattern) { type = Type.Pattern; - } - else if(value instanceof Integer){ + } else if (value instanceof Integer) { type = Type.Long; value = ((Integer) value).longValue(); - } - else if(value instanceof Float){ + } else if (value instanceof Float) { type = Type.Double; value = ((Float) value).doubleValue(); - } - else { - throw new QueryCompilerException(String.format("unknown value type %s",value.getClass())); + } else { + throw new QueryCompilerException(String.format("unknown value type %s", value.getClass())); } } /** * Returns the string representation of the value + * * @return value string */ @Override diff --git a/src/main/java/de/alexgruen/query/util/CompilerUtil.java b/src/main/java/de/alexgruen/query/util/CompilerUtil.java index 8207fa7..d60e5fe 100644 --- a/src/main/java/de/alexgruen/query/util/CompilerUtil.java +++ b/src/main/java/de/alexgruen/query/util/CompilerUtil.java @@ -32,24 +32,27 @@ import org.antlr.v4.runtime.tree.TerminalNode; import java.lang.reflect.Array; +import java.util.List; public class CompilerUtil { /** * Creates an array of an input type and specified length - * @param cl class of array objects + * + * @param cl class of array objects * @param length array length - * @param type of array objects + * @param type of array objects * @return array of type T[] */ @SuppressWarnings("unchecked") - public static T[] createArray(Class cl, int length){ + public static T[] createArray(Class cl, int length) { return (T[]) Array.newInstance(cl, length); } /** * Creates a Value object from a value context + * * @param ctx value context * @return value object */ @@ -57,8 +60,20 @@ public static Value createValue(QueryParser.ValueContext ctx) { return createValue(ctx.getText(), ctx.NULL(), ctx.NUMBER(), ctx.BOOLEAN_VALUE()); } + /** + * Creates a Value object from a value context + * + * @param ctx value context + * @return value object + */ + public static Value createListValue(QueryParser.Value_listContext ctx) { + List values = ctx.value().stream().map(CompilerUtil::createValue).toList(); + return new Value(values); + } + /** * Creates a Value object from a full text search context + * * @param ctx text search context * @return value object */ @@ -68,10 +83,11 @@ public static Value createValue(QueryParser.Full_search_valueContext ctx) { /** * Creates a Value object from the nodes in a value context - * @param text node inner text - * @param NULL null node + * + * @param text node inner text + * @param NULL null node * @param NUMBER number node - * @param BOOL boolean node + * @param BOOL boolean node * @return value object */ public static Value createValue(String text, TerminalNode NULL, TerminalNode NUMBER, TerminalNode BOOL) { @@ -119,6 +135,7 @@ public static Value createValue(String text, TerminalNode NULL, TerminalNode NUM /** * Creates a field from a variable context + * * @param ctx variable context * @return field object */ diff --git a/src/main/java/de/alexgruen/query/util/StringUtil.java b/src/main/java/de/alexgruen/query/util/StringUtil.java index 3fe76a3..61116c5 100644 --- a/src/main/java/de/alexgruen/query/util/StringUtil.java +++ b/src/main/java/de/alexgruen/query/util/StringUtil.java @@ -38,25 +38,23 @@ private StringUtil() { } - private static Pattern NUMBER_PATTERN = Pattern.compile("[0-9]+([\\.,][0-9]+)?"); + private static final Pattern NUMBER_PATTERN = Pattern.compile("[0-9]+([.,][0-9]+)?"); /** * Creates a string by repeating a specified char - * @param length length of resulting string + * + * @param length length of resulting string * @param indentChar input char * @return result string */ - public static String repeatChar(int length, char indentChar){ - StringBuilder sb = new StringBuilder(); - for(int i = 0; i < length; i++){ - sb.append(indentChar); - } - return sb.toString(); + public static String repeatChar(int length, char indentChar) { + return String.valueOf(indentChar).repeat(Math.max(0, length)); } /** * Returns parsed Number if the input string is a valid number representation. - * If the string can not be parsed null is returned. + * If the string cannot be parsed, null is returned. + * * @param value input string * @return parsed number or null */ @@ -76,6 +74,7 @@ public static Number toNumberIfValid(String value) { /** * Returns true if the input string is a valid number representation + * * @param value input string * @return true if number */ @@ -86,16 +85,18 @@ public static boolean isNumber(String value) { /** * Returns true if the string is quoted ("string" or 'string') + * * @param val input string * @return true if quoted */ public static boolean isQuoted(String val) { return (val.startsWith("'") && val.endsWith("'")) - || (val.startsWith("\"") && val.endsWith("\"")); + || (val.startsWith("\"") && val.endsWith("\"")); } /** * Removes quotes from a string ("string" -> string) + * * @param val input string * @return string without quotes */ @@ -114,16 +115,17 @@ public static String stripQuotes(String val) { /** * Returns true if a string value requires quotation. * "val 1" -> true, "val1" -> false + * * @param value input string * @return true if quotation is required */ public static boolean requiresQuotation(String value) { return !isQuoted(value) && - ( - value.contains(" ") || - value.contains("\t") || - value.contains("\n") - ); + ( + value.contains(" ") || + value.contains("\t") || + value.contains("\n") + ); } /** @@ -146,7 +148,7 @@ public static String putInQuotes(String input, Character quoteChar) { * * @param input input string * @param split char used to split - * @return string array containing all splitted parts + * @return string array containing all split parts */ public static String[] splitQuoted(String input, Character split) { List parts = new ArrayList<>(); @@ -157,7 +159,7 @@ public static String[] splitQuoted(String input, Character split) { /** - * Split an input string at a specified split-character into several parts. + * Split an input string at a specified split-character into several parts. * " and ' are considered during the process. *

"testA testB testB" -> [testA,testB,testC]

*

"'testA testB' testB" -> [testA testB,testC]

@@ -168,7 +170,7 @@ public static String[] splitQuoted(String input, Character split) { */ @SuppressWarnings("ConstantConditions") public static void splitQuoted(String input, Character split, List parts) { - if (input.length() == 0) { + if (input.isEmpty()) { return; } boolean inQuotation = false; diff --git a/src/test/java/de/alexgruen/querycompiler/CompileTest.java b/src/test/java/de/alexgruen/querycompiler/CompileTest.java index 58f39d7..df5e42c 100644 --- a/src/test/java/de/alexgruen/querycompiler/CompileTest.java +++ b/src/test/java/de/alexgruen/querycompiler/CompileTest.java @@ -1,28 +1,26 @@ package de.alexgruen.querycompiler; -import de.alexgruen.query.PrintQueryCreator; import de.alexgruen.query.PrintQuery; +import de.alexgruen.query.PrintQueryCreator; import de.alexgruen.query.QueryTree; import de.alexgruen.query.compiler.QueryCompiler; import de.alexgruen.query.compiler.QueryCompilerException; import de.alexgruen.query.optimization.Optimizations; import de.alexgruen.query.term.TermOperator; -import de.alexgruen.query.term.TermOperators; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class CompileTest { - private static QueryCompiler PRINT_COMPILER + private static final QueryCompiler PRINT_COMPILER = QueryCompiler.create(PrintQuery.class) - .withDefaultCreator(new PrintQueryCreator()) + .withQueryCreator(new PrintQueryCreator()) .withOptimization(Optimizations.RemoveRedundantBrackets) .build(); @Test - public void testCompile(){ + public void testCompile() { - //Predicates test("x > 0", "(x > 0)"); test("x == 'a'", "(x == 'a')"); test("x ~= /.+/", "(x ~= /.+/)"); @@ -33,7 +31,7 @@ public void testCompile(){ test("x.'x y' *= \"a\"", "(x.'x y' *= 'a')"); test("x.'x y' == \"a b\"", "(x.'x y' == 'a b')"); test("x.'x y' == false", "(x.'x y' == false)"); - test("(x > 0) && !(y < 1)","((x > 0) && !(y < 1))"); + test("(x > 0) && !(y < 1)", "((x > 0) && !(y < 1))"); test("x > 0 && y < 1", "((x > 0) && (y < 1))"); test("x > 0 && y < 1 && z == 2", @@ -51,32 +49,32 @@ public void testCompile(){ } @Test - public void testOptimization(){ + public void testOptimization() { System.out.println("###### remove brackets optimization:"); QueryCompiler noOptimizationCompiler = QueryCompiler .create(PrintQuery.class) - .withDefaultCreator(new PrintQueryCreator()) + .withQueryCreator(new PrintQueryCreator()) .build(); QueryCompiler withOptimizationCompiler = QueryCompiler .create(PrintQuery.class) - .withDefaultCreator(new PrintQueryCreator()) + .withQueryCreator(new PrintQueryCreator()) .withOptimization(Optimizations.RemoveRedundantBrackets) .build(); test("((x > 1) && ((y > 2) && z < 3))", "((x > 1) && ((y > 2) && (z < 3)))", - noOptimizationCompiler,true); + noOptimizationCompiler, true); System.out.println(); test("((x > 1) && ((y > 2) && z < 3))", "((x > 1) && (y > 2) && (z < 3))", - withOptimizationCompiler,true); + withOptimizationCompiler, true); } @Test - public void testException(){ + public void testException() { Assertions.assertThrows(QueryCompilerException.class, () -> PRINT_COMPILER.compile("x > a")); @@ -91,17 +89,17 @@ public void testException(){ } @Test - public void fullTextSearchTest(){ - test("xyz","('xyz')"); - test("-xyz","(!'xyz')"); - test("'xyz'","('xyz')"); - test("-'xyz'","(!'xyz')"); - test("'xyz' abc","('xyz' && 'abc')"); - test("asd xyz -deg 'z e d' -x","('asd' && 'xyz' && 'z e d' && !'deg' && !'x')"); + public void fullTextSearchTest() { + test("xyz", "('xyz')"); + test("-xyz", "(!'xyz')"); + test("'xyz'", "('xyz')"); + test("-'xyz'", "(!'xyz')"); + test("'xyz' abc", "('xyz' && 'abc')"); + test("asd xyz -deg 'z e d' -x", "('asd' && 'xyz' && 'z e d' && !'deg' && !'x')"); } @Test - public void testTree(){ + public void testTreeCase1() { String str = PRINT_COMPILER.compileTree("(x > 0 && y < 1 || (u == 2 && (h != 1 || z < 2))) || z == 2").toString(); System.out.println(str); @@ -114,20 +112,27 @@ public void testTree(){ " │ └──┐ OR\n" + " │ ├── (h != 1)\n" + " │ └── (z < 2)\n" + - " └── (z == 2)\n",str); + " └── (z == 2)\n", str); System.out.println(str); } @Test - public void testCustomOperator(){ - // - System.out.println("###### custom operator:"); + public void testListTerm() { + test("x IN ('1', '2')", "(x in ['1', '2'])"); + test("x !in ('1', 2, 3.1)", "(x !in ['1', 2, 3.1])"); + test("x in (1,2,3)", "(x in [1, 2, 3])"); + test("x in (1)", "(x in [1])"); + test("x !in (1)", "(x !in [1])"); + } + + @Test + public void testCustomOperator() { QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) - .withDefaultCreator(new PrintQueryCreator()) + .withQueryCreator(new PrintQueryCreator()) .withTermCreator( new TermOperator("&="), - (n,f,v) -> new PrintQuery() { + (n, f, v) -> new PrintQuery() { @Override public String toString() { return String.format("(%s &= %s)", f, PrintQueryCreator.value2string(v)); @@ -138,19 +143,19 @@ public String toString() { .withOptimization(Optimizations.RemoveRedundantBrackets) .build(); - test("x &= 2","(x &= 2)",compiler,true); + test("x &= 2", "(x &= 2)", compiler, true); } - private void test(String input, String output){ - test(input,output,PRINT_COMPILER,false); + private void test(String input, String output) { + test(input, output, PRINT_COMPILER, false); } - private void test(String input, String output, QueryCompiler compiler, boolean printTree){ + private void test(String input, String output, QueryCompiler compiler, boolean printTree) { QueryTree tree = compiler.compileTree(input); - if(printTree){ - System.out.println(String.format("%s ->\n%s",input,tree.toString())); + if (printTree) { + System.out.println(String.format("%s ->\n%s", input, tree.toString())); } PrintQuery p = compiler.compile(tree); - Assertions.assertEquals(output,p.toString()); + Assertions.assertEquals(output, p.toString()); } } diff --git a/src/test/java/de/alexgruen/querycompiler/CompilerUtilTest.java b/src/test/java/de/alexgruen/querycompiler/CompilerUtilTest.java new file mode 100644 index 0000000..1d20a2b --- /dev/null +++ b/src/test/java/de/alexgruen/querycompiler/CompilerUtilTest.java @@ -0,0 +1,60 @@ +package de.alexgruen.querycompiler; + +import de.alexgruen.query.compiler.QueryCompilerException; +import de.alexgruen.query.term.Field; +import de.alexgruen.query.term.Value; +import de.alexgruen.query.util.CompilerUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class CompilerUtilTest { + + @Test + public void testCreateArray() { + String[] stringArray = CompilerUtil.createArray(String.class, 3); + Assertions.assertNotNull(stringArray); + Assertions.assertEquals(3, stringArray.length); + + Integer[] intArray = CompilerUtil.createArray(Integer.class, 5); + Assertions.assertNotNull(intArray); + Assertions.assertEquals(5, intArray.length); + + Value[] valueArray = CompilerUtil.createArray(Value.class, 2); + Assertions.assertNotNull(valueArray); + Assertions.assertEquals(2, valueArray.length); + + Object[] emptyArray = CompilerUtil.createArray(Object.class, 0); + Assertions.assertNotNull(emptyArray); + Assertions.assertEquals(0, emptyArray.length); + } + + @Test + public void testCreateField() { + Field simpleField = new Field("x", "x"); + Assertions.assertEquals("x", simpleField.getFullPath()); + Assertions.assertEquals(1, simpleField.getPath().length); + Assertions.assertEquals("x", simpleField.getPath()[0]); + + Field nestedField = new Field("x.y.z", "x", "y", "z"); + Assertions.assertEquals("x.y.z", nestedField.getFullPath()); + Assertions.assertEquals(3, nestedField.getPath().length); + Assertions.assertEquals("x", nestedField.getPath()[0]); + Assertions.assertEquals("y", nestedField.getPath()[1]); + Assertions.assertEquals("z", nestedField.getPath()[2]); + } + + @Test + public void testCreateValue() { + Value nullValue = new Value(null); + Assertions.assertTrue(nullValue.isNull()); + + Value stringValue = new Value("test"); + Assertions.assertEquals("test", stringValue.getString()); + + Value numberValue = new Value(123.45); + Assertions.assertEquals(123.45, numberValue.getDouble()); + + Value boolValue = new Value(true); + Assertions.assertTrue(boolValue.getBoolean()); + } +} diff --git a/src/test/java/de/alexgruen/querycompiler/QueryCompilerBuilderTest.java b/src/test/java/de/alexgruen/querycompiler/QueryCompilerBuilderTest.java new file mode 100644 index 0000000..6c682c2 --- /dev/null +++ b/src/test/java/de/alexgruen/querycompiler/QueryCompilerBuilderTest.java @@ -0,0 +1,185 @@ +package de.alexgruen.querycompiler; + +import de.alexgruen.query.*; +import de.alexgruen.query.compiler.QueryCompiler; +import de.alexgruen.query.compiler.QueryCompilerBuilder; +import de.alexgruen.query.compiler.QueryContext; +import de.alexgruen.query.creator.LogicCreator; +import de.alexgruen.query.creator.TermCreator; +import de.alexgruen.query.optimization.QueryOptimization; +import de.alexgruen.query.optimization.RemoveRedundantBrackets; +import de.alexgruen.query.term.Field; +import de.alexgruen.query.term.TermOperator; +import de.alexgruen.query.term.TermOperators; +import de.alexgruen.query.term.Value; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class QueryCompilerBuilderTest { + + @Test + public void testCreate() { + QueryCompilerBuilder builder = QueryCompilerBuilder.create(PrintQuery.class); + Assertions.assertNotNull(builder); + + QueryCompiler compiler = builder.build(); + Assertions.assertNotNull(compiler); + } + + @Test + public void testCreateDefault() { + PrintQueryCreator creator = new PrintQueryCreator(); + QueryCompilerBuilder builder = QueryCompilerBuilder.createDefault(PrintQuery.class, creator); + Assertions.assertNotNull(builder); + + QueryCompiler compiler = builder.build(); + Assertions.assertNotNull(compiler); + + try { + java.lang.reflect.Field optimizationsField = QueryCompiler.class.getDeclaredField("optimizations"); + optimizationsField.setAccessible(true); + List optimizations = (List) optimizationsField.get(compiler); + + Assertions.assertEquals(1, optimizations.size()); + Assertions.assertTrue(optimizations.get(0) instanceof RemoveRedundantBrackets); + } catch (Exception e) { + Assertions.fail("Failed to access optimizations field: " + e.getMessage()); + } + } + + @Test + public void testWithQueryCreator() { + PrintQueryCreator creator = new PrintQueryCreator(); + QueryCompilerBuilder builder = QueryCompilerBuilder.create(PrintQuery.class) + .withQueryCreator(creator); + + QueryCompiler compiler = builder.build(); + try { + java.lang.reflect.Field contextField = QueryCompiler.class.getDeclaredField("context"); + contextField.setAccessible(true); + QueryContext context = (QueryContext) contextField.get(compiler); + + Assertions.assertNotNull(context.getTermCreator(TermOperators.EQ)); + Assertions.assertNotNull(context.getTermCreator(TermOperators.NE)); + Assertions.assertNotNull(context.getTermCreator(TermOperators.LT)); + Assertions.assertNotNull(context.getTermCreator(TermOperators.LE)); + Assertions.assertNotNull(context.getTermCreator(TermOperators.GT)); + Assertions.assertNotNull(context.getTermCreator(TermOperators.GE)); + Assertions.assertNotNull(context.getTermCreator(TermOperators.REGEX)); + Assertions.assertNotNull(context.getTermCreator(TermOperators.TEXT)); + Assertions.assertNotNull(context.getTermCreator(TermOperators.IN)); + Assertions.assertNotNull(context.getTermCreator(TermOperators.FULL_TEXT)); + + Assertions.assertNotNull(context.getLogicCreator(LogicalOperators.AND)); + Assertions.assertNotNull(context.getLogicCreator(LogicalOperators.OR)); + Assertions.assertNotNull(context.getLogicCreator(LogicalOperators.XOR)); + Assertions.assertNotNull(context.getLogicCreator(LogicalOperators.NOR)); + Assertions.assertNotNull(context.getLogicCreator(LogicalOperators.NOT)); + + Assertions.assertNotNull(context.getEmptyCreator()); + } catch (Exception e) { + Assertions.fail("Failed to access context field: " + e.getMessage()); + } + } + + @Test + public void testWithOptimization() { + QueryCompilerBuilder builder = QueryCompilerBuilder.create(PrintQuery.class) + .withOptimization(new RemoveRedundantBrackets()); + + QueryCompiler compiler = builder.build(); + try { + java.lang.reflect.Field optimizationsField = QueryCompiler.class.getDeclaredField("optimizations"); + optimizationsField.setAccessible(true); + List optimizations = (List) optimizationsField.get(compiler); + + Assertions.assertEquals(1, optimizations.size()); + Assertions.assertTrue(optimizations.get(0) instanceof RemoveRedundantBrackets); + } catch (Exception e) { + Assertions.fail("Failed to access optimizations field: " + e.getMessage()); + } + } + + @Test + public void testWithTermCreator() { + TermCreator customCreator = (node, field, value) -> new PrintQuery() { + @Override + public String toString() { + return "custom"; + } + }; + + QueryCompilerBuilder builder = QueryCompilerBuilder.create(PrintQuery.class) + .withTermCreator(new TermOperator("custom"), customCreator); + + QueryCompiler compiler = builder.build(); + try { + java.lang.reflect.Field contextField = QueryCompiler.class.getDeclaredField("context"); + contextField.setAccessible(true); + QueryContext context = (QueryContext) contextField.get(compiler); + + Assertions.assertNotNull(context.getTermCreator(new TermOperator("custom"))); + } catch (Exception e) { + Assertions.fail("Failed to access context field: " + e.getMessage()); + } + } + + @Test + public void testWithEmptyCreator() { + TermCreator emptyCreator = (node, field, value) -> new PrintQuery() { + @Override + public String toString() { + return "empty"; + } + }; + + QueryCompilerBuilder builder = QueryCompilerBuilder.create(PrintQuery.class) + .withEmptyCreator(emptyCreator); + + QueryCompiler compiler = builder.build(); + try { + java.lang.reflect.Field contextField = QueryCompiler.class.getDeclaredField("context"); + contextField.setAccessible(true); + QueryContext context = (QueryContext) contextField.get(compiler); + + Assertions.assertNotNull(context.getEmptyCreator()); + } catch (Exception e) { + Assertions.fail("Failed to access context field: " + e.getMessage()); + } + } + + @Test + public void testWithLogicCreators() { + LogicCreator andCreator = (node, children) -> new PrintQuery() { + @Override + public String toString() { + return "and"; + } + }; + + LogicCreator orCreator = (node, children) -> new PrintQuery() { + @Override + public String toString() { + return "or"; + } + }; + + QueryCompilerBuilder builder = QueryCompilerBuilder.create(PrintQuery.class) + .withANDCreator(andCreator) + .withORCreator(orCreator); + + QueryCompiler compiler = builder.build(); + try { + java.lang.reflect.Field contextField = QueryCompiler.class.getDeclaredField("context"); + contextField.setAccessible(true); + QueryContext context = (QueryContext) contextField.get(compiler); + + Assertions.assertNotNull(context.getLogicCreator(LogicalOperators.AND)); + Assertions.assertNotNull(context.getLogicCreator(LogicalOperators.OR)); + } catch (Exception e) { + Assertions.fail("Failed to access context field: " + e.getMessage()); + } + } +} diff --git a/src/test/java/de/alexgruen/querycompiler/QueryCompilerTest.java b/src/test/java/de/alexgruen/querycompiler/QueryCompilerTest.java new file mode 100644 index 0000000..65605f0 --- /dev/null +++ b/src/test/java/de/alexgruen/querycompiler/QueryCompilerTest.java @@ -0,0 +1,121 @@ +package de.alexgruen.querycompiler; + +import de.alexgruen.query.*; +import de.alexgruen.query.compiler.QueryCompiler; +import de.alexgruen.query.compiler.QueryCompilerException; +import de.alexgruen.query.optimization.QueryOptimization; +import de.alexgruen.query.term.Field; +import de.alexgruen.query.term.Value; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +public class QueryCompilerTest { + + @Test + public void testCompileWithInvalidInput() { + QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + .withQueryCreator(new PrintQueryCreator()) + .build(); + + Assertions.assertThrows(QueryCompilerException.class, () -> compiler.compile("x > ")); + + Assertions.assertThrows(QueryCompilerException.class, () -> compiler.compile("(x > 1")); + + Assertions.assertThrows(QueryCompilerException.class, () -> compiler.compile("x @ 1")); + } + + @Test + public void testCompileTree() { + QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + .withQueryCreator(new PrintQueryCreator()) + .build(); + + QueryTree tree = compiler.compileTree("x > 1"); + Assertions.assertNotNull(tree); + Assertions.assertNotNull(tree.getRoot()); + + tree = compiler.compileTree("(x > 1 && y < 2) || z == 3"); + Assertions.assertNotNull(tree); + Assertions.assertNotNull(tree.getRoot()); + Assertions.assertEquals(2, tree.getRoot().getChildren().size()); + } + + @Test + public void testCompileWithCustomOptimization() { + final int[] count = {0}; + QueryOptimization countingOptimization = tree -> count[0]++; + + QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + .withQueryCreator(new PrintQueryCreator()) + .withOptimization(countingOptimization) + .build(); + + compiler.compileTree("x > 1"); + Assertions.assertEquals(1, count[0]); + + compiler.compileTree("y < 2"); + Assertions.assertEquals(2, count[0]); + } + + @Test + public void testCompileWithEmptyInput() { + QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + .withQueryCreator(new PrintQueryCreator()) + .build(); + + PrintQuery query = compiler.compile(""); + Assertions.assertNotNull(query); + + query = compiler.compile(" "); + Assertions.assertNotNull(query); + } + + @Test + public void testCompileWithNoEmptyCreator() { + QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + .withANDCreator((node, children) -> new PrintQuery() { + @Override + public String toString() { + return "and"; + } + }) + .build(); + + Assertions.assertThrows(QueryCompilerException.class, () -> { + compiler.compile(""); + }); + } + + @Test + public void testGetContext() { + QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + .withQueryCreator(new PrintQueryCreator()) + .build(); + + Assertions.assertNotNull(compiler.getContext()); + Assertions.assertEquals(PrintQuery.class, compiler.getContext().getCl()); + } + + @Test + public void testGetOptimizations() { + List optimizations = new ArrayList<>(); + optimizations.add(tree -> { + }); + optimizations.add(tree -> { + }); + + QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + .withQueryCreator(new PrintQueryCreator()) + .withOptimization(optimizations.get(0)) + .withOptimization(optimizations.get(1)) + .build(); + + Assertions.assertNotNull(compiler.getOptimizations()); + Assertions.assertEquals(2, compiler.getOptimizations().size()); + Assertions.assertEquals(optimizations.get(0), compiler.getOptimizations().get(0)); + Assertions.assertEquals(optimizations.get(1), compiler.getOptimizations().get(1)); + } +} diff --git a/src/test/java/de/alexgruen/querycompiler/QueryCreatorTest.java b/src/test/java/de/alexgruen/querycompiler/QueryCreatorTest.java new file mode 100644 index 0000000..b8c98b5 --- /dev/null +++ b/src/test/java/de/alexgruen/querycompiler/QueryCreatorTest.java @@ -0,0 +1,217 @@ +package de.alexgruen.querycompiler; + +import de.alexgruen.query.*; +import de.alexgruen.query.term.Field; +import de.alexgruen.query.term.Value; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.regex.Pattern; + +public class QueryCreatorTest { + + private static class TestQueryCreator implements QueryCreator { + @Override + public PrintQuery ne(QueryNode queryNode, Field field, Value value) { + return createPrintQuery(String.format("(%s != %s)", field.getFullPath(), value)); + } + + @Override + public PrintQuery eq(QueryNode queryNode, Field field, Value value) { + return createPrintQuery(String.format("(%s == %s)", field.getFullPath(), value)); + } + + @Override + public PrintQuery ge(QueryNode queryNode, Field field, Value value) { + return createPrintQuery(String.format("(%s >= %s)", field.getFullPath(), value)); + } + + @Override + public PrintQuery gt(QueryNode queryNode, Field field, Value value) { + return createPrintQuery(String.format("(%s > %s)", field.getFullPath(), value)); + } + + @Override + public PrintQuery lt(QueryNode queryNode, Field field, Value value) { + return createPrintQuery(String.format("(%s < %s)", field.getFullPath(), value)); + } + + @Override + public PrintQuery le(QueryNode queryNode, Field field, Value value) { + return createPrintQuery(String.format("(%s <= %s)", field.getFullPath(), value)); + } + + @Override + public PrintQuery regex(QueryNode queryNode, Field field, Value value) { + return createPrintQuery(String.format("(%s ~= %s)", field.getFullPath(), value)); + } + + @Override + public PrintQuery text(QueryNode queryNode, Field field, Value value) { + return createPrintQuery(String.format("(%s *= %s)", field.getFullPath(), value)); + } + + @Override + public PrintQuery in(QueryNode queryNode, Field field, Value list) { + return createPrintQuery(String.format("(%s in %s)", field.getFullPath(), list)); + } + + @Override + public PrintQuery notIn(QueryNode queryNode, Field field, Value list) { + return createPrintQuery(String.format("(%s !in %s)", field.getFullPath(), list)); + } + + @Override + public PrintQuery not(QueryNode queryNode, PrintQuery v) { + return createPrintQuery(String.format("!%s", v)); + } + + @Override + public PrintQuery and(QueryNode queryNode, PrintQuery[] v) { + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (int i = 0; i < v.length; i++) { + sb.append(v[i]); + if (i < v.length - 1) { + sb.append(" && "); + } + } + sb.append(")"); + return createPrintQuery(sb.toString()); + } + + @Override + public PrintQuery or(QueryNode queryNode, PrintQuery[] v) { + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (int i = 0; i < v.length; i++) { + sb.append(v[i]); + if (i < v.length - 1) { + sb.append(" || "); + } + } + sb.append(")"); + return createPrintQuery(sb.toString()); + } + + @Override + public PrintQuery xor(QueryNode queryNode, PrintQuery[] v) { + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (int i = 0; i < v.length; i++) { + sb.append(v[i]); + if (i < v.length - 1) { + sb.append(" ^ "); + } + } + sb.append(")"); + return createPrintQuery(sb.toString()); + } + + @Override + public PrintQuery nor(QueryNode queryNode, PrintQuery[] v) { + StringBuilder sb = new StringBuilder(); + sb.append("!("); + for (int i = 0; i < v.length; i++) { + sb.append(v[i]); + if (i < v.length - 1) { + sb.append(" || "); + } + } + sb.append(")"); + return createPrintQuery(sb.toString()); + } + + @Override + public PrintQuery fullSearch(Value value) { + return createPrintQuery(String.format("'%s'", value)); + } + + @Override + public PrintQuery empty() { + return createPrintQuery("*"); + } + + private PrintQuery createPrintQuery(final String str) { + return new PrintQuery() { + @Override + public String toString() { + return str; + } + }; + } + } + + @Test + public void testTermOperations() { + TestQueryCreator creator = new TestQueryCreator(); + QueryNode node = new QueryNode(); + Field field = new Field("x", "x"); + Value value = new Value(123); + + PrintQuery query = creator.eq(node, field, value); + Assertions.assertEquals("(x == 123)", query.toString()); + + query = creator.ne(node, field, value); + Assertions.assertEquals("(x != 123)", query.toString()); + + query = creator.gt(node, field, value); + Assertions.assertEquals("(x > 123)", query.toString()); + + query = creator.ge(node, field, value); + Assertions.assertEquals("(x >= 123)", query.toString()); + + query = creator.lt(node, field, value); + Assertions.assertEquals("(x < 123)", query.toString()); + + query = creator.le(node, field, value); + Assertions.assertEquals("(x <= 123)", query.toString()); + + Value patternValue = new Value(Pattern.compile("test")); + query = creator.regex(node, field, patternValue); + Assertions.assertEquals("(x ~= test)", query.toString()); + + Value textValue = new Value("test"); + query = creator.text(node, field, textValue); + Assertions.assertEquals("(x *= test)", query.toString()); + + Value listValue = new Value(List.of(new Value(123), new Value(124))); + query = creator.in(node, field, listValue); + Assertions.assertEquals("(x in [123, 124])", query.toString()); + + query = creator.notIn(node, field, listValue); + Assertions.assertEquals("(x !in [123, 124])", query.toString()); + + query = creator.fullSearch(textValue); + Assertions.assertEquals("'test'", query.toString()); + + query = creator.empty(); + Assertions.assertEquals("*", query.toString()); + } + + @Test + public void testLogicOperations() { + TestQueryCreator creator = new TestQueryCreator(); + QueryNode node = new QueryNode(); + + PrintQuery q1 = creator.createPrintQuery("q1"); + PrintQuery q2 = creator.createPrintQuery("q2"); + PrintQuery q3 = creator.createPrintQuery("q3"); + + PrintQuery query = creator.not(node, q1); + Assertions.assertEquals("!q1", query.toString()); + + query = creator.and(node, new PrintQuery[]{q1, q2, q3}); + Assertions.assertEquals("(q1 && q2 && q3)", query.toString()); + + query = creator.or(node, new PrintQuery[]{q1, q2, q3}); + Assertions.assertEquals("(q1 || q2 || q3)", query.toString()); + + query = creator.xor(node, new PrintQuery[]{q1, q2, q3}); + Assertions.assertEquals("(q1 ^ q2 ^ q3)", query.toString()); + + query = creator.nor(node, new PrintQuery[]{q1, q2, q3}); + Assertions.assertEquals("!(q1 || q2 || q3)", query.toString()); + } +} diff --git a/src/test/java/de/alexgruen/querycompiler/RegexTermVisitorTest.java b/src/test/java/de/alexgruen/querycompiler/RegexTermVisitorTest.java new file mode 100644 index 0000000..8d54843 --- /dev/null +++ b/src/test/java/de/alexgruen/querycompiler/RegexTermVisitorTest.java @@ -0,0 +1,64 @@ +package de.alexgruen.querycompiler; + +import de.alexgruen.query.compiler.QueryCompilerException; +import de.alexgruen.query.compiler.RegexTermVisitor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.regex.Pattern; + +public class RegexTermVisitorTest { + + @Test + public void testConvertPattern() throws Exception { + Method convertPatternMethod = RegexTermVisitor.class.getDeclaredMethod("convertPattern", String.class); + convertPatternMethod.setAccessible(true); + + Pattern pattern = (Pattern) convertPatternMethod.invoke(null, "/test/"); + Assertions.assertNotNull(pattern); + Assertions.assertTrue(pattern.matcher("test").matches()); + Assertions.assertFalse(pattern.matcher("other").matches()); + + pattern = (Pattern) convertPatternMethod.invoke(null, "/t.st/"); + Assertions.assertNotNull(pattern); + Assertions.assertTrue(pattern.matcher("test").matches()); + Assertions.assertTrue(pattern.matcher("tast").matches()); + Assertions.assertFalse(pattern.matcher("tst").matches()); + + pattern = (Pattern) convertPatternMethod.invoke(null, "/\\d+/"); + Assertions.assertNotNull(pattern); + Assertions.assertTrue(pattern.matcher("123").matches()); + Assertions.assertFalse(pattern.matcher("abc").matches()); + } + + @Test + public void testConvertPatternInvalid() throws Exception { + Method convertPatternMethod = RegexTermVisitor.class.getDeclaredMethod("convertPattern", String.class); + convertPatternMethod.setAccessible(true); + + try { + convertPatternMethod.invoke(null, "test/"); + Assertions.fail("Expected QueryCompilerException was not thrown"); + } catch (Exception e) { + Assertions.assertInstanceOf(QueryCompilerException.class, e.getCause()); + Assertions.assertTrue(e.getCause().getMessage().contains("wrong pattern format")); + } + + try { + convertPatternMethod.invoke(null, "/test"); + Assertions.fail("Expected QueryCompilerException was not thrown"); + } catch (Exception e) { + Assertions.assertInstanceOf(QueryCompilerException.class, e.getCause()); + Assertions.assertTrue(e.getCause().getMessage().contains("wrong pattern format")); + } + + try { + convertPatternMethod.invoke(null, "test"); + Assertions.fail("Expected QueryCompilerException was not thrown"); + } catch (Exception e) { + Assertions.assertInstanceOf(QueryCompilerException.class, e.getCause()); + Assertions.assertTrue(e.getCause().getMessage().contains("wrong pattern format")); + } + } +} diff --git a/src/test/java/de/alexgruen/querycompiler/TermCreatorTest.java b/src/test/java/de/alexgruen/querycompiler/TermCreatorTest.java new file mode 100644 index 0000000..8517f5b --- /dev/null +++ b/src/test/java/de/alexgruen/querycompiler/TermCreatorTest.java @@ -0,0 +1,90 @@ +package de.alexgruen.querycompiler; + +import de.alexgruen.query.PrintQuery; +import de.alexgruen.query.QueryNode; +import de.alexgruen.query.creator.TermCreator; +import de.alexgruen.query.term.Field; +import de.alexgruen.query.term.Value; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TermCreatorTest { + + @Test + public void testTermCreator() { + TermCreator termCreator = (node, field, value) -> new PrintQuery() { + @Override + public String toString() { + return String.format("(%s == %s)", field.getFullPath(), value.toString()); + } + }; + + Field field = new Field("x", "x"); + Value value = new Value(123); + PrintQuery query = termCreator.create(null, field, value); + + Assertions.assertNotNull(query); + Assertions.assertEquals("(x == 123)", query.toString()); + + field = new Field("x.y.z", "x", "y", "z"); + query = termCreator.create(null, field, value); + + Assertions.assertNotNull(query); + Assertions.assertEquals("(x.y.z == 123)", query.toString()); + + value = new Value("test"); + query = termCreator.create(null, field, value); + + Assertions.assertNotNull(query); + Assertions.assertEquals("(x.y.z == test)", query.toString()); + + value = new Value(null); + query = termCreator.create(null, field, value); + + Assertions.assertNotNull(query); + Assertions.assertEquals("(x.y.z == null)", query.toString()); + } + + @Test + public void testTermCreatorWithLambda() { + TermCreator termCreator = (node, field, value) -> new PrintQuery() { + @Override + public String toString() { + return String.format("(%s != %s)", field.getFullPath(), value); + } + }; + + Field field = new Field("x", new String[]{"x"}); + Value value = new Value(123); + PrintQuery query = termCreator.create(null, field, value); + + Assertions.assertNotNull(query); + Assertions.assertEquals("(x != 123)", query.toString()); + } + + @Test + public void testTermCreatorWithQueryNode() { + TermCreator termCreator = (node, field, value) -> new PrintQuery() { + @Override + public String toString() { + return String.format("(%s %s %s)", field.getFullPath(), + node != null && node.isNegate() ? "!=" : "==", + value.toString()); + } + }; + + QueryNode node = new QueryNode(); + Field field = new Field("x", "x"); + Value value = new Value(123); + PrintQuery query = termCreator.create(node, field, value); + + Assertions.assertNotNull(query); + Assertions.assertEquals("(x == 123)", query.toString()); + + node.setNegate(true); + query = termCreator.create(node, field, value); + + Assertions.assertNotNull(query); + Assertions.assertEquals("(x != 123)", query.toString()); + } +} diff --git a/src/test/java/de/alexgruen/querycompiler/ValueTest.java b/src/test/java/de/alexgruen/querycompiler/ValueTest.java new file mode 100644 index 0000000..76455ea --- /dev/null +++ b/src/test/java/de/alexgruen/querycompiler/ValueTest.java @@ -0,0 +1,125 @@ +package de.alexgruen.querycompiler; + +import de.alexgruen.query.compiler.QueryCompilerException; +import de.alexgruen.query.term.Value; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +public class ValueTest { + + @Test + public void testValueCreation() { + Value nullValue = new Value(null); + Assertions.assertTrue(nullValue.isNull()); + Assertions.assertEquals(Value.Type.Null, nullValue.getType()); + Assertions.assertNull(nullValue.getValue()); + Assertions.assertNull(nullValue.getString()); + + Double doubleVal = 123.45; + Value doubleValue = new Value(doubleVal); + Assertions.assertEquals(Value.Type.Double, doubleValue.getType()); + Assertions.assertEquals(doubleVal, doubleValue.getValue()); + Assertions.assertTrue(doubleValue.isNumber()); + Assertions.assertEquals(doubleVal, doubleValue.getDouble()); + Assertions.assertEquals(123L, doubleValue.getLong()); + + Long longVal = 123L; + Value longValue = new Value(longVal); + Assertions.assertEquals(Value.Type.Long, longValue.getType()); + Assertions.assertEquals(longVal, longValue.getValue()); + Assertions.assertTrue(longValue.isNumber()); + Assertions.assertEquals(123.0, longValue.getDouble()); + Assertions.assertEquals(longVal, longValue.getLong()); + + String stringVal = "test"; + Value stringValue = new Value(stringVal); + Assertions.assertEquals(Value.Type.String, stringValue.getType()); + Assertions.assertEquals(stringVal, stringValue.getValue()); + Assertions.assertTrue(stringValue.isString()); + Assertions.assertEquals(stringVal, stringValue.getString()); + + Boolean boolVal = true; + Value boolValue = new Value(boolVal); + Assertions.assertEquals(Value.Type.Boolean, boolValue.getType()); + Assertions.assertEquals(boolVal, boolValue.getValue()); + Assertions.assertTrue(boolValue.isBoolean()); + Assertions.assertEquals(boolVal, boolValue.getBoolean()); + + Pattern patternVal = Pattern.compile("test"); + Value patternValue = new Value(patternVal); + Assertions.assertEquals(Value.Type.Pattern, patternValue.getType()); + Assertions.assertEquals(patternVal, patternValue.getValue()); + Assertions.assertTrue(patternValue.isPattern()); + Assertions.assertEquals(patternVal, patternValue.getPattern()); + + Integer intVal = 123; + Value intValue = new Value(intVal); + Assertions.assertEquals(Value.Type.Long, intValue.getType()); + Assertions.assertEquals(123L, intValue.getValue()); + + Float floatVal = 123.45f; + Value floatValue = new Value(floatVal); + Assertions.assertEquals(Value.Type.Double, floatValue.getType()); + Assertions.assertEquals(123.45, floatValue.getDouble(), 0.001); + } + + @Test + public void testListValue() { + List valueList = new ArrayList<>(); + valueList.add(new Value("test1")); + valueList.add(new Value(123L)); + + Value listValue = new Value(valueList); + Assertions.assertEquals(Value.Type.List, listValue.getType()); + Assertions.assertTrue(listValue.isList()); + Assertions.assertEquals(valueList, listValue.getList()); + Assertions.assertEquals(2, listValue.getList().size()); + Assertions.assertEquals("test1", listValue.getList().get(0).getString()); + Assertions.assertEquals(123L, listValue.getList().get(1).getLong()); + } + + @Test + public void testInvalidListValue() { + List invalidList = new ArrayList<>(); + invalidList.add("test"); + + Assertions.assertThrows(QueryCompilerException.class, () -> { + new Value(invalidList); + }); + } + + @Test + public void testTypeConversionExceptions() { + Value stringValue = new Value("test"); + + Assertions.assertThrows(QueryCompilerException.class, stringValue::getDouble); + + Assertions.assertThrows(QueryCompilerException.class, stringValue::getLong); + + Assertions.assertThrows(QueryCompilerException.class, stringValue::getBoolean); + + Assertions.assertThrows(QueryCompilerException.class, stringValue::getPattern); + + Assertions.assertThrows(QueryCompilerException.class, stringValue::getList); + } + + @Test + public void testUnsupportedType() { + Assertions.assertThrows(QueryCompilerException.class, () -> { + new Value(new StringBuilder("test")); + }); + } + + @Test + public void testToString() { + Value stringValue = new Value("test"); + Assertions.assertEquals("test", stringValue.toString()); + + Value nullValue = new Value(null); + Assertions.assertNull(nullValue.toString()); + } +} From cbb32461ff5a0caacea396f94f36e9f0e8b47bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Gr=C3=BCn?= Date: Mon, 14 Apr 2025 11:07:40 +0200 Subject: [PATCH 2/2] fix code smells --- .../de/alexgruen/query/LogicalOperators.java | 4 + .../de/alexgruen/query/PrintQueryCreator.java | 2 - .../java/de/alexgruen/query/QueryCreator.java | 2 - .../java/de/alexgruen/query/QueryPrinter.java | 7 +- .../java/de/alexgruen/query/QueryTree.java | 4 +- .../query/compiler/QueryCompiler.java | 24 -- .../query/compiler/QueryCompilerBuilder.java | 3 +- .../query/compiler/QueryTreeCompiler.java | 2 +- .../query/compiler/TermQueryVisitor.java | 6 +- .../query/compiler/parser/ParserUtil.java | 36 +-- .../query/creator/OperatorCreatorMap.java | 6 +- .../query/optimization/Optimizations.java | 6 +- .../alexgruen/query/term/TermOperators.java | 3 + .../java/de/alexgruen/query/term/Value.java | 57 +++-- .../de/alexgruen/query/util/CompilerUtil.java | 79 ++++--- .../de/alexgruen/query/util/StringUtil.java | 6 +- .../query/PrintQueryCreatorTest.java | 93 ++++++++ .../compiler}/CompileTest.java | 57 +++-- .../compiler}/QueryCompilerBuilderTest.java | 34 ++- .../compiler}/QueryCompilerTest.java | 46 ++-- .../compiler}/RegexTermVisitorTest.java | 12 +- .../query/compiler/parser/ParserTest.java | 39 ++++ .../query/compiler/parser/ParserUtilTest.java | 137 +++++++++++ .../creator}/TermCreatorTest.java | 19 +- .../term}/ValueTest.java | 48 ++-- .../util}/CompilerUtilTest.java | 14 +- .../util}/StringTest.java | 53 +++-- .../querycompiler/QueryCreatorTest.java | 217 ------------------ 28 files changed, 525 insertions(+), 491 deletions(-) create mode 100644 src/test/java/de/alexgruen/query/PrintQueryCreatorTest.java rename src/test/java/de/alexgruen/{querycompiler => query/compiler}/CompileTest.java (80%) rename src/test/java/de/alexgruen/{querycompiler => query/compiler}/QueryCompilerBuilderTest.java (90%) rename src/test/java/de/alexgruen/{querycompiler => query/compiler}/QueryCompilerTest.java (71%) rename src/test/java/de/alexgruen/{querycompiler => query/compiler}/RegexTermVisitorTest.java (88%) create mode 100644 src/test/java/de/alexgruen/query/compiler/parser/ParserTest.java create mode 100644 src/test/java/de/alexgruen/query/compiler/parser/ParserUtilTest.java rename src/test/java/de/alexgruen/{querycompiler => query/creator}/TermCreatorTest.java (89%) rename src/test/java/de/alexgruen/{querycompiler => query/term}/ValueTest.java (78%) rename src/test/java/de/alexgruen/{querycompiler => query/util}/CompilerUtilTest.java (87%) rename src/test/java/de/alexgruen/{querycompiler => query/util}/StringTest.java (54%) delete mode 100644 src/test/java/de/alexgruen/querycompiler/QueryCreatorTest.java diff --git a/src/main/java/de/alexgruen/query/LogicalOperators.java b/src/main/java/de/alexgruen/query/LogicalOperators.java index b4ab08e..5318d17 100644 --- a/src/main/java/de/alexgruen/query/LogicalOperators.java +++ b/src/main/java/de/alexgruen/query/LogicalOperators.java @@ -29,6 +29,10 @@ import java.util.List; public class LogicalOperators { + private LogicalOperators() { + throw new IllegalStateException("utility class"); + } + /** * AND operator */ diff --git a/src/main/java/de/alexgruen/query/PrintQueryCreator.java b/src/main/java/de/alexgruen/query/PrintQueryCreator.java index 2d6cb25..dfe40a0 100644 --- a/src/main/java/de/alexgruen/query/PrintQueryCreator.java +++ b/src/main/java/de/alexgruen/query/PrintQueryCreator.java @@ -27,8 +27,6 @@ import de.alexgruen.query.term.Field; import de.alexgruen.query.term.Value; -import java.util.List; - /** * Default creator for {@link PrintQuery} */ diff --git a/src/main/java/de/alexgruen/query/QueryCreator.java b/src/main/java/de/alexgruen/query/QueryCreator.java index 89263d4..4f8bd7c 100644 --- a/src/main/java/de/alexgruen/query/QueryCreator.java +++ b/src/main/java/de/alexgruen/query/QueryCreator.java @@ -3,8 +3,6 @@ import de.alexgruen.query.term.Field; import de.alexgruen.query.term.Value; -import java.util.List; - public interface QueryCreator { /** * Query creator for != operator diff --git a/src/main/java/de/alexgruen/query/QueryPrinter.java b/src/main/java/de/alexgruen/query/QueryPrinter.java index 2edbfae..9242937 100644 --- a/src/main/java/de/alexgruen/query/QueryPrinter.java +++ b/src/main/java/de/alexgruen/query/QueryPrinter.java @@ -27,10 +27,9 @@ import de.alexgruen.query.util.StringUtil; import java.util.function.Function; +import java.util.function.UnaryOperator; public class QueryPrinter { - public static final QueryPrinter DEFAULT = QueryPrinter.create().build(); - private String horizontalLine = "──"; private String verticalLine = "│"; private String tLine = "├"; @@ -94,7 +93,7 @@ public static final class Builder { private String labelPrefix = ""; private String labelPostfix = ""; private String lineSeparator = "\n"; - private Function indentFunction = (l) -> StringUtil.repeatChar(l.length() - 2, ' '); + private UnaryOperator indentFunction = l -> StringUtil.repeatChar(l.length() - 2, ' '); private Builder() { } @@ -128,7 +127,7 @@ public Builder withCornerLast(String cornerLast) { return this; } - public Builder witIndentFunction(Function indentFunction) { + public Builder witIndentFunction(UnaryOperator indentFunction) { this.indentFunction = indentFunction; return this; } diff --git a/src/main/java/de/alexgruen/query/QueryTree.java b/src/main/java/de/alexgruen/query/QueryTree.java index 1773d0d..bc97705 100644 --- a/src/main/java/de/alexgruen/query/QueryTree.java +++ b/src/main/java/de/alexgruen/query/QueryTree.java @@ -26,6 +26,8 @@ public class QueryTree { + private static final QueryPrinter PRINTER = QueryPrinter.create().build(); + private QueryNode root; public QueryTree(QueryNode root) { @@ -42,7 +44,7 @@ public void setRoot(QueryNode root) { @Override public String toString() { - return QueryPrinter.DEFAULT.toString(root); + return PRINTER.toString(root); } } diff --git a/src/main/java/de/alexgruen/query/compiler/QueryCompiler.java b/src/main/java/de/alexgruen/query/compiler/QueryCompiler.java index bcc987c..4cee91d 100644 --- a/src/main/java/de/alexgruen/query/compiler/QueryCompiler.java +++ b/src/main/java/de/alexgruen/query/compiler/QueryCompiler.java @@ -45,30 +45,6 @@ protected QueryCompiler(QueryContext context, List optimiz this.queryTreeCompiler = new QueryTreeCompiler<>(context); } - /** - * Create a new {@link QueryCompilerBuilder} - * - * @param cl target class - * @param target type - * @return query compiler instance - */ - public static QueryCompilerBuilder create(Class cl) { - return QueryCompilerBuilder.create(cl); - } - - - /** - * Create a new {@link QueryCompilerBuilder} using a {@link QueryCreator}. - * - * @param cl target class - * @param queryCreator default operation creator - * @param target type - * @return query compiler instance - */ - public static QueryCompilerBuilder createDefault(Class cl, QueryCreator queryCreator) { - return QueryCompilerBuilder.createDefault(cl, queryCreator); - } - /** * Returns the {@link QueryContext} * diff --git a/src/main/java/de/alexgruen/query/compiler/QueryCompilerBuilder.java b/src/main/java/de/alexgruen/query/compiler/QueryCompilerBuilder.java index 03e33fb..7be5570 100644 --- a/src/main/java/de/alexgruen/query/compiler/QueryCompilerBuilder.java +++ b/src/main/java/de/alexgruen/query/compiler/QueryCompilerBuilder.java @@ -198,7 +198,7 @@ public QueryCompilerBuilder withNOTCreator(LogicCreator creator) { * @return query context */ private QueryContext createContext() { - return new QueryContext( + return new QueryContext<>( termCreators, logicCreators, emptyCreator, cl ); } @@ -211,5 +211,4 @@ private QueryContext createContext() { public QueryCompiler build() { return new QueryCompiler<>(createContext(), optimizations); } - } diff --git a/src/main/java/de/alexgruen/query/compiler/QueryTreeCompiler.java b/src/main/java/de/alexgruen/query/compiler/QueryTreeCompiler.java index b8078bb..d89341d 100644 --- a/src/main/java/de/alexgruen/query/compiler/QueryTreeCompiler.java +++ b/src/main/java/de/alexgruen/query/compiler/QueryTreeCompiler.java @@ -56,7 +56,7 @@ public QueryTreeCompiler(QueryContext context) { */ public QueryTree compile(String queryString) { if (context == null) { - throw new RuntimeException("context required"); + throw new QueryCompilerException("context required"); } queryString = queryString.trim(); diff --git a/src/main/java/de/alexgruen/query/compiler/TermQueryVisitor.java b/src/main/java/de/alexgruen/query/compiler/TermQueryVisitor.java index 740c6f5..571dd87 100644 --- a/src/main/java/de/alexgruen/query/compiler/TermQueryVisitor.java +++ b/src/main/java/de/alexgruen/query/compiler/TermQueryVisitor.java @@ -70,11 +70,6 @@ public QueryNode visitFull_search(QueryParser.Full_searchContext ctx) { return textSearchVisitor.visitFull_search(ctx); } - @Override - public QueryNode visitValue_list(QueryParser.Value_listContext ctx) { - return super.visitValue_list(ctx); - } - /** * Creates a query node from a query context. * Recursively creates all child query nodes for the root query context. @@ -82,6 +77,7 @@ public QueryNode visitValue_list(QueryParser.Value_listContext ctx) { * @param ctx root query context * @return query node */ + @Override public QueryNode visitQuery(QueryParser.QueryContext ctx) { if (ctx.term() != null) { TermVisitor termVisitor = new TermVisitor(context); diff --git a/src/main/java/de/alexgruen/query/compiler/parser/ParserUtil.java b/src/main/java/de/alexgruen/query/compiler/parser/ParserUtil.java index 70c0b6e..a23122c 100644 --- a/src/main/java/de/alexgruen/query/compiler/parser/ParserUtil.java +++ b/src/main/java/de/alexgruen/query/compiler/parser/ParserUtil.java @@ -33,10 +33,14 @@ * Created by Alex on 04.06.2015. */ public class ParserUtil { + private ParserUtil() { + throw new IllegalStateException("utility class"); + } + /** * Contains all parsers assigned for different target types */ - private final static Map, Parser> parserMap = new ConcurrentHashMap<>(); + private static final Map, Parser> parserMap = new ConcurrentHashMap<>(); static { init(); @@ -90,22 +94,23 @@ public Long parse(String s) { parserMap.put(Boolean.class, new Parser() { @Override public Boolean parse(String s) throws ParseException { - if (s == null || !( - "false".equals((s = s.toLowerCase())) + if(s == null){ + throw new ParseException("illegal boolean value: null", 0); + } + s = s.toLowerCase(); + if (!( + "false".equals(s) || "true".equals(s) || "f".equals(s) || "t".equals(s) )) { throw new ParseException(String.format("illegal boolean value: %s", s), 0); } - switch (s) { - case "f": - return false; - case "t": - return true; - default: - return Boolean.parseBoolean(s); - } + return switch (s) { + case "f" -> false; + case "t" -> true; + default -> Boolean.parseBoolean(s); + }; } }); @@ -143,8 +148,8 @@ public Byte parse(String s) { * @throws ParseException thrown if the string can not be parsed */ private static T parseArray(Class cl, String x) throws ParseException { - Parser p = getParserMap().get(cl.getComponentType()); - Class cc = cl.getComponentType(); + Parser p = getParserMap().get(cl.getComponentType()); + Class cc = cl.getComponentType(); String[] vals = x.split("[;,|]"); Object r = Array.newInstance(cc, vals.length); for (int i = 0; i < vals.length; i++) { @@ -201,7 +206,7 @@ public static boolean hasParser(Class cl) { */ @SuppressWarnings("unchecked") public static Parser getParser(Class cl) throws ParserNotFoundException { - Parser parser = getParserMap().get(cl); + Parser parser = getParserMap().get(cl); if (parser == null) { throw new ParserNotFoundException(cl); } @@ -218,7 +223,8 @@ public static Parser getParser(Class cl) throws ParserNotFoundExceptio public static Parser findParserOrNull(Class cl) { try { return getParser(cl); - } catch (ParserNotFoundException e) { + } catch (ParserNotFoundException ignored) { + // ignore errors } return null; } diff --git a/src/main/java/de/alexgruen/query/creator/OperatorCreatorMap.java b/src/main/java/de/alexgruen/query/creator/OperatorCreatorMap.java index 412970b..10f131e 100644 --- a/src/main/java/de/alexgruen/query/creator/OperatorCreatorMap.java +++ b/src/main/java/de/alexgruen/query/creator/OperatorCreatorMap.java @@ -41,7 +41,7 @@ */ public class OperatorCreatorMap { private final Map operatorMap = new HashMap<>(); - private final Map operatorCreatorMap = new HashMap<>(); + private final Map creatorMap = new HashMap<>(); /** * Returns all names and aliases of the operators saved in this map @@ -73,7 +73,7 @@ private void add(O op) { */ public void add(O operator, H creator) { add(operator); - operatorCreatorMap.put(operator, creator); + creatorMap.put(operator, creator); } /** @@ -84,7 +84,7 @@ public void add(O operator, H creator) { * @return creator */ public H getCreator(O operator) { - return operatorCreatorMap.get(operator); + return creatorMap.get(operator); } /** diff --git a/src/main/java/de/alexgruen/query/optimization/Optimizations.java b/src/main/java/de/alexgruen/query/optimization/Optimizations.java index 9cfbebb..c1dc2cc 100644 --- a/src/main/java/de/alexgruen/query/optimization/Optimizations.java +++ b/src/main/java/de/alexgruen/query/optimization/Optimizations.java @@ -25,8 +25,12 @@ package de.alexgruen.query.optimization; public class Optimizations { + private Optimizations() { + throw new IllegalStateException("utility class"); + } + /** * Removes redundant brackets from a query tree. */ - public final static QueryOptimization RemoveRedundantBrackets = new RemoveRedundantBrackets(); + public static final QueryOptimization RemoveRedundantBrackets = new RemoveRedundantBrackets(); } diff --git a/src/main/java/de/alexgruen/query/term/TermOperators.java b/src/main/java/de/alexgruen/query/term/TermOperators.java index 06eaa1b..809060f 100644 --- a/src/main/java/de/alexgruen/query/term/TermOperators.java +++ b/src/main/java/de/alexgruen/query/term/TermOperators.java @@ -27,6 +27,9 @@ import java.util.*; public class TermOperators { + private TermOperators() { + throw new IllegalStateException("utility class"); + } /** * Equals operator */ diff --git a/src/main/java/de/alexgruen/query/term/Value.java b/src/main/java/de/alexgruen/query/term/Value.java index 8c1b3db..14bdb8d 100644 --- a/src/main/java/de/alexgruen/query/term/Value.java +++ b/src/main/java/de/alexgruen/query/term/Value.java @@ -34,9 +34,8 @@ * Represents a value within a term (field operator value) */ public class Value { - //Value types public enum Type { - Double, Long, String, Boolean, Pattern, Null, List + DOUBLE, LONG, STRING, BOOLEAN, PATTERN, NULL, LIST } private Object value; @@ -97,7 +96,7 @@ public boolean isNull() { * @return true if number */ public boolean isNumber() { - return type == Type.Double || type == Type.Long; + return type == Type.DOUBLE || type == Type.LONG; } /** @@ -106,7 +105,7 @@ public boolean isNumber() { * @return true if string */ public boolean isString() { - return type == Type.String; + return type == Type.STRING; } /** @@ -115,7 +114,7 @@ public boolean isString() { * @return true if boolean */ public boolean isBoolean() { - return type == Type.Boolean; + return type == Type.BOOLEAN; } /** @@ -124,7 +123,7 @@ public boolean isBoolean() { * @return true if pattern */ public boolean isPattern() { - return type == Type.Pattern; + return type == Type.PATTERN; } /** @@ -133,7 +132,7 @@ public boolean isPattern() { * @return true if pattern */ public boolean isList() { - return type == Type.List; + return type == Type.LIST; } /** @@ -142,7 +141,7 @@ public boolean isList() { * @return double value */ public Number getNumber() { - if (type == Type.Double || type == Type.Long) { + if (type == Type.DOUBLE || type == Type.LONG) { return (Number) value; } throw new QueryCompilerException(String.format("value is not available as number (%s)", type.name())); @@ -154,10 +153,10 @@ public Number getNumber() { * @return double value */ public Double getDouble() { - if (type == Type.Double) { + if (type == Type.DOUBLE) { return (Double) value; } - if (type == Type.Long) { + if (type == Type.LONG) { return ((Long) value).doubleValue(); } throw new QueryCompilerException(String.format("value is not available as double (%s)", type.name())); @@ -170,10 +169,10 @@ public Double getDouble() { * @return long value */ public Long getLong() { - if (type == Type.Long) { + if (type == Type.LONG) { return (Long) value; } - if (type == Type.Double) { + if (type == Type.DOUBLE) { return ((Double) value).longValue(); } throw new QueryCompilerException(String.format("value is not available as long (%s)", type.name())); @@ -185,7 +184,7 @@ public Long getLong() { * @return boolean value */ public Boolean getBoolean() { - if (type == Type.Boolean) { + if (type == Type.BOOLEAN) { return (Boolean) value; } @@ -198,7 +197,7 @@ public Boolean getBoolean() { * @return Pattern value */ public Pattern getPattern() { - if (type == Type.Pattern) { + if (type == Type.PATTERN) { return (Pattern) value; } @@ -211,7 +210,7 @@ public Pattern getPattern() { * @return String value */ public String getString() { - if (type == Type.Null) { + if (type == Type.NULL) { return null; } return value.toString(); @@ -224,7 +223,7 @@ public String getString() { */ @SuppressWarnings("unchecked") public List getList() { - if (type == Type.List) { + if (type == Type.LIST) { return (List) value; } throw new QueryCompilerException(String.format("value is not available as list (%s)", type.name())); @@ -236,7 +235,7 @@ public List getList() { */ private void updateType() { if (value == null) { - type = Type.Null; + type = Type.NULL; } else if (value instanceof List) { ((List) value).forEach((v -> { @@ -244,24 +243,24 @@ else if (value instanceof List) { throw new QueryCompilerException(String.format("unsupported list value type %s", v.getClass())); } })); - type = Type.List; + type = Type.LIST; } else if (value instanceof Double) { - type = Type.Double; + type = Type.DOUBLE; } else if (value instanceof Long) { - type = Type.Long; + type = Type.LONG; } else if (value instanceof String) { - type = Type.String; + type = Type.STRING; } else if (value instanceof Boolean) { - type = Type.Boolean; + type = Type.BOOLEAN; } else if (value instanceof Pattern) { - type = Type.Pattern; - } else if (value instanceof Integer) { - type = Type.Long; - value = ((Integer) value).longValue(); - } else if (value instanceof Float) { - type = Type.Double; - value = ((Float) value).doubleValue(); + type = Type.PATTERN; + } else if (value instanceof Integer intValue) { + type = Type.LONG; + value = intValue.longValue(); + } else if (value instanceof Float floatValue) { + type = Type.DOUBLE; + value = floatValue.doubleValue(); } else { throw new QueryCompilerException(String.format("unknown value type %s", value.getClass())); } diff --git a/src/main/java/de/alexgruen/query/util/CompilerUtil.java b/src/main/java/de/alexgruen/query/util/CompilerUtil.java index d60e5fe..df4c914 100644 --- a/src/main/java/de/alexgruen/query/util/CompilerUtil.java +++ b/src/main/java/de/alexgruen/query/util/CompilerUtil.java @@ -36,7 +36,9 @@ public class CompilerUtil { - + private CompilerUtil() { + throw new IllegalStateException("utility class"); + } /** * Creates an array of an input type and specified length * @@ -85,54 +87,67 @@ public static Value createValue(QueryParser.Full_search_valueContext ctx) { * Creates a Value object from the nodes in a value context * * @param text node inner text - * @param NULL null node - * @param NUMBER number node - * @param BOOL boolean node + * @param nullNode null node + * @param numberNode number node + * @param boolNode boolean node * @return value object */ - public static Value createValue(String text, TerminalNode NULL, TerminalNode NUMBER, TerminalNode BOOL) { - if (NULL != null) { + public static Value createValue(String text, TerminalNode nullNode, TerminalNode numberNode, TerminalNode boolNode) { + if (nullNode != null) { return null; } - if (NUMBER != null) { - String n = NUMBER.getText(); - try { - if (n.contains(".")) { - return new Value(ParserUtil.parse(Double.class, n)); - } else { - return new Value(ParserUtil.parse(Long.class, n)); - } - } catch (Exception e) { - throw new QueryCompilerException(String.format("error parsing value '%s'", n)); - } + if (numberNode != null) { + return createNumberValue(numberNode); } - if (BOOL != null) { - try { - return new Value( - ParserUtil.parse(Boolean.class, BOOL.getText()) - ); - } catch (Exception e) { - throw new QueryCompilerException(String.format("error parsing value '%s'", BOOL.getText())); + if (boolNode != null) { + return createBoolValue(boolNode); + } + return createTextValue(text); + } + + private static Value createNumberValue(TerminalNode node) { + String n = node.getText(); + try { + if (n.contains(".")) { + return new Value(ParserUtil.parse(Double.class, n)); + } else { + return new Value(ParserUtil.parse(Long.class, n)); } + } catch (Exception e) { + throw new QueryCompilerException(String.format("error parsing value '%s'", n)); + } + } + + private static Value createBoolValue(TerminalNode node) { + try { + return new Value( + ParserUtil.parse(Boolean.class, node.getText()) + ); + } catch (Exception e) { + throw new QueryCompilerException(String.format("error parsing value '%s'", node.getText())); } - String value = text; + } + + private static Value createTextValue(String value) { if (StringUtil.isQuoted(value)) { value = StringUtil.stripQuotes(value); return new Value(value); } Number numberValue = StringUtil.toNumberIfValid(value); - if (numberValue != null) { - if (numberValue instanceof Double) { - return new Value(numberValue); - } - if (numberValue instanceof Long) { - return new Value(numberValue); - } + if (numberValue == null) { + return new Value(value); + } + if (numberValue instanceof Double) { + return new Value(numberValue); + } + if (numberValue instanceof Long) { + return new Value(numberValue); } return new Value(value); } + /** * Creates a field from a variable context * diff --git a/src/main/java/de/alexgruen/query/util/StringUtil.java b/src/main/java/de/alexgruen/query/util/StringUtil.java index 61116c5..e405689 100644 --- a/src/main/java/de/alexgruen/query/util/StringUtil.java +++ b/src/main/java/de/alexgruen/query/util/StringUtil.java @@ -38,7 +38,7 @@ private StringUtil() { } - private static final Pattern NUMBER_PATTERN = Pattern.compile("[0-9]+([.,][0-9]+)?"); + private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+([.,]\\d+)?"); /** * Creates a string by repeating a specified char @@ -168,7 +168,7 @@ public static String[] splitQuoted(String input, Character split) { * @param split char used to split * @param parts list filled with the resulting parts */ - @SuppressWarnings("ConstantConditions") + @SuppressWarnings({"ConstantConditions", "java:S3776", "java:S135"}) public static void splitQuoted(String input, Character split, List parts) { if (input.isEmpty()) { return; @@ -201,7 +201,7 @@ public static void splitQuoted(String input, Character split, List parts } else if (c == '\"') { if (inDoubleQuotation) { inDoubleQuotation = false; - } else if (!inDoubleQuotation && startOrSplit) { + } else if (startOrSplit) { inDoubleQuotation = true; startOrSplit = false; } else { diff --git a/src/test/java/de/alexgruen/query/PrintQueryCreatorTest.java b/src/test/java/de/alexgruen/query/PrintQueryCreatorTest.java new file mode 100644 index 0000000..e69fc6a --- /dev/null +++ b/src/test/java/de/alexgruen/query/PrintQueryCreatorTest.java @@ -0,0 +1,93 @@ +package de.alexgruen.query; + +import de.alexgruen.query.term.Field; +import de.alexgruen.query.term.Value; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.regex.Pattern; + +class PrintQueryCreatorTest { + + private PrintQuery createSimplePrintQuery(final String str) { + return new PrintQuery() { + @Override + public String toString() { + return str; + } + }; + } + + @Test + void testTermOperations() { + PrintQueryCreator creator = new PrintQueryCreator(); + QueryNode node = new QueryNode(); + Field field = new Field("x", "x"); + Value value = new Value(123); + + PrintQuery query = creator.eq(node, field, value); + Assertions.assertEquals("(x == 123)", query.toString()); + + query = creator.ne(node, field, value); + Assertions.assertEquals("(x != 123)", query.toString()); + + query = creator.gt(node, field, value); + Assertions.assertEquals("(x > 123)", query.toString()); + + query = creator.ge(node, field, value); + Assertions.assertEquals("(x >= 123)", query.toString()); + + query = creator.lt(node, field, value); + Assertions.assertEquals("(x < 123)", query.toString()); + + query = creator.le(node, field, value); + Assertions.assertEquals("(x <= 123)", query.toString()); + + Value patternValue = new Value(Pattern.compile("test")); + query = creator.regex(node, field, patternValue); + Assertions.assertEquals("(x ~= /test/)", query.toString()); + + Value textValue = new Value("test"); + query = creator.text(node, field, textValue); + Assertions.assertEquals("(x *= 'test')", query.toString()); + + Value listValue = new Value(List.of(new Value(123), new Value(124))); + query = creator.in(node, field, listValue); + Assertions.assertEquals("(x in [123, 124])", query.toString()); + + query = creator.notIn(node, field, listValue); + Assertions.assertEquals("(x !in [123, 124])", query.toString()); + + query = creator.fullSearch(textValue); + Assertions.assertEquals("'test'", query.toString()); + + query = creator.empty(); + Assertions.assertEquals("*", query.toString()); + } + + @Test + void testLogicOperations() { + PrintQueryCreator creator = new PrintQueryCreator(); + QueryNode node = new QueryNode(); + + PrintQuery q1 = createSimplePrintQuery("q1"); + PrintQuery q2 = createSimplePrintQuery("q2"); + PrintQuery q3 = createSimplePrintQuery("q3"); + + PrintQuery query = creator.not(node, q1); + Assertions.assertEquals("!q1", query.toString()); + + query = creator.and(node, q1, q2, q3); + Assertions.assertEquals("(q1 && q2 && q3)", query.toString()); + + query = creator.or(node, q1, q2, q3); + Assertions.assertEquals("(q1 || q2 || q3)", query.toString()); + + query = creator.xor(node, q1, q2, q3); + Assertions.assertEquals("(q1 XOR q2 XOR q3)", query.toString()); + + query = creator.nor(node, q1, q2, q3); + Assertions.assertEquals("(q1 NOR q2 NOR q3)", query.toString()); + } +} diff --git a/src/test/java/de/alexgruen/querycompiler/CompileTest.java b/src/test/java/de/alexgruen/query/compiler/CompileTest.java similarity index 80% rename from src/test/java/de/alexgruen/querycompiler/CompileTest.java rename to src/test/java/de/alexgruen/query/compiler/CompileTest.java index df5e42c..3c125a0 100644 --- a/src/test/java/de/alexgruen/querycompiler/CompileTest.java +++ b/src/test/java/de/alexgruen/query/compiler/CompileTest.java @@ -1,24 +1,22 @@ -package de.alexgruen.querycompiler; +package de.alexgruen.query.compiler; import de.alexgruen.query.PrintQuery; import de.alexgruen.query.PrintQueryCreator; import de.alexgruen.query.QueryTree; -import de.alexgruen.query.compiler.QueryCompiler; -import de.alexgruen.query.compiler.QueryCompilerException; import de.alexgruen.query.optimization.Optimizations; import de.alexgruen.query.term.TermOperator; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class CompileTest { +class CompileTest { private static final QueryCompiler PRINT_COMPILER - = QueryCompiler.create(PrintQuery.class) + = QueryCompilerBuilder.create(PrintQuery.class) .withQueryCreator(new PrintQueryCreator()) .withOptimization(Optimizations.RemoveRedundantBrackets) .build(); @Test - public void testCompile() { + void testCompile() { test("x > 0", "(x > 0)"); @@ -49,14 +47,14 @@ public void testCompile() { } @Test - public void testOptimization() { + void testOptimization() { System.out.println("###### remove brackets optimization:"); - QueryCompiler noOptimizationCompiler = QueryCompiler + QueryCompiler noOptimizationCompiler = QueryCompilerBuilder .create(PrintQuery.class) .withQueryCreator(new PrintQueryCreator()) .build(); - QueryCompiler withOptimizationCompiler = QueryCompiler + QueryCompiler withOptimizationCompiler = QueryCompilerBuilder .create(PrintQuery.class) .withQueryCreator(new PrintQueryCreator()) .withOptimization(Optimizations.RemoveRedundantBrackets) @@ -74,7 +72,7 @@ public void testOptimization() { } @Test - public void testException() { + void testException() { Assertions.assertThrows(QueryCompilerException.class, () -> PRINT_COMPILER.compile("x > a")); @@ -89,7 +87,7 @@ public void testException() { } @Test - public void fullTextSearchTest() { + void fullTextSearchTest() { test("xyz", "('xyz')"); test("-xyz", "(!'xyz')"); test("'xyz'", "('xyz')"); @@ -99,25 +97,24 @@ public void fullTextSearchTest() { } @Test - public void testTreeCase1() { + void testTreeCase1() { String str = PRINT_COMPILER.compileTree("(x > 0 && y < 1 || (u == 2 && (h != 1 || z < 2))) || z == 2").toString(); - System.out.println(str); - - Assertions.assertEquals("──┐ OR\n" + - " ├──┐ AND\n" + - " │ ├── (x > 0)\n" + - " │ └── (y < 1)\n" + - " ├──┐ AND\n" + - " │ ├── (u == 2)\n" + - " │ └──┐ OR\n" + - " │ ├── (h != 1)\n" + - " │ └── (z < 2)\n" + - " └── (z == 2)\n", str); - System.out.println(str); + Assertions.assertEquals(""" + ──┐ OR + ├──┐ AND + │ ├── (x > 0) + │ └── (y < 1) + ├──┐ AND + │ ├── (u == 2) + │ └──┐ OR + │ ├── (h != 1) + │ └── (z < 2) + └── (z == 2) + """, str); } @Test - public void testListTerm() { + void testListTerm() { test("x IN ('1', '2')", "(x in ['1', '2'])"); test("x !in ('1', 2, 3.1)", "(x !in ['1', 2, 3.1])"); test("x in (1,2,3)", "(x in [1, 2, 3])"); @@ -126,9 +123,9 @@ public void testListTerm() { } @Test - public void testCustomOperator() { + void testCustomOperator() { QueryCompiler compiler - = QueryCompiler.create(PrintQuery.class) + = QueryCompilerBuilder.create(PrintQuery.class) .withQueryCreator(new PrintQueryCreator()) .withTermCreator( new TermOperator("&="), @@ -153,9 +150,9 @@ private void test(String input, String output) { private void test(String input, String output, QueryCompiler compiler, boolean printTree) { QueryTree tree = compiler.compileTree(input); if (printTree) { - System.out.println(String.format("%s ->\n%s", input, tree.toString())); + System.out.printf("%s ->%n%s%n", input, tree.toString()); } PrintQuery p = compiler.compile(tree); Assertions.assertEquals(output, p.toString()); } -} +} \ No newline at end of file diff --git a/src/test/java/de/alexgruen/querycompiler/QueryCompilerBuilderTest.java b/src/test/java/de/alexgruen/query/compiler/QueryCompilerBuilderTest.java similarity index 90% rename from src/test/java/de/alexgruen/querycompiler/QueryCompilerBuilderTest.java rename to src/test/java/de/alexgruen/query/compiler/QueryCompilerBuilderTest.java index 6c682c2..f6d09bc 100644 --- a/src/test/java/de/alexgruen/querycompiler/QueryCompilerBuilderTest.java +++ b/src/test/java/de/alexgruen/query/compiler/QueryCompilerBuilderTest.java @@ -1,26 +1,24 @@ -package de.alexgruen.querycompiler; +package de.alexgruen.query.compiler; -import de.alexgruen.query.*; -import de.alexgruen.query.compiler.QueryCompiler; -import de.alexgruen.query.compiler.QueryCompilerBuilder; -import de.alexgruen.query.compiler.QueryContext; +import de.alexgruen.query.LogicalOperators; +import de.alexgruen.query.PrintQuery; +import de.alexgruen.query.PrintQueryCreator; import de.alexgruen.query.creator.LogicCreator; import de.alexgruen.query.creator.TermCreator; import de.alexgruen.query.optimization.QueryOptimization; import de.alexgruen.query.optimization.RemoveRedundantBrackets; -import de.alexgruen.query.term.Field; import de.alexgruen.query.term.TermOperator; import de.alexgruen.query.term.TermOperators; -import de.alexgruen.query.term.Value; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.List; -public class QueryCompilerBuilderTest { +@SuppressWarnings("unchecked") +class QueryCompilerBuilderTest { @Test - public void testCreate() { + void testCreate() { QueryCompilerBuilder builder = QueryCompilerBuilder.create(PrintQuery.class); Assertions.assertNotNull(builder); @@ -29,7 +27,7 @@ public void testCreate() { } @Test - public void testCreateDefault() { + void testCreateDefault() { PrintQueryCreator creator = new PrintQueryCreator(); QueryCompilerBuilder builder = QueryCompilerBuilder.createDefault(PrintQuery.class, creator); Assertions.assertNotNull(builder); @@ -43,14 +41,14 @@ public void testCreateDefault() { List optimizations = (List) optimizationsField.get(compiler); Assertions.assertEquals(1, optimizations.size()); - Assertions.assertTrue(optimizations.get(0) instanceof RemoveRedundantBrackets); + Assertions.assertInstanceOf(RemoveRedundantBrackets.class, optimizations.getFirst()); } catch (Exception e) { Assertions.fail("Failed to access optimizations field: " + e.getMessage()); } } @Test - public void testWithQueryCreator() { + void testWithQueryCreator() { PrintQueryCreator creator = new PrintQueryCreator(); QueryCompilerBuilder builder = QueryCompilerBuilder.create(PrintQuery.class) .withQueryCreator(creator); @@ -85,7 +83,7 @@ public void testWithQueryCreator() { } @Test - public void testWithOptimization() { + void testWithOptimization() { QueryCompilerBuilder builder = QueryCompilerBuilder.create(PrintQuery.class) .withOptimization(new RemoveRedundantBrackets()); @@ -96,14 +94,14 @@ public void testWithOptimization() { List optimizations = (List) optimizationsField.get(compiler); Assertions.assertEquals(1, optimizations.size()); - Assertions.assertTrue(optimizations.get(0) instanceof RemoveRedundantBrackets); + Assertions.assertInstanceOf(RemoveRedundantBrackets.class, optimizations.getFirst()); } catch (Exception e) { Assertions.fail("Failed to access optimizations field: " + e.getMessage()); } } @Test - public void testWithTermCreator() { + void testWithTermCreator() { TermCreator customCreator = (node, field, value) -> new PrintQuery() { @Override public String toString() { @@ -127,7 +125,7 @@ public String toString() { } @Test - public void testWithEmptyCreator() { + void testWithEmptyCreator() { TermCreator emptyCreator = (node, field, value) -> new PrintQuery() { @Override public String toString() { @@ -151,7 +149,7 @@ public String toString() { } @Test - public void testWithLogicCreators() { + void testWithLogicCreators() { LogicCreator andCreator = (node, children) -> new PrintQuery() { @Override public String toString() { @@ -182,4 +180,4 @@ public String toString() { Assertions.fail("Failed to access context field: " + e.getMessage()); } } -} +} \ No newline at end of file diff --git a/src/test/java/de/alexgruen/querycompiler/QueryCompilerTest.java b/src/test/java/de/alexgruen/query/compiler/QueryCompilerTest.java similarity index 71% rename from src/test/java/de/alexgruen/querycompiler/QueryCompilerTest.java rename to src/test/java/de/alexgruen/query/compiler/QueryCompilerTest.java index 65605f0..f824af8 100644 --- a/src/test/java/de/alexgruen/querycompiler/QueryCompilerTest.java +++ b/src/test/java/de/alexgruen/query/compiler/QueryCompilerTest.java @@ -1,22 +1,20 @@ -package de.alexgruen.querycompiler; +package de.alexgruen.query.compiler; -import de.alexgruen.query.*; -import de.alexgruen.query.compiler.QueryCompiler; -import de.alexgruen.query.compiler.QueryCompilerException; +import de.alexgruen.query.PrintQuery; +import de.alexgruen.query.PrintQueryCreator; +import de.alexgruen.query.QueryTree; import de.alexgruen.query.optimization.QueryOptimization; -import de.alexgruen.query.term.Field; -import de.alexgruen.query.term.Value; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; -public class QueryCompilerTest { +class QueryCompilerTest { @Test - public void testCompileWithInvalidInput() { - QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + void testCompileWithInvalidInput() { + QueryCompiler compiler = QueryCompilerBuilder.create(PrintQuery.class) .withQueryCreator(new PrintQueryCreator()) .build(); @@ -28,8 +26,8 @@ public void testCompileWithInvalidInput() { } @Test - public void testCompileTree() { - QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + void testCompileTree() { + QueryCompiler compiler = QueryCompilerBuilder.create(PrintQuery.class) .withQueryCreator(new PrintQueryCreator()) .build(); @@ -44,11 +42,11 @@ public void testCompileTree() { } @Test - public void testCompileWithCustomOptimization() { + void testCompileWithCustomOptimization() { final int[] count = {0}; QueryOptimization countingOptimization = tree -> count[0]++; - QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + QueryCompiler compiler = QueryCompilerBuilder.create(PrintQuery.class) .withQueryCreator(new PrintQueryCreator()) .withOptimization(countingOptimization) .build(); @@ -61,8 +59,8 @@ public void testCompileWithCustomOptimization() { } @Test - public void testCompileWithEmptyInput() { - QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + void testCompileWithEmptyInput() { + QueryCompiler compiler = QueryCompilerBuilder.create(PrintQuery.class) .withQueryCreator(new PrintQueryCreator()) .build(); @@ -74,8 +72,8 @@ public void testCompileWithEmptyInput() { } @Test - public void testCompileWithNoEmptyCreator() { - QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + void testCompileWithNoEmptyCreator() { + QueryCompiler compiler = QueryCompilerBuilder.create(PrintQuery.class) .withANDCreator((node, children) -> new PrintQuery() { @Override public String toString() { @@ -84,14 +82,12 @@ public String toString() { }) .build(); - Assertions.assertThrows(QueryCompilerException.class, () -> { - compiler.compile(""); - }); + Assertions.assertThrows(QueryCompilerException.class, () -> compiler.compile("")); } @Test - public void testGetContext() { - QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + void testGetContext() { + QueryCompiler compiler = QueryCompilerBuilder.create(PrintQuery.class) .withQueryCreator(new PrintQueryCreator()) .build(); @@ -100,14 +96,14 @@ public void testGetContext() { } @Test - public void testGetOptimizations() { + void testGetOptimizations() { List optimizations = new ArrayList<>(); optimizations.add(tree -> { }); optimizations.add(tree -> { }); - QueryCompiler compiler = QueryCompiler.create(PrintQuery.class) + QueryCompiler compiler = QueryCompilerBuilder.create(PrintQuery.class) .withQueryCreator(new PrintQueryCreator()) .withOptimization(optimizations.get(0)) .withOptimization(optimizations.get(1)) @@ -118,4 +114,4 @@ public void testGetOptimizations() { Assertions.assertEquals(optimizations.get(0), compiler.getOptimizations().get(0)); Assertions.assertEquals(optimizations.get(1), compiler.getOptimizations().get(1)); } -} +} \ No newline at end of file diff --git a/src/test/java/de/alexgruen/querycompiler/RegexTermVisitorTest.java b/src/test/java/de/alexgruen/query/compiler/RegexTermVisitorTest.java similarity index 88% rename from src/test/java/de/alexgruen/querycompiler/RegexTermVisitorTest.java rename to src/test/java/de/alexgruen/query/compiler/RegexTermVisitorTest.java index 8d54843..94621ac 100644 --- a/src/test/java/de/alexgruen/querycompiler/RegexTermVisitorTest.java +++ b/src/test/java/de/alexgruen/query/compiler/RegexTermVisitorTest.java @@ -1,17 +1,15 @@ -package de.alexgruen.querycompiler; +package de.alexgruen.query.compiler; -import de.alexgruen.query.compiler.QueryCompilerException; -import de.alexgruen.query.compiler.RegexTermVisitor; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.lang.reflect.Method; import java.util.regex.Pattern; -public class RegexTermVisitorTest { +class RegexTermVisitorTest { @Test - public void testConvertPattern() throws Exception { + void testConvertPattern() throws Exception { Method convertPatternMethod = RegexTermVisitor.class.getDeclaredMethod("convertPattern", String.class); convertPatternMethod.setAccessible(true); @@ -33,7 +31,7 @@ public void testConvertPattern() throws Exception { } @Test - public void testConvertPatternInvalid() throws Exception { + void testConvertPatternInvalid() throws Exception { Method convertPatternMethod = RegexTermVisitor.class.getDeclaredMethod("convertPattern", String.class); convertPatternMethod.setAccessible(true); @@ -61,4 +59,4 @@ public void testConvertPatternInvalid() throws Exception { Assertions.assertTrue(e.getCause().getMessage().contains("wrong pattern format")); } } -} +} \ No newline at end of file diff --git a/src/test/java/de/alexgruen/query/compiler/parser/ParserTest.java b/src/test/java/de/alexgruen/query/compiler/parser/ParserTest.java new file mode 100644 index 0000000..c31da55 --- /dev/null +++ b/src/test/java/de/alexgruen/query/compiler/parser/ParserTest.java @@ -0,0 +1,39 @@ +package de.alexgruen.query.compiler.parser; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.text.ParseException; + +class ParserTest { + + @Test + void testParse() throws ParseException { + TestParser parser = new TestParser(); + Integer result = parser.parse("123"); + Assertions.assertEquals(123, result); + + Assertions.assertThrows(ParseException.class, () -> parser.parse("abc")); + } + + @Test + void testParseOrNull() { + TestParser parser = new TestParser(); + Integer result = parser.parseOrNull("456"); + Assertions.assertEquals(456, result); + + Integer nullResult = parser.parseOrNull("abc"); + Assertions.assertNull(nullResult); + } + + private static class TestParser extends Parser { + @Override + public Integer parse(String s) throws ParseException { + try { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + throw new ParseException("Failed to parse: " + s, 0); + } + } + } +} \ No newline at end of file diff --git a/src/test/java/de/alexgruen/query/compiler/parser/ParserUtilTest.java b/src/test/java/de/alexgruen/query/compiler/parser/ParserUtilTest.java new file mode 100644 index 0000000..0ea77d1 --- /dev/null +++ b/src/test/java/de/alexgruen/query/compiler/parser/ParserUtilTest.java @@ -0,0 +1,137 @@ +package de.alexgruen.query.compiler.parser; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.text.ParseException; + +class ParserUtilTest { + + @Test + void testParse() throws ParseException { + String stringResult = ParserUtil.parse(String.class, "test"); + Assertions.assertEquals("test", stringResult); + + Integer intResult = ParserUtil.parse(Integer.class, "123"); + Assertions.assertEquals(123, intResult); + + Double doubleResult = ParserUtil.parse(Double.class, "123.45"); + Assertions.assertEquals(123.45, doubleResult); + + Boolean boolResult = ParserUtil.parse(Boolean.class, "true"); + Assertions.assertTrue(boolResult); + + Boolean boolResultShort = ParserUtil.parse(Boolean.class, "t"); + Assertions.assertTrue(boolResultShort); + + Boolean boolResultFalse = ParserUtil.parse(Boolean.class, "false"); + Assertions.assertFalse(boolResultFalse); + + Boolean boolResultFalseShort = ParserUtil.parse(Boolean.class, "f"); + Assertions.assertFalse(boolResultFalseShort); + + Assertions.assertThrows(ParseException.class, () -> ParserUtil.parse(Boolean.class, "invalid")); + } + + @Test + void testParseArray() throws ParseException { + String[] stringArray = ParserUtil.parse(String[].class, "a,b,c"); + Assertions.assertEquals(3, stringArray.length); + Assertions.assertEquals("a", stringArray[0]); + Assertions.assertEquals("b", stringArray[1]); + Assertions.assertEquals("c", stringArray[2]); + + Integer[] intArray = ParserUtil.parse(Integer[].class, "1;2;3"); + Assertions.assertEquals(3, intArray.length); + Assertions.assertEquals(1, intArray[0]); + Assertions.assertEquals(2, intArray[1]); + Assertions.assertEquals(3, intArray[2]); + + Double[] doubleArray = ParserUtil.parse(Double[].class, "1.1|2.2|3.3"); + Assertions.assertEquals(3, doubleArray.length); + Assertions.assertEquals(1.1, doubleArray[0]); + Assertions.assertEquals(2.2, doubleArray[1]); + Assertions.assertEquals(3.3, doubleArray[2]); + } + + @Test + void testHasParser() { + Assertions.assertTrue(ParserUtil.hasParser(String.class)); + Assertions.assertTrue(ParserUtil.hasParser(Integer.class)); + Assertions.assertTrue(ParserUtil.hasParser(Double.class)); + Assertions.assertTrue(ParserUtil.hasParser(Boolean.class)); + Assertions.assertTrue(ParserUtil.hasParser(Long.class)); + Assertions.assertTrue(ParserUtil.hasParser(Float.class)); + Assertions.assertTrue(ParserUtil.hasParser(Short.class)); + Assertions.assertTrue(ParserUtil.hasParser(Character.class)); + Assertions.assertTrue(ParserUtil.hasParser(Byte.class)); + + Assertions.assertFalse(ParserUtil.hasParser(ParserUtilTest.class)); + } + + @Test + void testGetParser() throws ParserNotFoundException { + Parser stringParser = ParserUtil.getParser(String.class); + Assertions.assertNotNull(stringParser); + + Parser intParser = ParserUtil.getParser(Integer.class); + Assertions.assertNotNull(intParser); + + Assertions.assertThrows(ParserNotFoundException.class, () -> ParserUtil.getParser(ParserUtilTest.class)); + } + + @Test + void testFindParserOrNull() { + Parser stringParser = ParserUtil.findParserOrNull(String.class); + Assertions.assertNotNull(stringParser); + + Parser nullParser = ParserUtil.findParserOrNull(ParserUtilTest.class); + Assertions.assertNull(nullParser); + } + + @Test + void testParseOrNull() { + String stringResult = ParserUtil.parseOrNull(String.class, "test"); + Assertions.assertEquals("test", stringResult); + + Integer intResult = ParserUtil.parseOrNull(Integer.class, "123"); + Assertions.assertEquals(123, intResult); + + Integer nullResult = ParserUtil.parseOrNull(Integer.class, "abc"); + Assertions.assertNull(nullResult); + + Object nullClassResult = ParserUtil.parseOrNull(ParserUtilTest.class, "test"); + Assertions.assertNull(nullClassResult); + } + + @Test + void testAddParser() throws ParseException { + class TestClass { + private final String value; + + public TestClass(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } + + Parser testParser = new Parser<>() { + @Override + public TestClass parse(String s) { + return new TestClass(s); + } + }; + + ParserUtil.addParser(TestClass.class, testParser); + + Assertions.assertTrue(ParserUtil.hasParser(TestClass.class)); + + TestClass result = ParserUtil.parse(TestClass.class, "test"); + Assertions.assertNotNull(result); + Assertions.assertEquals("test", result.toString()); + } +} \ No newline at end of file diff --git a/src/test/java/de/alexgruen/querycompiler/TermCreatorTest.java b/src/test/java/de/alexgruen/query/creator/TermCreatorTest.java similarity index 89% rename from src/test/java/de/alexgruen/querycompiler/TermCreatorTest.java rename to src/test/java/de/alexgruen/query/creator/TermCreatorTest.java index 8517f5b..3b937c6 100644 --- a/src/test/java/de/alexgruen/querycompiler/TermCreatorTest.java +++ b/src/test/java/de/alexgruen/query/creator/TermCreatorTest.java @@ -1,17 +1,16 @@ -package de.alexgruen.querycompiler; +package de.alexgruen.query.creator; import de.alexgruen.query.PrintQuery; import de.alexgruen.query.QueryNode; -import de.alexgruen.query.creator.TermCreator; import de.alexgruen.query.term.Field; import de.alexgruen.query.term.Value; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class TermCreatorTest { +class TermCreatorTest { @Test - public void testTermCreator() { + void testTermCreator() { TermCreator termCreator = (node, field, value) -> new PrintQuery() { @Override public String toString() { @@ -46,7 +45,7 @@ public String toString() { } @Test - public void testTermCreatorWithLambda() { + void testTermCreatorWithLambda() { TermCreator termCreator = (node, field, value) -> new PrintQuery() { @Override public String toString() { @@ -54,7 +53,7 @@ public String toString() { } }; - Field field = new Field("x", new String[]{"x"}); + Field field = new Field("x", "x"); Value value = new Value(123); PrintQuery query = termCreator.create(null, field, value); @@ -63,12 +62,12 @@ public String toString() { } @Test - public void testTermCreatorWithQueryNode() { + void testTermCreatorWithQueryNode() { TermCreator termCreator = (node, field, value) -> new PrintQuery() { @Override public String toString() { - return String.format("(%s %s %s)", field.getFullPath(), - node != null && node.isNegate() ? "!=" : "==", + return String.format("(%s %s %s)", field.getFullPath(), + node != null && node.isNegate() ? "!=" : "==", value.toString()); } }; @@ -87,4 +86,4 @@ public String toString() { Assertions.assertNotNull(query); Assertions.assertEquals("(x != 123)", query.toString()); } -} +} \ No newline at end of file diff --git a/src/test/java/de/alexgruen/querycompiler/ValueTest.java b/src/test/java/de/alexgruen/query/term/ValueTest.java similarity index 78% rename from src/test/java/de/alexgruen/querycompiler/ValueTest.java rename to src/test/java/de/alexgruen/query/term/ValueTest.java index 76455ea..45df9fb 100644 --- a/src/test/java/de/alexgruen/querycompiler/ValueTest.java +++ b/src/test/java/de/alexgruen/query/term/ValueTest.java @@ -1,27 +1,28 @@ -package de.alexgruen.querycompiler; +package de.alexgruen.query.term; import de.alexgruen.query.compiler.QueryCompilerException; -import de.alexgruen.query.term.Value; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; -public class ValueTest { +class ValueTest { @Test - public void testValueCreation() { + @SuppressWarnings("java:S5961") + void testValueCreation() { Value nullValue = new Value(null); Assertions.assertTrue(nullValue.isNull()); - Assertions.assertEquals(Value.Type.Null, nullValue.getType()); + Assertions.assertEquals(Value.Type.NULL, nullValue.getType()); Assertions.assertNull(nullValue.getValue()); Assertions.assertNull(nullValue.getString()); Double doubleVal = 123.45; Value doubleValue = new Value(doubleVal); - Assertions.assertEquals(Value.Type.Double, doubleValue.getType()); + Assertions.assertEquals(Value.Type.DOUBLE, doubleValue.getType()); Assertions.assertEquals(doubleVal, doubleValue.getValue()); Assertions.assertTrue(doubleValue.isNumber()); Assertions.assertEquals(doubleVal, doubleValue.getDouble()); @@ -29,7 +30,7 @@ public void testValueCreation() { Long longVal = 123L; Value longValue = new Value(longVal); - Assertions.assertEquals(Value.Type.Long, longValue.getType()); + Assertions.assertEquals(Value.Type.LONG, longValue.getType()); Assertions.assertEquals(longVal, longValue.getValue()); Assertions.assertTrue(longValue.isNumber()); Assertions.assertEquals(123.0, longValue.getDouble()); @@ -37,44 +38,44 @@ public void testValueCreation() { String stringVal = "test"; Value stringValue = new Value(stringVal); - Assertions.assertEquals(Value.Type.String, stringValue.getType()); + Assertions.assertEquals(Value.Type.STRING, stringValue.getType()); Assertions.assertEquals(stringVal, stringValue.getValue()); Assertions.assertTrue(stringValue.isString()); Assertions.assertEquals(stringVal, stringValue.getString()); Boolean boolVal = true; Value boolValue = new Value(boolVal); - Assertions.assertEquals(Value.Type.Boolean, boolValue.getType()); + Assertions.assertEquals(Value.Type.BOOLEAN, boolValue.getType()); Assertions.assertEquals(boolVal, boolValue.getValue()); Assertions.assertTrue(boolValue.isBoolean()); Assertions.assertEquals(boolVal, boolValue.getBoolean()); Pattern patternVal = Pattern.compile("test"); Value patternValue = new Value(patternVal); - Assertions.assertEquals(Value.Type.Pattern, patternValue.getType()); + Assertions.assertEquals(Value.Type.PATTERN, patternValue.getType()); Assertions.assertEquals(patternVal, patternValue.getValue()); Assertions.assertTrue(patternValue.isPattern()); Assertions.assertEquals(patternVal, patternValue.getPattern()); Integer intVal = 123; Value intValue = new Value(intVal); - Assertions.assertEquals(Value.Type.Long, intValue.getType()); + Assertions.assertEquals(Value.Type.LONG, intValue.getType()); Assertions.assertEquals(123L, intValue.getValue()); Float floatVal = 123.45f; Value floatValue = new Value(floatVal); - Assertions.assertEquals(Value.Type.Double, floatValue.getType()); + Assertions.assertEquals(Value.Type.DOUBLE, floatValue.getType()); Assertions.assertEquals(123.45, floatValue.getDouble(), 0.001); } @Test - public void testListValue() { + void testListValue() { List valueList = new ArrayList<>(); valueList.add(new Value("test1")); valueList.add(new Value(123L)); Value listValue = new Value(valueList); - Assertions.assertEquals(Value.Type.List, listValue.getType()); + Assertions.assertEquals(Value.Type.LIST, listValue.getType()); Assertions.assertTrue(listValue.isList()); Assertions.assertEquals(valueList, listValue.getList()); Assertions.assertEquals(2, listValue.getList().size()); @@ -83,17 +84,15 @@ public void testListValue() { } @Test - public void testInvalidListValue() { + void testInvalidListValue() { List invalidList = new ArrayList<>(); invalidList.add("test"); - Assertions.assertThrows(QueryCompilerException.class, () -> { - new Value(invalidList); - }); + Assertions.assertThrows(QueryCompilerException.class, () -> new Value(invalidList)); } @Test - public void testTypeConversionExceptions() { + void testTypeConversionExceptions() { Value stringValue = new Value("test"); Assertions.assertThrows(QueryCompilerException.class, stringValue::getDouble); @@ -108,18 +107,17 @@ public void testTypeConversionExceptions() { } @Test - public void testUnsupportedType() { - Assertions.assertThrows(QueryCompilerException.class, () -> { - new Value(new StringBuilder("test")); - }); + void testUnsupportedType() { + File input = new File("test"); + Assertions.assertThrows(QueryCompilerException.class, () -> new Value(input)); } @Test - public void testToString() { + void testToString() { Value stringValue = new Value("test"); Assertions.assertEquals("test", stringValue.toString()); Value nullValue = new Value(null); Assertions.assertNull(nullValue.toString()); } -} +} \ No newline at end of file diff --git a/src/test/java/de/alexgruen/querycompiler/CompilerUtilTest.java b/src/test/java/de/alexgruen/query/util/CompilerUtilTest.java similarity index 87% rename from src/test/java/de/alexgruen/querycompiler/CompilerUtilTest.java rename to src/test/java/de/alexgruen/query/util/CompilerUtilTest.java index 1d20a2b..87310f2 100644 --- a/src/test/java/de/alexgruen/querycompiler/CompilerUtilTest.java +++ b/src/test/java/de/alexgruen/query/util/CompilerUtilTest.java @@ -1,16 +1,14 @@ -package de.alexgruen.querycompiler; +package de.alexgruen.query.util; -import de.alexgruen.query.compiler.QueryCompilerException; import de.alexgruen.query.term.Field; import de.alexgruen.query.term.Value; -import de.alexgruen.query.util.CompilerUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class CompilerUtilTest { +class CompilerUtilTest { @Test - public void testCreateArray() { + void testCreateArray() { String[] stringArray = CompilerUtil.createArray(String.class, 3); Assertions.assertNotNull(stringArray); Assertions.assertEquals(3, stringArray.length); @@ -29,7 +27,7 @@ public void testCreateArray() { } @Test - public void testCreateField() { + void testCreateField() { Field simpleField = new Field("x", "x"); Assertions.assertEquals("x", simpleField.getFullPath()); Assertions.assertEquals(1, simpleField.getPath().length); @@ -44,7 +42,7 @@ public void testCreateField() { } @Test - public void testCreateValue() { + void testCreateValue() { Value nullValue = new Value(null); Assertions.assertTrue(nullValue.isNull()); @@ -57,4 +55,4 @@ public void testCreateValue() { Value boolValue = new Value(true); Assertions.assertTrue(boolValue.getBoolean()); } -} +} \ No newline at end of file diff --git a/src/test/java/de/alexgruen/querycompiler/StringTest.java b/src/test/java/de/alexgruen/query/util/StringTest.java similarity index 54% rename from src/test/java/de/alexgruen/querycompiler/StringTest.java rename to src/test/java/de/alexgruen/query/util/StringTest.java index 5df2a7f..2be8af5 100644 --- a/src/test/java/de/alexgruen/querycompiler/StringTest.java +++ b/src/test/java/de/alexgruen/query/util/StringTest.java @@ -1,45 +1,45 @@ -package de.alexgruen.querycompiler; +package de.alexgruen.query.util; -import de.alexgruen.query.util.StringUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class StringTest { +class StringTest { @Test - public void fieldParserTest(){ + void fieldParserTest() { - String[] path = StringUtil.splitQuoted("test",'.'); - Assertions.assertArrayEquals(new String[]{"test"},path); + String[] path = StringUtil.splitQuoted("test", '.'); + Assertions.assertArrayEquals(new String[]{"test"}, path); - path = StringUtil.splitQuoted("'test'",'.'); - Assertions.assertArrayEquals(new String[]{"test"},path); + path = StringUtil.splitQuoted("'test'", '.'); + Assertions.assertArrayEquals(new String[]{"test"}, path); - path = StringUtil.splitQuoted(".test",'.'); - Assertions.assertArrayEquals(new String[]{".test"},path); + path = StringUtil.splitQuoted(".test", '.'); + Assertions.assertArrayEquals(new String[]{".test"}, path); - path = StringUtil.splitQuoted(".test.x",'.'); - Assertions.assertArrayEquals(new String[]{".test","x"},path); + path = StringUtil.splitQuoted(".test.x", '.'); + Assertions.assertArrayEquals(new String[]{".test", "x"}, path); - path = StringUtil.splitQuoted("\"test\"",'.'); - Assertions.assertArrayEquals(new String[]{"test"},path); + path = StringUtil.splitQuoted("\"test\"", '.'); + Assertions.assertArrayEquals(new String[]{"test"}, path); - path = StringUtil.splitQuoted("\"te's\\\"t\"",'.'); - Assertions.assertArrayEquals(new String[]{"te's\"t"},path); + path = StringUtil.splitQuoted("\"te's\\\"t\"", '.'); + Assertions.assertArrayEquals(new String[]{"te's\"t"}, path); - path = StringUtil.splitQuoted("test.x",'.'); - Assertions.assertArrayEquals(new String[]{"test","x"},path); + path = StringUtil.splitQuoted("test.x", '.'); + Assertions.assertArrayEquals(new String[]{"test", "x"}, path); - path = StringUtil.splitQuoted("test.'x y'",'.'); - Assertions.assertArrayEquals(new String[]{"test","x y"},path); + path = StringUtil.splitQuoted("test.'x y'", '.'); + Assertions.assertArrayEquals(new String[]{"test", "x y"}, path); - path = StringUtil.splitQuoted("\"test path\".'x y'",'.'); - Assertions.assertArrayEquals(new String[]{"test path","x y"},path); + path = StringUtil.splitQuoted("\"test path\".'x y'", '.'); + Assertions.assertArrayEquals(new String[]{"test path", "x y"}, path); } + @Test - public void stringQuotesTest(){ + void stringQuotesTest() { Assertions.assertTrue(StringUtil.isQuoted("'test'")); Assertions.assertTrue(StringUtil.isQuoted("\"test\"")); Assertions.assertTrue(StringUtil.isQuoted("\"test test\"")); @@ -54,8 +54,8 @@ public void stringQuotesTest(){ Assertions.assertFalse(StringUtil.isQuoted("\"t\"es't'")); - Assertions.assertEquals("'test'", StringUtil.putInQuotes("test",'\'')); - Assertions.assertEquals("'te\\'st'", StringUtil.putInQuotes("te'st",'\'')); + Assertions.assertEquals("'test'", StringUtil.putInQuotes("test", '\'')); + Assertions.assertEquals("'te\\'st'", StringUtil.putInQuotes("te'st", '\'')); Assertions.assertEquals("test", StringUtil.stripQuotes("'test'")); Assertions.assertEquals("te'st", StringUtil.stripQuotes("'te\\'st'")); @@ -68,5 +68,4 @@ public void stringQuotesTest(){ Assertions.assertFalse(StringUtil.requiresQuotation("xx")); Assertions.assertFalse(StringUtil.requiresQuotation("x")); } - -} +} \ No newline at end of file diff --git a/src/test/java/de/alexgruen/querycompiler/QueryCreatorTest.java b/src/test/java/de/alexgruen/querycompiler/QueryCreatorTest.java deleted file mode 100644 index b8c98b5..0000000 --- a/src/test/java/de/alexgruen/querycompiler/QueryCreatorTest.java +++ /dev/null @@ -1,217 +0,0 @@ -package de.alexgruen.querycompiler; - -import de.alexgruen.query.*; -import de.alexgruen.query.term.Field; -import de.alexgruen.query.term.Value; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.regex.Pattern; - -public class QueryCreatorTest { - - private static class TestQueryCreator implements QueryCreator { - @Override - public PrintQuery ne(QueryNode queryNode, Field field, Value value) { - return createPrintQuery(String.format("(%s != %s)", field.getFullPath(), value)); - } - - @Override - public PrintQuery eq(QueryNode queryNode, Field field, Value value) { - return createPrintQuery(String.format("(%s == %s)", field.getFullPath(), value)); - } - - @Override - public PrintQuery ge(QueryNode queryNode, Field field, Value value) { - return createPrintQuery(String.format("(%s >= %s)", field.getFullPath(), value)); - } - - @Override - public PrintQuery gt(QueryNode queryNode, Field field, Value value) { - return createPrintQuery(String.format("(%s > %s)", field.getFullPath(), value)); - } - - @Override - public PrintQuery lt(QueryNode queryNode, Field field, Value value) { - return createPrintQuery(String.format("(%s < %s)", field.getFullPath(), value)); - } - - @Override - public PrintQuery le(QueryNode queryNode, Field field, Value value) { - return createPrintQuery(String.format("(%s <= %s)", field.getFullPath(), value)); - } - - @Override - public PrintQuery regex(QueryNode queryNode, Field field, Value value) { - return createPrintQuery(String.format("(%s ~= %s)", field.getFullPath(), value)); - } - - @Override - public PrintQuery text(QueryNode queryNode, Field field, Value value) { - return createPrintQuery(String.format("(%s *= %s)", field.getFullPath(), value)); - } - - @Override - public PrintQuery in(QueryNode queryNode, Field field, Value list) { - return createPrintQuery(String.format("(%s in %s)", field.getFullPath(), list)); - } - - @Override - public PrintQuery notIn(QueryNode queryNode, Field field, Value list) { - return createPrintQuery(String.format("(%s !in %s)", field.getFullPath(), list)); - } - - @Override - public PrintQuery not(QueryNode queryNode, PrintQuery v) { - return createPrintQuery(String.format("!%s", v)); - } - - @Override - public PrintQuery and(QueryNode queryNode, PrintQuery[] v) { - StringBuilder sb = new StringBuilder(); - sb.append("("); - for (int i = 0; i < v.length; i++) { - sb.append(v[i]); - if (i < v.length - 1) { - sb.append(" && "); - } - } - sb.append(")"); - return createPrintQuery(sb.toString()); - } - - @Override - public PrintQuery or(QueryNode queryNode, PrintQuery[] v) { - StringBuilder sb = new StringBuilder(); - sb.append("("); - for (int i = 0; i < v.length; i++) { - sb.append(v[i]); - if (i < v.length - 1) { - sb.append(" || "); - } - } - sb.append(")"); - return createPrintQuery(sb.toString()); - } - - @Override - public PrintQuery xor(QueryNode queryNode, PrintQuery[] v) { - StringBuilder sb = new StringBuilder(); - sb.append("("); - for (int i = 0; i < v.length; i++) { - sb.append(v[i]); - if (i < v.length - 1) { - sb.append(" ^ "); - } - } - sb.append(")"); - return createPrintQuery(sb.toString()); - } - - @Override - public PrintQuery nor(QueryNode queryNode, PrintQuery[] v) { - StringBuilder sb = new StringBuilder(); - sb.append("!("); - for (int i = 0; i < v.length; i++) { - sb.append(v[i]); - if (i < v.length - 1) { - sb.append(" || "); - } - } - sb.append(")"); - return createPrintQuery(sb.toString()); - } - - @Override - public PrintQuery fullSearch(Value value) { - return createPrintQuery(String.format("'%s'", value)); - } - - @Override - public PrintQuery empty() { - return createPrintQuery("*"); - } - - private PrintQuery createPrintQuery(final String str) { - return new PrintQuery() { - @Override - public String toString() { - return str; - } - }; - } - } - - @Test - public void testTermOperations() { - TestQueryCreator creator = new TestQueryCreator(); - QueryNode node = new QueryNode(); - Field field = new Field("x", "x"); - Value value = new Value(123); - - PrintQuery query = creator.eq(node, field, value); - Assertions.assertEquals("(x == 123)", query.toString()); - - query = creator.ne(node, field, value); - Assertions.assertEquals("(x != 123)", query.toString()); - - query = creator.gt(node, field, value); - Assertions.assertEquals("(x > 123)", query.toString()); - - query = creator.ge(node, field, value); - Assertions.assertEquals("(x >= 123)", query.toString()); - - query = creator.lt(node, field, value); - Assertions.assertEquals("(x < 123)", query.toString()); - - query = creator.le(node, field, value); - Assertions.assertEquals("(x <= 123)", query.toString()); - - Value patternValue = new Value(Pattern.compile("test")); - query = creator.regex(node, field, patternValue); - Assertions.assertEquals("(x ~= test)", query.toString()); - - Value textValue = new Value("test"); - query = creator.text(node, field, textValue); - Assertions.assertEquals("(x *= test)", query.toString()); - - Value listValue = new Value(List.of(new Value(123), new Value(124))); - query = creator.in(node, field, listValue); - Assertions.assertEquals("(x in [123, 124])", query.toString()); - - query = creator.notIn(node, field, listValue); - Assertions.assertEquals("(x !in [123, 124])", query.toString()); - - query = creator.fullSearch(textValue); - Assertions.assertEquals("'test'", query.toString()); - - query = creator.empty(); - Assertions.assertEquals("*", query.toString()); - } - - @Test - public void testLogicOperations() { - TestQueryCreator creator = new TestQueryCreator(); - QueryNode node = new QueryNode(); - - PrintQuery q1 = creator.createPrintQuery("q1"); - PrintQuery q2 = creator.createPrintQuery("q2"); - PrintQuery q3 = creator.createPrintQuery("q3"); - - PrintQuery query = creator.not(node, q1); - Assertions.assertEquals("!q1", query.toString()); - - query = creator.and(node, new PrintQuery[]{q1, q2, q3}); - Assertions.assertEquals("(q1 && q2 && q3)", query.toString()); - - query = creator.or(node, new PrintQuery[]{q1, q2, q3}); - Assertions.assertEquals("(q1 || q2 || q3)", query.toString()); - - query = creator.xor(node, new PrintQuery[]{q1, q2, q3}); - Assertions.assertEquals("(q1 ^ q2 ^ q3)", query.toString()); - - query = creator.nor(node, new PrintQuery[]{q1, q2, q3}); - Assertions.assertEquals("!(q1 || q2 || q3)", query.toString()); - } -}