org.eclipse.persistence.jpa.jpql.parser.BetweenExpression Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation
//
package org.eclipse.persistence.jpa.jpql.parser;
import java.util.Collection;
import java.util.List;
import org.eclipse.persistence.jpa.jpql.ExpressionTools;
import org.eclipse.persistence.jpa.jpql.WordParser;
/**
* Used in conditional expression to determine whether the result of an expression falls within an
* inclusive range of values. Numeric, string and date expression can be evaluated in this way.
*
* BNF: between_expression ::= arithmetic_expression [NOT] BETWEEN arithmetic_expression AND arithmetic_expression |
* string_expression [NOT] BETWEEN string_expression AND string_expression |
* datetime_expression [NOT] BETWEEN datetime_expression AND datetime_expression
*
* @version 2.5
* @since 2.3
* @author Pascal Filion
*/
public final class BetweenExpression extends AbstractExpression {
/**
* The actual AND identifier found in the string representation of the JPQL query.
*/
private String andIdentifier;
/**
* The actual identifier found in the string representation of the JPQL query.
*/
private String betweenIdentifier;
/**
* The {@link Expression} to be tested for an inclusive range of values.
*/
private AbstractExpression expression;
/**
* Determines whether a whitespace was found after AND.
*/
private boolean hasSpaceAfterAnd;
/**
* Determines whether a whitespace was found after BETWEEN.
*/
private boolean hasSpaceAfterBetween;
/**
* Determines whether a whitespace was found after the lower bound expression.
*/
private boolean hasSpaceAfterLowerBound;
/**
* The {@link Expression} representing the lower bound expression.
*/
private AbstractExpression lowerBoundExpression;
/**
* The actual NOT identifier found in the string representation of the JPQL query.
*/
private String notIdentifier;
/**
* The {@link Expression} representing the upper bound expression.
*/
private AbstractExpression upperBoundExpression;
/**
* Creates a new BetweenExpression
.
*
* @param parent The parent of this expression
* @param expression The {@link Expression} that is tested to be inclusive in a range of values
*/
public BetweenExpression(AbstractExpression parent, AbstractExpression expression) {
super(parent, BETWEEN);
if (expression != null) {
this.expression = expression;
this.expression.setParent(this);
}
}
@Override
public void accept(ExpressionVisitor visitor) {
visitor.visit(this);
}
@Override
public void acceptChildren(ExpressionVisitor visitor) {
getExpression().accept(visitor);
getLowerBoundExpression().accept(visitor);
getUpperBoundExpression().accept(visitor);
}
@Override
protected void addChildrenTo(Collection children) {
children.add(getExpression());
children.add(getLowerBoundExpression());
children.add(getUpperBoundExpression());
}
@Override
protected void addOrderedChildrenTo(List children) {
// Expression
if (hasExpression()) {
children.add(expression);
children.add(buildStringExpression(SPACE));
}
// 'NOT'
if (notIdentifier != null) {
children.add(buildStringExpression(NOT));
children.add(buildStringExpression(SPACE));
}
// BETWEEN x AND y
// Identifier
children.add(buildStringExpression(getText()));
if (hasSpaceAfterBetween) {
children.add(buildStringExpression(SPACE));
}
// Lower bound expression
if (lowerBoundExpression != null) {
children.add(lowerBoundExpression);
}
if (hasSpaceAfterLowerBound) {
children.add(buildStringExpression(SPACE));
}
// 'AND'
if (andIdentifier != null) {
children.add(buildStringExpression(AND));
}
if (hasSpaceAfterAnd) {
children.add(buildStringExpression(SPACE));
}
// Upper bound expression
if (upperBoundExpression != null) {
children.add(upperBoundExpression);
}
}
@Override
public JPQLQueryBNF findQueryBNF(Expression expression) {
if ((upperBoundExpression != null) && upperBoundExpression.isAncestor(expression) ||
(lowerBoundExpression != null) && lowerBoundExpression.isAncestor(expression)) {
return getQueryBNF(InternalBetweenExpressionBNF.ID);
}
// There is no generic BNF so we'll generalize with scalar expression
if ((this.expression != null) && expression.isAncestor(expression)) {
return getQueryBNF(ScalarExpressionBNF.ID);
}
return super.findQueryBNF(expression);
}
/**
* Returns the actual AND identifier found in the string representation of the JPQL query,
* which has the actual case that was used.
*
* @return The AND identifier that was actually parsed, or an empty string if it was not parsed
*/
public String getActualAndIdentifier() {
return (andIdentifier != null) ? andIdentifier : ExpressionTools.EMPTY_STRING;
}
/**
* Returns the actual identifier found in the string representation of the JPQL query, which has the actual
* case that was used.
*
* @return The identifier that was actually parsed
*/
public String getActualBetweenIdentifier() {
return betweenIdentifier;
}
/**
* Returns the actual NOT identifier found in the string representation of the JPQL query,
* which has the actual case that was used.
*
* @return The NOT identifier that was actually parsed, or an empty string if it was not
* parsed
*/
public String getActualNotIdentifier() {
return (notIdentifier != null) ? notIdentifier : ExpressionTools.EMPTY_STRING;
}
/**
* Returns the unique identifier of the BNF for the lower and upper bound expressions.
*
* @return The unique identifier of the JPQL query BNF for the lower and upper bound expressions
*/
public String getBoundExpressionQueryBNFId() {
return InternalBetweenExpressionBNF.ID;
}
/**
* Returns the {@link Expression} representing the expression to be tested for a range of values.
*
* @return The expression that was parsed representing the expression to be tested
*/
public Expression getExpression() {
if (expression == null) {
expression = buildNullExpression();
}
return expression;
}
/**
* Returns the identifier for this expression that may include NOT if it was parsed.
*
* @return Either BETWEEN or NOT BETWEEN
*/
public String getIdentifier() {
return (notIdentifier != null) ? NOT_BETWEEN : BETWEEN;
}
/**
* Returns the {@link Expression} representing the lower bound expression.
*
* @return The expression that was parsed representing the lower bound expression
*/
public Expression getLowerBoundExpression() {
if (lowerBoundExpression == null) {
lowerBoundExpression = buildNullExpression();
}
return lowerBoundExpression;
}
@Override
public JPQLQueryBNF getQueryBNF() {
return getQueryBNF(BetweenExpressionBNF.ID);
}
/**
* Returns the {@link Expression} representing the upper bound expression.
*
* @return The expression that was parsed representing the upper bound expression
*/
public Expression getUpperBoundExpression() {
if (upperBoundExpression == null) {
upperBoundExpression = buildNullExpression();
}
return upperBoundExpression;
}
/**
* Determines whether the identifier AND was part of the query.
*
* @return true
if the identifier AND was parsed; false
otherwise
*/
public boolean hasAnd() {
return andIdentifier != null;
}
/**
* Determines whether the identifier BETWEEN was part of the query.
*
* @return true
if the identifier BETWEEN was parsed; false
otherwise
*/
protected boolean hasBetween() {
return betweenIdentifier != null;
}
/**
* Determines whether the expression before the identifier was parsed.
*
* @return true
if the query has the expression before BETWEEN;
* false
otherwise
*/
public boolean hasExpression() {
return expression != null &&
!expression.isNull();
}
/**
* Determines whether the lower bound expression was parsed.
*
* @return true
if the query has the lower bound expression; false
* otherwise
*/
public boolean hasLowerBoundExpression() {
return lowerBoundExpression != null &&
!lowerBoundExpression.isNull();
}
/**
* Determines whether the identifier NOT was part of the query.
*
* @return true
if the identifier NOT was parsed; false
otherwise
*/
public boolean hasNot() {
return notIdentifier != null;
}
/**
* Determines whether a whitespace was found after AND.
*
* @return true
if there was a whitespace after AND; false
otherwise
*/
public boolean hasSpaceAfterAnd() {
return hasSpaceAfterAnd;
}
/**
* Determines whether a whitespace was found after BETWEEN.
*
* @return true
if there was a whitespace after the BETWEEN;
* false
otherwise
*/
public boolean hasSpaceAfterBetween() {
return hasSpaceAfterBetween;
}
/**
* Determines whether a whitespace was found after the lower bound expression.
*
* @return true
if there was a whitespace after the lower bound expression;
* false
otherwise
*/
public boolean hasSpaceAfterLowerBound() {
return hasSpaceAfterLowerBound;
}
/**
* Determines whether the upper bound expression was parsed.
*
* @return true
if the query has the upper bound expression; false
otherwise
*/
public boolean hasUpperBoundExpression() {
return upperBoundExpression != null &&
!upperBoundExpression.isNull();
}
@Override
protected boolean isParsingComplete(WordParser wordParser, String word, Expression expression) {
return wordParser.character() == RIGHT_PARENTHESIS ||
word.equalsIgnoreCase(AND) ||
word.equalsIgnoreCase(THEN) ||
word.equalsIgnoreCase(ELSE) ||
super.isParsingComplete(wordParser, word, expression);
}
@Override
protected void parse(WordParser wordParser, boolean tolerant) {
// Parse 'NOT'
if (wordParser.startsWithIgnoreCase('N')) {
notIdentifier = wordParser.moveForward(NOT);
wordParser.skipLeadingWhitespace();
}
// Parse 'BETWEEN'
betweenIdentifier = wordParser.moveForward(BETWEEN);
hasSpaceAfterBetween = (wordParser.skipLeadingWhitespace() > 0);
// Parse lower bound expression
lowerBoundExpression = parse(wordParser, InternalBetweenExpressionBNF.ID, tolerant);
if (lowerBoundExpression != null) {
hasSpaceAfterLowerBound = (wordParser.skipLeadingWhitespace() > 0);
}
// Parse 'AND'
if (!tolerant || wordParser.startsWithIdentifier(AND)) {
andIdentifier = wordParser.moveForward(AND);
hasSpaceAfterAnd = (wordParser.skipLeadingWhitespace() > 0);
}
// Parse upper bound expression
upperBoundExpression = parse(wordParser, InternalBetweenExpressionBNF.ID, tolerant);
}
@Override
protected void toParsedText(StringBuilder writer, boolean actual) {
// Expression
if (hasExpression()) {
expression.toParsedText(writer, actual);
writer.append(SPACE);
}
// 'NOT'
if (notIdentifier != null) {
writer.append(actual ? notIdentifier : NOT);
writer.append(SPACE);
}
// Identifier
if (betweenIdentifier != null) {
writer.append(actual ? betweenIdentifier : BETWEEN);
}
if (hasSpaceAfterBetween) {
writer.append(SPACE);
}
// Lower bound expression
if (lowerBoundExpression != null) {
lowerBoundExpression.toParsedText(writer, actual);
}
if (hasSpaceAfterLowerBound) {
writer.append(SPACE);
}
// 'AND'
if (andIdentifier != null) {
writer.append(actual ? andIdentifier : AND);
}
if (hasSpaceAfterAnd) {
writer.append(SPACE);
}
// Upper bound expression
if (upperBoundExpression != null) {
upperBoundExpression.toParsedText(writer, actual);
}
}
}