Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2014 - 2019 Blazebit.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.blazebit.persistence.parser;
import com.blazebit.persistence.parser.expression.AggregateExpression;
import com.blazebit.persistence.parser.expression.ArithmeticExpression;
import com.blazebit.persistence.parser.expression.ArithmeticFactor;
import com.blazebit.persistence.parser.expression.ArithmeticOperator;
import com.blazebit.persistence.parser.expression.ArrayExpression;
import com.blazebit.persistence.parser.expression.DateLiteral;
import com.blazebit.persistence.parser.expression.EntityLiteral;
import com.blazebit.persistence.parser.expression.EnumLiteral;
import com.blazebit.persistence.parser.expression.Expression;
import com.blazebit.persistence.parser.expression.FunctionExpression;
import com.blazebit.persistence.parser.expression.GeneralCaseExpression;
import com.blazebit.persistence.parser.expression.ListIndexExpression;
import com.blazebit.persistence.parser.expression.MapEntryExpression;
import com.blazebit.persistence.parser.expression.MapKeyExpression;
import com.blazebit.persistence.parser.expression.MapValueExpression;
import com.blazebit.persistence.parser.expression.NullExpression;
import com.blazebit.persistence.parser.expression.NumericLiteral;
import com.blazebit.persistence.parser.expression.OrderByItem;
import com.blazebit.persistence.parser.expression.ParameterExpression;
import com.blazebit.persistence.parser.expression.PathElementExpression;
import com.blazebit.persistence.parser.expression.PathExpression;
import com.blazebit.persistence.parser.expression.PropertyExpression;
import com.blazebit.persistence.parser.expression.SimpleCaseExpression;
import com.blazebit.persistence.parser.expression.StringLiteral;
import com.blazebit.persistence.parser.expression.SubqueryExpression;
import com.blazebit.persistence.parser.expression.TimeLiteral;
import com.blazebit.persistence.parser.expression.TimestampLiteral;
import com.blazebit.persistence.parser.expression.TreatExpression;
import com.blazebit.persistence.parser.expression.TrimExpression;
import com.blazebit.persistence.parser.expression.TypeFunctionExpression;
import com.blazebit.persistence.parser.expression.WhenClauseExpression;
import com.blazebit.persistence.parser.expression.WindowDefinition;
import com.blazebit.persistence.parser.expression.WindowFrameExclusionType;
import com.blazebit.persistence.parser.expression.WindowFramePositionType;
import com.blazebit.persistence.parser.predicate.BetweenPredicate;
import com.blazebit.persistence.parser.predicate.BooleanLiteral;
import com.blazebit.persistence.parser.predicate.CompoundPredicate;
import com.blazebit.persistence.parser.predicate.EqPredicate;
import com.blazebit.persistence.parser.predicate.ExistsPredicate;
import com.blazebit.persistence.parser.predicate.GePredicate;
import com.blazebit.persistence.parser.predicate.GtPredicate;
import com.blazebit.persistence.parser.predicate.InPredicate;
import com.blazebit.persistence.parser.predicate.IsEmptyPredicate;
import com.blazebit.persistence.parser.predicate.IsNullPredicate;
import com.blazebit.persistence.parser.predicate.LePredicate;
import com.blazebit.persistence.parser.predicate.LikePredicate;
import com.blazebit.persistence.parser.predicate.LtPredicate;
import com.blazebit.persistence.parser.predicate.MemberOfPredicate;
import com.blazebit.persistence.parser.predicate.Predicate;
import com.blazebit.persistence.parser.predicate.PredicateQuantifier;
import com.blazebit.persistence.parser.predicate.QuantifiableBinaryExpressionPredicate;
import com.blazebit.persistence.parser.util.TypeConverter;
import com.blazebit.persistence.parser.util.TypeUtils;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
*
* @author Christian Beikov
* @author Moritz Becker
* @since 1.0.0
*/
public class SimpleQueryGenerator implements Expression.Visitor {
private static final ThreadLocal INSTANCE_CACHE = new ThreadLocal() {
@Override
protected SimpleQueryGenerator initialValue() {
return new SimpleQueryGenerator(new StringBuilder());
}
};
protected StringBuilder sb;
// indicates if the query generator operates in a context where it needs conditional expressions
private BooleanLiteralRenderingContext booleanLiteralRenderingContext;
private ParameterRenderingMode parameterRenderingMode = ParameterRenderingMode.PLACEHOLDER;
public SimpleQueryGenerator() {
}
private SimpleQueryGenerator(StringBuilder sb) {
this.sb = sb;
}
/**
* Returns the thread local {@link SimpleQueryGenerator} instance.
* Take special care that this method is not invoked in a nested context since {@link SimpleQueryGenerator} is mutable.
* Always invoke clear
*
* @return the thread local instance
*/
public static SimpleQueryGenerator getThreadLocalInstance() {
return INSTANCE_CACHE.get();
}
public void generate(Expression expression) {
expression.accept(this);
}
public void clear() {
this.sb.setLength(0);
this.booleanLiteralRenderingContext = null;
this.parameterRenderingMode = ParameterRenderingMode.PLACEHOLDER;
}
public BooleanLiteralRenderingContext setBooleanLiteralRenderingContext(BooleanLiteralRenderingContext booleanLiteralRenderingContext) {
BooleanLiteralRenderingContext oldBooleanLiteralRenderingContext = this.booleanLiteralRenderingContext;
this.booleanLiteralRenderingContext = booleanLiteralRenderingContext;
return oldBooleanLiteralRenderingContext;
}
public StringBuilder getQueryBuffer() {
return sb;
}
public void setQueryBuffer(StringBuilder sb) {
this.sb = sb;
}
protected String getBooleanConditionalExpression(boolean value) {
return value ? "TRUE" : "FALSE";
}
public ParameterRenderingMode setParameterRenderingMode(ParameterRenderingMode parameterRenderingMode) {
ParameterRenderingMode oldParameterRenderingMode = this.parameterRenderingMode;
this.parameterRenderingMode = parameterRenderingMode;
return oldParameterRenderingMode;
}
protected String getBooleanExpression(boolean value) {
return value ? "TRUE" : "FALSE";
}
protected String escapeCharacter(char character) {
return Character.toString(character);
}
@Override
public void visit(final CompoundPredicate predicate) {
BooleanLiteralRenderingContext oldConditionalContext = setBooleanLiteralRenderingContext(BooleanLiteralRenderingContext.PREDICATE);
ParameterRenderingMode oldParameterRenderingMode = setParameterRenderingMode(ParameterRenderingMode.PLACEHOLDER);
boolean parenthesisRequired = predicate.getChildren().size() > 1;
if (predicate.isNegated()) {
sb.append("NOT ");
if (parenthesisRequired) {
sb.append('(');
}
}
if (predicate.getChildren().size() == 1) {
predicate.getChildren().get(0).accept(this);
return;
}
final int startLen = sb.length();
final String operator = " " + predicate.getOperator().toString() + " ";
List children = predicate.getChildren();
int size = children.size();
for (int i = 0; i < size; i++) {
Predicate child = children.get(i);
if (child instanceof CompoundPredicate && ((CompoundPredicate) child).getOperator() != predicate.getOperator() && !child.isNegated()) {
sb.append("(");
int len = sb.length();
child.accept(this);
// If the child was empty, we remove the opening parenthesis again
if (len == sb.length()) {
sb.deleteCharAt(len - 1);
} else {
sb.append(")");
sb.append(operator);
}
} else {
child.accept(this);
sb.append(operator);
}
}
// Delete the last operator only if the children actually generated something
if (startLen < sb.length()) {
sb.delete(sb.length() - operator.length(), sb.length());
}
if (predicate.isNegated() && parenthesisRequired) {
sb.append(')');
}
setBooleanLiteralRenderingContext(oldConditionalContext);
setParameterRenderingMode(oldParameterRenderingMode);
}
@Override
public void visit(final EqPredicate predicate) {
if (predicate.isNegated()) {
visitQuantifiableBinaryPredicate(predicate, " <> ");
} else {
visitQuantifiableBinaryPredicate(predicate, " = ");
}
}
@Override
public void visit(IsNullPredicate predicate) {
// Null check does not require a type to be known
ParameterRenderingMode oldParameterRenderingMode = setParameterRenderingMode(ParameterRenderingMode.PLACEHOLDER);
predicate.getExpression().accept(this);
if (predicate.isNegated()) {
sb.append(" IS NOT NULL");
} else {
sb.append(" IS NULL");
}
setParameterRenderingMode(oldParameterRenderingMode);
}
@Override
public void visit(IsEmptyPredicate predicate) {
// IS EMPTY requires a collection expression, so no need to set the nested context
predicate.getExpression().accept(this);
if (predicate.isNegated()) {
sb.append(" IS NOT EMPTY");
} else {
sb.append(" IS EMPTY");
}
}
@Override
public void visit(final MemberOfPredicate predicate) {
BooleanLiteralRenderingContext oldBooleanLiteralRenderingContext = setBooleanLiteralRenderingContext(BooleanLiteralRenderingContext.PLAIN);
// Since MEMBER OF requires a collection expression on the RHS, we can safely assume parameters are fine
ParameterRenderingMode oldParameterRenderingMode = setParameterRenderingMode(ParameterRenderingMode.PLACEHOLDER);
predicate.getLeft().accept(this);
setBooleanLiteralRenderingContext(oldBooleanLiteralRenderingContext);
setParameterRenderingMode(oldParameterRenderingMode);
if (predicate.isNegated()) {
sb.append(" NOT MEMBER OF ");
} else {
sb.append(" MEMBER OF ");
}
predicate.getRight().accept(this);
}
@Override
public void visit(final LikePredicate predicate) {
// Since like is defined for Strings, we can always infer types
ParameterRenderingMode oldParameterRenderingMode = setParameterRenderingMode(ParameterRenderingMode.PLACEHOLDER);
if (!predicate.isCaseSensitive()) {
sb.append("UPPER(");
}
predicate.getLeft().accept(this);
if (!predicate.isCaseSensitive()) {
sb.append(")");
}
if (predicate.isNegated()) {
sb.append(" NOT LIKE ");
} else {
sb.append(" LIKE ");
}
if (!predicate.isCaseSensitive()) {
sb.append("UPPER(");
}
predicate.getRight().accept(this);
if (!predicate.isCaseSensitive()) {
sb.append(")");
}
if (predicate.getEscapeCharacter() != null) {
sb.append(" ESCAPE ");
if (!predicate.isCaseSensitive()) {
sb.append("UPPER(");
}
sb.append("'").append(escapeCharacter(predicate.getEscapeCharacter())).append("'");
if (!predicate.isCaseSensitive()) {
sb.append(")");
}
}
setParameterRenderingMode(oldParameterRenderingMode);
}
@Override
public void visit(final BetweenPredicate predicate) {
// TODO: when a type can be inferred by the results of the WHEN or ELSE clauses, we can set PLACEHOLDER, otherwise we have to render literals for parameters
// TODO: Currently we assume that types can be inferred, and render parameters through but e.g. ":param1 BETWEEN :param2 AND :param3" will fail
ParameterRenderingMode oldParameterRenderingMode = setParameterRenderingMode(ParameterRenderingMode.PLACEHOLDER);
predicate.getLeft().accept(this);
if (predicate.isNegated()) {
sb.append(" NOT BETWEEN ");
} else {
sb.append(" BETWEEN ");
}
predicate.getStart().accept(this);
sb.append(" AND ");
predicate.getEnd().accept(this);
setParameterRenderingMode(oldParameterRenderingMode);
}
@Override
public void visit(final InPredicate predicate) {
// we have to render false if the parameter list for IN is empty
if (predicate.getRight().size() == 1) {
Expression right = predicate.getRight().get(0);
if (right instanceof ParameterExpression) {
ParameterExpression parameterExpr = (ParameterExpression) right;
// We might have named parameters
if (parameterExpr.getValue() != null && parameterExpr.isCollectionValued()) {
Object collection = parameterExpr.getValue();
if (((Collection) collection).isEmpty()) {
// we have to distinguish between conditional and non conditional context since hibernate parser does not support
// literal
// and the workarounds like 1 = 0 or case when only work in specific contexts
if (booleanLiteralRenderingContext == BooleanLiteralRenderingContext.PREDICATE) {
sb.append(getBooleanConditionalExpression(predicate.isNegated()));
} else {
sb.append(getBooleanExpression(predicate.isNegated()));
}
return;
}
}
} else if (right instanceof PathExpression) {
// NOTE: this is a special case where we can transform an IN predicate to an equality predicate
BooleanLiteralRenderingContext oldConditionalContext = setBooleanLiteralRenderingContext(BooleanLiteralRenderingContext.PLAIN);
predicate.getLeft().accept(this);
if (predicate.isNegated()) {
sb.append(" <> ");
} else {
sb.append(" = ");
}
right.accept(this);
setBooleanLiteralRenderingContext(oldConditionalContext);
return;
}
}
BooleanLiteralRenderingContext oldBooleanLiteralRenderingContext = setBooleanLiteralRenderingContext(BooleanLiteralRenderingContext.PLAIN);
ParameterRenderingMode oldParameterRenderingMode = setParameterRenderingMode(ParameterRenderingMode.PLACEHOLDER);
predicate.getLeft().accept(this);
if (predicate.isNegated()) {
sb.append(" NOT IN ");
} else {
sb.append(" IN ");
}
boolean paranthesisRequired;
if (predicate.getRight().size() == 1) {
// NOTE: other cases are handled by ResolvingQueryGenerator
Expression singleRightExpression = predicate.getRight().get(0);
if (singleRightExpression instanceof ParameterExpression && ((ParameterExpression) singleRightExpression).isCollectionValued()) {
paranthesisRequired = false;
} else {
paranthesisRequired = !(singleRightExpression instanceof SubqueryExpression) || !isSimpleSubquery((SubqueryExpression) singleRightExpression);
}
} else {
paranthesisRequired = true;
}
if (paranthesisRequired) {
sb.append('(');
}
if (!predicate.getRight().isEmpty()) {
predicate.getRight().get(0).accept(this);
for (int i = 1; i < predicate.getRight().size(); i++) {
sb.append(", ");
predicate.getRight().get(i).accept(this);
}
}
if (paranthesisRequired) {
sb.append(')');
}
setBooleanLiteralRenderingContext(oldBooleanLiteralRenderingContext);
setParameterRenderingMode(oldParameterRenderingMode);
}
protected boolean isSimpleSubquery(SubqueryExpression expression) {
return true;
}
@Override
public void visit(final ExistsPredicate predicate) {
if (predicate.isNegated()) {
sb.append("NOT EXISTS ");
} else {
sb.append("EXISTS ");
}
predicate.getExpression().accept(this);
}
private void visitQuantifiableBinaryPredicate(QuantifiableBinaryExpressionPredicate predicate, String operator) {
BooleanLiteralRenderingContext oldBooleanLiteralRenderingContext = setBooleanLiteralRenderingContext(BooleanLiteralRenderingContext.PLAIN);
// TODO: Currently we assume that types can be inferred, and render parameters through but e.g. ":param1 = :param2" will fail
ParameterRenderingMode oldParameterRenderingMode = setParameterRenderingMode(ParameterRenderingMode.PLACEHOLDER);
predicate.getLeft().accept(this);
sb.append(operator);
if (predicate.getQuantifier() != PredicateQuantifier.ONE) {
sb.append(predicate.getQuantifier().toString());
}
predicate.getRight().accept(this);
setBooleanLiteralRenderingContext(oldBooleanLiteralRenderingContext);
setParameterRenderingMode(oldParameterRenderingMode);
}
@Override
public void visit(final GtPredicate predicate) {
if (predicate.isNegated()) {
sb.append("NOT ");
}
visitQuantifiableBinaryPredicate(predicate, " > ");
}
@Override
public void visit(final GePredicate predicate) {
if (predicate.isNegated()) {
sb.append("NOT ");
}
visitQuantifiableBinaryPredicate(predicate, " >= ");
}
@Override
public void visit(final LtPredicate predicate) {
if (predicate.isNegated()) {
sb.append("NOT ");
}
visitQuantifiableBinaryPredicate(predicate, " < ");
}
@Override
public void visit(final LePredicate predicate) {
if (predicate.isNegated()) {
sb.append("NOT ");
}
visitQuantifiableBinaryPredicate(predicate, " <= ");
}
@Override
public void visit(ParameterExpression expression) {
String paramName;
if (expression.getName() == null) {
throw new IllegalStateException("Unsatisfied parameter " + expression.getName());
} else {
paramName = expression.getName();
}
String value;
if (ParameterRenderingMode.LITERAL == parameterRenderingMode && (value = getLiteralParameterValue(expression)) != null) {
sb.append(value);
} else {
if (Character.isDigit(paramName.charAt(0))) {
sb.append("?");
} else {
sb.append(":");
}
sb.append(paramName);
}
}
protected String getLiteralParameterValue(ParameterExpression expression) {
Object value = expression.getValue();
if (value != null) {
final TypeConverter