org.eclipse.persistence.jpa.jpql.parser.AbstractTripleEncapsulatedExpression 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 three expressions
* separated by a comma.
*
* BNF: expression ::= <identifier>(first_expression, second_expression, third_expression)
*
* @see LocateExpression
* @see SubstringExpression
*
* @version 2.5.1
* @since 2.3
* @author Pascal Filion
*/
public abstract class AbstractTripleEncapsulatedExpression 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 hasFirstComma;
/**
* Determines whether the comma separating the first and second expression was parsed.
*/
private boolean hasSecondComma;
/**
* Determines whether a whitespace is following the comma.
*/
private boolean hasSpaceAfterFirstComma;
/**
* Determines whether a whitespace is following the comma.
*/
private boolean hasSpaceAfterSecondComma;
/**
* Determines which child expression is been currently parsed.
*/
protected int parameterIndex;
/**
* The {@link Expression} that represents the second expression.
*/
private AbstractExpression secondExpression;
/**
* The {@link Expression} that represents the first expression.
*/
private AbstractExpression thirdExpression;
/**
* Creates a new AbstractTripleEncapsulatedExpression
.
*
* @param parent The parent of this expression
* @param identifier The JPQL identifier that starts this expression
*/
protected AbstractTripleEncapsulatedExpression(AbstractExpression parent, String identifier) {
super(parent, identifier);
}
@Override
public void acceptChildren(ExpressionVisitor visitor) {
getFirstExpression().accept(visitor);
getSecondExpression().accept(visitor);
getThirdExpression().accept(visitor);
}
@Override
protected void addChildrenTo(Collection children) {
children.add(getFirstExpression());
children.add(getSecondExpression());
children.add(getThirdExpression());
}
@Override
protected void addOrderedEncapsulatedExpressionTo(List children) {
// Fist expression
if (firstExpression != null) {
children.add(firstExpression);
}
// ','
if (hasFirstComma) {
children.add(buildStringExpression(COMMA));
}
if (hasSpaceAfterFirstComma) {
children.add(buildStringExpression(SPACE));
}
// Second expression
if (secondExpression != null) {
children.add(secondExpression);
}
// ','
if (hasSecondComma) {
children.add(buildStringExpression(COMMA));
}
if (hasSpaceAfterSecondComma) {
children.add(buildStringExpression(SPACE));
}
// Third expression
if (thirdExpression != null) {
children.add(thirdExpression);
}
}
/**
* Creates a new {@link CollectionExpression} that will wrap the first, second and third
* expressions.
*
* @return The first, second and third expressions represented by a temporary collection
*/
public final CollectionExpression buildCollectionExpression() {
List children = new ArrayList<>(3);
children.add((AbstractExpression) getFirstExpression());
children.add((AbstractExpression) getSecondExpression());
children.add((AbstractExpression) getThirdExpression());
List commas = new ArrayList<>(3);
commas.add(hasFirstComma);
commas.add(hasSecondComma);
commas.add(Boolean.FALSE);
List spaces = new ArrayList<>(3);
spaces.add(hasSpaceAfterFirstComma);
spaces.add(hasSpaceAfterSecondComma);
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(getParameterQueryBNFId(0));
}
if ((secondExpression != null) && secondExpression.isAncestor(expression)) {
return getQueryBNF(getParameterQueryBNFId(1));
}
if ((thirdExpression != null) && thirdExpression.isAncestor(expression)) {
return getQueryBNF(getParameterQueryBNFId(2));
}
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 unique identifier of the {@link JPQLQueryBNF} to be used to parse one of the
* encapsulated expression at the given position.
*
* @param index The position of the encapsulated {@link Expression} that needs to be parsed
* within the parenthesis, which starts at position 0
* @return The ID of the {@link JPQLQueryBNF} to be used to parse one of the encapsulated expression
*/
public abstract String getParameterQueryBNFId(int index);
/**
* 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;
}
/**
* Returns the {@link Expression} that represents the first expression.
*
* @return The expression that was parsed representing the first expression
*/
public final Expression getThirdExpression() {
if (thirdExpression == null) {
thirdExpression = buildNullExpression();
}
return thirdExpression;
}
@Override
public boolean hasEncapsulatedExpression() {
return hasFirstExpression() || hasFirstComma ||
hasSecondExpression() || hasSecondComma ||
hasThirdExpression();
}
/**
* 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 hasFirstComma() {
return hasFirstComma;
}
/**
* 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 comma was parsed after the second expression.
*
* @return true
if a comma was parsed after the second expression; false
* otherwise
*/
public final boolean hasSecondComma() {
return hasSecondComma;
}
/**
* 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 first comma.
*
* @return true
if there was a whitespace after the first comma; false
* otherwise
*/
public final boolean hasSpaceAfterFirstComma() {
return hasSpaceAfterFirstComma;
}
/**
* Determines whether a whitespace was parsed after the second comma.
*
* @return true
if there was a whitespace after the second comma; false
* otherwise
*/
public final boolean hasSpaceAfterSecondComma() {
return hasSpaceAfterSecondComma;
}
/**
* Determines whether the third expression of the query was parsed.
*
* @return true
if the third expression was parsed; false
if it was not
* parsed
*/
public final boolean hasThirdExpression() {
return thirdExpression != null &&
!thirdExpression.isNull();
}
@Override
protected boolean isParsingComplete(WordParser wordParser, String word, Expression expression) {
char character = wordParser.character();
// When parsing an invalid JPQL query (eg: LOCATE + ABS(e.name)) then "+ ABS(e.name)"
// should not be parsed as an invalid first expression
if ((parameterIndex == 0) &&
((character == '+') || (character == '-')) &&
!hasLeftParenthesis()) {
parameterIndex = -1;
return true;
}
return super.isParsingComplete(wordParser, word, expression);
}
/**
* Determines whether the third expression is an optional expression, which means a valid query
* can have it or not.
*
* @return true
if the third expression can either be present or not in a valid
* query; false
if it's mandatory
*/
protected abstract boolean isThirdExpressionOptional();
@Override
protected void parseEncapsulatedExpression(WordParser wordParser,
int whitespaceCount,
boolean tolerant) {
int count = 0;
// Parse the first expression
parameterIndex = 0;
firstExpression = parse(wordParser, getParameterQueryBNFId(0), tolerant);
if (firstExpression != null) {
count = wordParser.skipLeadingWhitespace();
}
// See comment in isParsingComplete()
else if (parameterIndex == -1) {
return;
}
// Parse ','
hasFirstComma = wordParser.startsWith(COMMA);
if (hasFirstComma) {
count = 0;
wordParser.moveForward(1);
hasSpaceAfterFirstComma = wordParser.skipLeadingWhitespace() > 0;
}
// Parse the second expression
parameterIndex = 1;
secondExpression = parse(wordParser, getParameterQueryBNFId(1), tolerant);
if (!hasFirstComma) {
hasSpaceAfterFirstComma = (count > 0);
}
count = wordParser.skipLeadingWhitespace();
// Parse ','
hasSecondComma = wordParser.startsWith(COMMA);
if (hasSecondComma) {
count = 0;
wordParser.moveForward(1);
hasSpaceAfterSecondComma = wordParser.skipLeadingWhitespace() > 0;
}
// Parse the third expression
parameterIndex = 2;
thirdExpression = parse(wordParser, getParameterQueryBNFId(2), tolerant);
if (!hasSecondComma && (!isThirdExpressionOptional() || (thirdExpression != null))) {
hasSpaceAfterSecondComma = (count > 0);
}
}
@Override
protected void removeEncapsulatedExpression() {
hasFirstComma = false;
hasSecondComma = false;
firstExpression = null;
thirdExpression = null;
secondExpression = null;
hasSpaceAfterFirstComma = false;
hasSpaceAfterSecondComma = false;
}
@Override
protected final void toParsedTextEncapsulatedExpression(StringBuilder writer, boolean actual) {
// First expression
if (firstExpression != null) {
firstExpression.toParsedText(writer, actual);
}
// ','
if (hasFirstComma) {
writer.append(COMMA);
}
if (hasSpaceAfterFirstComma) {
writer.append(SPACE);
}
// Second expression
if (secondExpression != null) {
secondExpression.toParsedText(writer, actual);
}
// ','
if (hasSecondComma) {
writer.append(COMMA);
}
if (hasSpaceAfterSecondComma) {
writer.append(SPACE);
}
// Third expression
if (thirdExpression != null) {
thirdExpression.toParsedText(writer, actual);
}
}
}