org.eclipse.persistence.jpa.jpql.parser.AbstractFromClause 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;
/**
* The FROM clause of a query defines the domain of the query by declaring identification
* variables. An identification variable is an identifier declared in the FROM clause of a
* query. The domain of the query may be constrained by path expressions. Identification variables
* designate instances of a particular entity abstract schema type. The FROM clause can
* contain multiple identification variable declarations separated by a comma (,).
*
* @see FromClause
* @see SimpleFromClause
*
* @version 2.5
* @since 2.3
* @author Pascal Filion
*/
public abstract class AbstractFromClause extends AbstractExpression {
/**
* The {@link Expression} that represents the AS OF
clause.
*
* @since 2.5
*/
private AbstractExpression asOfClause;
/**
* The declaration portion of this FROM clause.
*/
private AbstractExpression declaration;
/**
* Determines whether a whitespace was parsed after the identifier FROM.
*/
private boolean hasSpace;
/**
* Determines whether there is a whitespace after the hierarchical query clause.
*
* @since 2.5
*/
private boolean hasSpaceAfterHierarchicalQueryClause;
/**
* Determines whether there is a whitespace after the declaration and either the hierarchical
* query clause or the AS OF
clause was parsed.
*
* @since 2.5
*/
private boolean hasSpaceDeclaration;
/**
* The hierarchical query clause, which holds onto the START WITH
and
* CONNECT BY
clauses.
*
* @since 2.5
*/
private AbstractExpression hierarchicalQueryClause;
/**
* The actual identifier found in the string representation of the JPQL query.
*/
private String identifier;
/**
* Creates a new AbstractFromClause
.
*
* @param parent The parent of this expression
*/
protected AbstractFromClause(AbstractExpression parent) {
super(parent, FROM);
}
@Override
public void acceptChildren(ExpressionVisitor visitor) {
getDeclaration().accept(visitor);
getHierarchicalQueryClause().accept(visitor);
getAsOfClause().accept(visitor);
}
@Override
protected void addChildrenTo(Collection children) {
children.add(getDeclaration());
children.add(getHierarchicalQueryClause());
children.add(getAsOfClause());
}
@Override
protected void addOrderedChildrenTo(List children) {
// 'FROM'
children.add(buildStringExpression(FROM));
// Space between FROM and the declaration
if (hasSpace) {
children.add(buildStringExpression(SPACE));
}
// Declaration
if (declaration != null) {
children.add(declaration);
}
if (hasSpaceDeclaration) {
children.add(buildStringExpression(SPACE));
}
// Hierarchical query clause
if (hierarchicalQueryClause != null) {
children.add(hierarchicalQueryClause);
}
if (hasSpaceAfterHierarchicalQueryClause) {
children.add(buildStringExpression(SPACE));
}
// 'AS OF' clause
if (asOfClause != null) {
children.add(asOfClause);
}
}
/**
* Creates a new {@link CollectionExpression} that will wrap the single declaration.
*
* @return The single declaration represented by a temporary collection
*/
public final CollectionExpression buildCollectionExpression() {
List children = new ArrayList<>(1);
children.add((AbstractExpression) getDeclaration());
List commas = new ArrayList<>(1);
commas.add(Boolean.FALSE);
List spaces = new ArrayList<>(1);
spaces.add(Boolean.FALSE);
return new CollectionExpression(this, children, commas, spaces, true);
}
@Override
public final JPQLQueryBNF findQueryBNF(Expression expression) {
if ((declaration != null) && declaration.isAncestor(expression)) {
return getQueryBNF(getDeclarationQueryBNFId());
}
return super.findQueryBNF(expression);
}
/**
* Returns the actual FROM identifier found in the string representation of the JPQL
* query, which has the actual case that was used.
*
* @return The FROM identifier that was actually parsed
*/
public final String getActualIdentifier() {
return identifier;
}
/**
* Returns the {@link Expression} representing the AS OF clause.
*
* @return The expression representing the AS OF clause
*/
public final Expression getAsOfClause() {
if (asOfClause == null) {
asOfClause = buildNullExpression();
}
return asOfClause;
}
/**
* Returns the {@link Expression} that represents the declaration of this clause.
*
* @return The expression that was parsed representing the declaration
*/
public final Expression getDeclaration() {
if (declaration == null) {
declaration = buildNullExpression();
}
return declaration;
}
/**
* Returns the BNF of the declaration part of this clause.
*
* @return The BNF of the declaration part of this clause
*/
public abstract String getDeclarationQueryBNFId();
/**
* Returns the {@link Expression} representing the hierarchical query clause.
*
* @return The expression representing the hierarchical query clause
* @since 2.5
*/
public final Expression getHierarchicalQueryClause() {
if (hierarchicalQueryClause == null) {
hierarchicalQueryClause = buildNullExpression();
}
return hierarchicalQueryClause;
}
/**
* Determines whether the AS OF clause is defined.
*
* @return true
if the query that got parsed had the AS OF clause
*/
public final boolean hasAsOfClause() {
return asOfClause != null &&
!asOfClause.isNull();
}
/**
* Determines whether the declaration of this clause was parsed.
*
* @return true
if the declaration of this clause was parsed; false
if
* it was not parsed
*/
public final boolean hasDeclaration() {
return declaration != null &&
!declaration.isNull();
}
/**
* Determines whether the hierarchical query clause was parsed or not.
*
* @return true
if the query that got parsed had the hierarchical query clause
* @since 2.5
*/
public final boolean hasHierarchicalQueryClause() {
return hierarchicalQueryClause != null &&
!hierarchicalQueryClause.isNull();
}
/**
* Determines whether a whitespace was found after the declaration query clause, which will be
* true
if it's followed by either the hierarchical query clause or the AS
* OF
clause.
*
* @return true
if there was a whitespace after the declaration; false
otherwise
* @since 2.5
*/
public final boolean hasSpaceAfterDeclaration() {
return hasSpaceDeclaration;
}
/**
* Determines whether a whitespace was parsed after the FROM identifier.
*
* @return true
if a whitespace was parsed after the FROM identifier;
* false
otherwise
*/
public final boolean hasSpaceAfterFrom() {
return hasSpace;
}
/**
* Determines whether a whitespace was found after the hierarchical query clause. In some cases,
* the space is owned by a child of the hierarchical query clause.
*
* @return true
if there was a whitespace after the hierarchical query clause and
* owned by this expression; false
otherwise
* @since 2.5
*/
public final boolean hasSpaceAfterHierarchicalQueryClause() {
return hasSpaceAfterHierarchicalQueryClause;
}
@Override
protected boolean isParsingComplete(WordParser wordParser, String word, Expression expression) {
char character = wordParser.character();
// TODO: Add parameter tolerance and check for these 4 signs if tolerant is turned on only
// this could happen while parsing an invalid query
return wordParser.isArithmeticSymbol(character) ||
super.isParsingComplete(wordParser, word, expression);
}
@Override
protected void parse(WordParser wordParser, boolean tolerant) {
// Parse 'FROM'
identifier = wordParser.moveForward(FROM);
hasSpace = wordParser.skipLeadingWhitespace() > 0;
// Parse the declaration
declaration = parse(wordParser, getDeclarationQueryBNFId(), tolerant);
int count = wordParser.skipLeadingWhitespace();
hasSpaceDeclaration = (count > 0);
// Parse hierarchical query clause
if (wordParser.startsWithIdentifier(START_WITH) ||
wordParser.startsWithIdentifier(CONNECT_BY) ||
wordParser.startsWithIdentifier(ORDER_SIBLINGS_BY)) {
hierarchicalQueryClause = new HierarchicalQueryClause(this);
hierarchicalQueryClause.parse(wordParser, tolerant);
count = wordParser.skipLeadingWhitespace();
hasSpaceAfterHierarchicalQueryClause = (count > 0);
}
// AS OF clause
if (wordParser.startsWithIdentifier(AS_OF)) {
asOfClause = new AsOfClause(this);
asOfClause.parse(wordParser, tolerant);
}
else if (hasSpaceAfterHierarchicalQueryClause) {
hasSpaceAfterHierarchicalQueryClause = false;
wordParser.moveBackward(count);
}
else if (hierarchicalQueryClause == null) {
hasSpaceDeclaration = false;
wordParser.moveBackward(count);
}
}
@Override
protected boolean shouldParseWithFactoryFirst() {
return true;
}
@Override
protected void toParsedText(StringBuilder writer, boolean actual) {
// 'FROM'
writer.append(actual ? identifier : FROM);
if (hasSpace) {
writer.append(SPACE);
}
// Declaration
if (declaration != null) {
declaration.toParsedText(writer, actual);
}
if (hasSpaceDeclaration) {
writer.append(SPACE);
}
// Hierarchical query clause
if (hierarchicalQueryClause != null) {
hierarchicalQueryClause.toParsedText(writer, actual);
}
if (hasSpaceAfterHierarchicalQueryClause) {
writer.append(SPACE);
}
// AS OF clause
if (asOfClause != null) {
asOfClause.toParsedText(writer, actual);
}
}
}