org.eclipse.persistence.jpa.jpql.parser.AbstractDoubleEncapsulatedExpression 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.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.persistence.jpa.jpql.WordParser;
/**
* This {@link Expression} takes care of parsing an expression that encapsulates two expressions
* separated by a comma.
*
* BNF: expression ::= <identifier>(first_expression, second_expression)
*
* @see ConcatExpression
* @see ModExpression
* @see NullIfExpression
*
* @version 2.5.1
* @since 2.3
* @author Pascal Filion
*/
public abstract class AbstractDoubleEncapsulatedExpression extends AbstractEncapsulatedExpression {
/**
* The {@link Expression} that represents the first expression.
*/
private AbstractExpression firstExpression;
/**
* Determines whether the comma separating the first and second expression was parsed.
*/
private boolean hasComma;
/**
* Determines whether a whitespace is following the comma.
*/
private boolean hasSpaceAfterComma;
/**
* The {@link Expression} that represents the second expression.
*/
private AbstractExpression secondExpression;
/**
* Creates a new AbstractDoubleEncapsulatedExpression
.
*
* @param parent The parent of this expression
* @param identifier The JPQL identifier that starts this expression
*/
protected AbstractDoubleEncapsulatedExpression(AbstractExpression parent, String identifier) {
super(parent, identifier);
}
@Override
public void acceptChildren(ExpressionVisitor visitor) {
getFirstExpression().accept(visitor);
getSecondExpression().accept(visitor);
}
@Override
protected void addChildrenTo(Collection children) {
children.add(getFirstExpression());
children.add(getSecondExpression());
}
@Override
protected void addOrderedEncapsulatedExpressionTo(List children) {
// Fist expression
if (firstExpression != null) {
children.add(firstExpression);
}
// ','
if (hasComma) {
children.add(buildStringExpression(COMMA));
}
if (hasSpaceAfterComma) {
children.add(buildStringExpression(SPACE));
}
// Second expression
if (secondExpression != null) {
children.add(secondExpression);
}
}
/**
* Creates a new {@link CollectionExpression} that will wrap the first and second expressions.
*
* @return The first and second expressions wrapped by a temporary collection
*/
public final CollectionExpression buildCollectionExpression() {
List children = new ArrayList<>(3);
children.add((AbstractExpression) getFirstExpression());
children.add((AbstractExpression) getSecondExpression());
List commas = new ArrayList<>(2);
commas.add(hasComma);
commas.add(Boolean.FALSE);
List spaces = new ArrayList<>(2);
spaces.add(hasSpaceAfterComma);
spaces.add(Boolean.FALSE);
return new CollectionExpression(this, children, commas, spaces, true);
}
@Override
public JPQLQueryBNF findQueryBNF(Expression expression) {
if ((firstExpression != null) && firstExpression.isAncestor(expression)) {
return getQueryBNF(parameterExpressionBNF(0));
}
if ((secondExpression != null) && secondExpression.isAncestor(expression)) {
return getQueryBNF(parameterExpressionBNF(1));
}
return super.findQueryBNF(expression);
}
/**
* Returns the {@link Expression} that represents the first expression.
*
* @return The expression that was parsed representing the first expression
*/
public final Expression getFirstExpression() {
if (firstExpression == null) {
firstExpression = buildNullExpression();
}
return firstExpression;
}
/**
* Returns the {@link Expression} that represents the second expression.
*
* @return The expression that was parsed representing the second expression
*/
public final Expression getSecondExpression() {
if (secondExpression == null) {
secondExpression = buildNullExpression();
}
return secondExpression;
}
/**
* Determines whether the comma was parsed after the first expression.
*
* @return true
if a comma was parsed after the first expression; false
* otherwise
*/
public final boolean hasComma() {
return hasComma;
}
@Override
public boolean hasEncapsulatedExpression() {
return hasFirstExpression() || hasComma || hasSecondExpression();
}
/**
* Determines whether the first expression of the query was parsed.
*
* @return true
if the first expression was parsed; false
if it was
* not parsed
*/
public final boolean hasFirstExpression() {
return firstExpression != null &&
!firstExpression.isNull();
}
/**
* Determines whether the second expression of the query was parsed.
*
* @return true
if the second expression was parsed; false
if it was
* not parsed
*/
public final boolean hasSecondExpression() {
return secondExpression != null &&
!secondExpression.isNull();
}
/**
* Determines whether a whitespace was parsed after the comma.
*
* @return true
if there was a whitespace after the comma; false
* otherwise
*/
public final boolean hasSpaceAfterComma() {
return hasSpaceAfterComma;
}
@Override
protected boolean isParsingComplete(WordParser wordParser, String word, Expression expression) {
char character = wordParser.character();
return character == COMMA ||
character == RIGHT_PARENTHESIS ||
word.equalsIgnoreCase(AND) ||
word.equalsIgnoreCase(OR) ||
super.isParsingComplete(wordParser, word, expression);
}
/**
* Determines whether the second expression is optional or not.
*
* @return true
if the second expression is optional; false
if it is
* required
*/
protected boolean isSecondExpressionOptional() {
return false;
}
/**
* Returns the BNF to be used to parse one of the encapsulated expression.
*
* @param index The position of the encapsulated {@link Expression} that needs to be parsed
* within the parenthesis, which starts at position 0
* @return The BNF to be used to parse one of the encapsulated expression
*/
public abstract String parameterExpressionBNF(int index);
@Override
protected void parseEncapsulatedExpression(WordParser wordParser,
int whitespaceCount,
boolean tolerant) {
int count = 0;
// Parse the first expression
firstExpression = parse(wordParser, parameterExpressionBNF(0), tolerant);
if (firstExpression != null) {
count = wordParser.skipLeadingWhitespace();
}
// Parse ','
hasComma = wordParser.startsWith(COMMA);
if (hasComma) {
count = 0;
wordParser.moveForward(1);
hasSpaceAfterComma = wordParser.skipLeadingWhitespace() > 0;
}
else if (hasFirstExpression()) {
hasSpaceAfterComma = (count > 0);
}
// Parse the second expression
secondExpression = parse(wordParser, parameterExpressionBNF(1), tolerant);
if (secondExpression == null) {
if (!hasComma && isSecondExpressionOptional()) {
hasSpaceAfterComma = false;
}
wordParser.moveBackward(count);
}
}
@Override
protected void removeEncapsulatedExpression() {
hasComma = false;
firstExpression = null;
secondExpression = null;
hasSpaceAfterComma = false;
}
@Override
protected void toParsedTextEncapsulatedExpression(StringBuilder writer, boolean actual) {
// First expression
if (firstExpression != null) {
firstExpression.toParsedText(writer, actual);
}
// ','
if (hasComma) {
writer.append(COMMA);
}
if (hasSpaceAfterComma) {
writer.append(SPACE);
}
// Second expression
if (secondExpression != null) {
secondExpression.toParsedText(writer, actual);
}
}
}