org.eclipse.persistence.jpa.jpql.parser.HierarchicalQueryClause 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) 2012, 2018 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.WordParser;
/**
* If a table contains hierarchical data, then rows can be selected in a hierarchical order using
* the hierarchical query clause.
*
* START WITH
specifies the root row(s) of the hierarchy.
*
* CONNECT BY
specifies the relationship between parent rows and child rows of
* the hierarchy.
*
*
BNF: hierarchical_query_clause ::= [start_with_clause] connectby_clause [order_siblings_by_clause]
*
* @see StartWithClause
* @see ConnectByClause
* @see OrderSiblingsByClause
*
* @version 2.5
* @since 2.5
* @author Pascal Filion
*/
public final class HierarchicalQueryClause extends AbstractExpression {
/**
* The {@link Expression} that represents the CONNECT BY
clause.
*/
private AbstractExpression connectByClause;
/**
* Determines whether a whitespace was parsed after the CONNECT BY
clause.
*/
private boolean hasSpaceAfterConnectByClause;
/**
* Determines whether a whitespace was parsed after the START WITH
clause.
*/
private boolean hasSpaceAfterStartWithClause;
/**
* The {@link Expression} that represents the ORDER SIBLINGS BY
clause.
*/
private AbstractExpression orderSiblingsByClause;
/**
* The {@link Expression} that represents the START WITH
clause.
*/
private AbstractExpression startWithClause;
/**
* Creates a new HierarchicalQueryClause
.
*
* @param parent The parent of this expression
*/
public HierarchicalQueryClause(AbstractExpression parent) {
super(parent);
}
/**
* {@inheritDoc}
*/
public void accept(ExpressionVisitor visitor) {
acceptUnknownVisitor(visitor);
}
/**
* {@inheritDoc}
*/
public void acceptChildren(ExpressionVisitor visitor) {
getStartWithClause().accept(visitor);
getConnectByClause().accept(visitor);
getOrderSiblingsByClause().accept(visitor);
}
/**
* {@inheritDoc}
*/
@Override
protected void addChildrenTo(Collection children) {
super.addChildrenTo(children);
children.add(getStartWithClause());
children.add(getConnectByClause());
children.add(getOrderSiblingsByClause());
}
/**
* {@inheritDoc}
*/
@Override
protected void addOrderedChildrenTo(List children) {
// START WITH clause
if (startWithClause != null) {
children.add(startWithClause);
}
if (hasSpaceAfterStartWithClause) {
children.add(buildStringExpression(SPACE));
}
// CONNECT BY clause
if (connectByClause != null) {
children.add(connectByClause);
}
if (hasSpaceAfterConnectByClause) {
children.add(buildStringExpression(SPACE));
}
// ORDER SIBLINGS BY clause
if (orderSiblingsByClause != null) {
children.add(orderSiblingsByClause);
}
}
/**
* {@inheritDoc}
*/
@Override
public JPQLQueryBNF findQueryBNF(Expression expression) {
if ((startWithClause != null) && startWithClause.isAncestor(expression)) {
return getQueryBNF(StartWithClauseBNF.ID);
}
if ((connectByClause != null) && connectByClause.isAncestor(expression)) {
return getQueryBNF(ConnectByClauseBNF.ID);
}
if ((orderSiblingsByClause != null) && orderSiblingsByClause.isAncestor(expression)) {
return getQueryBNF(OrderSiblingsByClauseBNF.ID);
}
return super.findQueryBNF(expression);
}
/**
* Returns the {@link Expression} representing the CONNECT BY clause.
*
* @return The expression representing the CONNECT BY clause
*/
public Expression getConnectByClause() {
if (connectByClause == null) {
connectByClause = buildNullExpression();
}
return connectByClause;
}
/**
* Returns the {@link Expression} representing the ORDER SIBLINGS BY clause.
*
* @return The expression representing the ORDER SIBLINGS BY clause
*/
public Expression getOrderSiblingsByClause() {
if (orderSiblingsByClause == null) {
orderSiblingsByClause = buildNullExpression();
}
return orderSiblingsByClause;
}
/**
* {@inheritDoc}
*/
public JPQLQueryBNF getQueryBNF() {
return getQueryBNF(HierarchicalQueryClauseBNF.ID);
}
/**
* Returns the {@link Expression} representing the START WITH clause.
*
* @return The expression representing the START WITH clause
*/
public Expression getStartWithClause() {
if (startWithClause == null) {
startWithClause = buildNullExpression();
}
return startWithClause;
}
/**
* Determines whether the CONNECT BY clause is defined.
*
* @return true
if the query that got parsed had the CONNECT BY clause
*/
public boolean hasConnectByClause() {
return connectByClause != null &&
!connectByClause.isNull();
}
/**
* Determines whether the ORDER SIBLINGS BY clause is defined.
*
* @return true
if the query that got parsed had the ORDER SIBLINGS BY clause
*/
public boolean hasOrderSiblingsByClause() {
return orderSiblingsByClause != null &&
!orderSiblingsByClause.isNull();
}
/**
* Determines whether a whitespace was found after the CONNECT BY
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 CONNECT BY
* clause and owned by this expression; false
otherwise
*/
public boolean hasSpaceAfterConnectByClause() {
return hasSpaceAfterConnectByClause;
}
/**
* Determines whether a whitespace was found after the START WITH
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 START WITH
* clause and owned by this expression; false
otherwise
*/
public boolean hasSpaceAfterStartWithClause() {
return hasSpaceAfterStartWithClause;
}
/**
* Determines whether the START WITH clause is defined.
*
* @return true
if the query that got parsed had the START WITH clause
*/
public boolean hasStartWithClause() {
return startWithClause != null &&
!startWithClause.isNull();
}
/**
* {@inheritDoc}
*/
@Override
protected void parse(WordParser wordParser, boolean tolerant) {
int count = 0;
// START WITH clause
if (wordParser.startsWithIdentifier(START_WITH)) {
startWithClause = new StartWithClause(this);
startWithClause.parse(wordParser, tolerant);
count = wordParser.skipLeadingWhitespace();
hasSpaceAfterStartWithClause = (count > 0);
}
// CONNECT BY clause
if (wordParser.startsWithIdentifier(CONNECT_BY)) {
connectByClause = new ConnectByClause(this);
connectByClause.parse(wordParser, tolerant);
count = wordParser.skipLeadingWhitespace();
hasSpaceAfterConnectByClause = (count > 0);
}
// ORDER SIBLINGS BY clause
if (wordParser.startsWithIdentifier(ORDER_SIBLINGS_BY)) {
orderSiblingsByClause = new OrderSiblingsByClause(this);
orderSiblingsByClause.parse(wordParser, tolerant);
}
// Let's the parent statement own the whitespace
if (hasSpaceAfterStartWithClause &&
connectByClause == null &&
orderSiblingsByClause == null) {
hasSpaceAfterStartWithClause = false;
wordParser.moveBackward(count);
}
else if (hasSpaceAfterConnectByClause &&
orderSiblingsByClause == null) {
hasSpaceAfterConnectByClause = false;
wordParser.moveBackward(count);
}
}
/**
* {@inheritDoc}
*/
@Override
protected void toParsedText(StringBuilder writer, boolean actual) {
// START WITH clause
if (startWithClause != null) {
startWithClause.toParsedText(writer, actual);
if (hasSpaceAfterStartWithClause) {
writer.append(SPACE);
}
}
// CONNECT BY clause
if (connectByClause != null) {
connectByClause.toParsedText(writer, actual);
if (hasSpaceAfterConnectByClause) {
writer.append(SPACE);
}
}
// ORDER SIBLINGS BY clause
if (orderSiblingsByClause != null) {
orderSiblingsByClause.toParsedText(writer, actual);
}
}
}