org.eclipse.persistence.jpa.jpql.parser.UnionClause 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, 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.WordParser;
/**
* The UNION clause allows the results of two queries to be combined.
*
* BNF: union_clause ::= { UNION | INTERSECT | EXCEPT } [ALL] subquery
*
* @version 2.5
* @since 2.4
* @author James Sutherland
*/
public final class UnionClause extends AbstractExpression {
/**
* The actual identifier found in the string representation of the JPQL query.
*/
private String actualIdentifier;
/**
* The actual All identifier found in the string representation of the JPQL query.
*/
private String allIdentifier;
/**
* Determines whether a whitespace was parsed after ALL.
*/
private boolean hasSpaceAfterAll;
/**
* Determines whether a whitespace was parsed after UNION.
*/
private boolean hasSpaceAfterIdentifier;
/**
* The {@link Expression} representing the unioned query.
*/
private AbstractExpression query;
/**
* Creates a new UnionClause
.
*
* @param parent The parent of this expression
* @param identifier Either UNION
, INTERSECT
or
* EXCEPT
*/
public UnionClause(AbstractExpression parent, String identifier) {
super(parent, identifier);
}
@Override
public void accept(ExpressionVisitor visitor) {
acceptUnknownVisitor(visitor);
}
@Override
public void acceptChildren(ExpressionVisitor visitor) {
getQuery().accept(visitor);
}
@Override
protected void addChildrenTo(Collection children) {
children.add(getQuery());
}
@Override
protected void addOrderedChildrenTo(List children) {
// 'UNION' or 'INTERSECT' or 'EXCEPT'
children.add(buildStringExpression(getText()));
if (hasSpaceAfterIdentifier) {
children.add(buildStringExpression(SPACE));
}
// 'ALL'
if (allIdentifier != null) {
children.add(buildStringExpression(ALL));
if (hasSpaceAfterAll) {
children.add(buildStringExpression(SPACE));
}
}
// Subquery
if (query != null) {
children.add(query);
}
}
@Override
public JPQLQueryBNF findQueryBNF(Expression expression) {
if ((query != null) && query.isAncestor(expression)) {
return getQueryBNF(SubqueryBNF.ID);
}
return super.findQueryBNF(expression);
}
/**
* Returns the actual ALL
found in the string representation of the JPQL
* query, which has the actual case that was used.
*
* @return The ALL
identifier that was actually parsed
*/
public String getActualAll() {
return allIdentifier;
}
/**
* 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 getActualIdentifier() {
return actualIdentifier;
}
/**
* Returns the union identifier.
*
* @return Either "UNION", "INTERSECT", "EXCEPT"
*/
public String getIdentifier() {
return getText();
}
/**
* Returns the {@link Expression} representing the unioned query.
*
* @return The {@link Expression} representing the subquery
*/
public Expression getQuery() {
if (query == null) {
query = buildNullExpression();
}
return query;
}
@Override
public JPQLQueryBNF getQueryBNF() {
return getQueryBNF(UnionClauseBNF.ID);
}
/**
* Determines whether ALL
was parsed.
*
* @return true
if ALL
was parsed; false
otherwise
*/
public boolean hasAll() {
return allIdentifier != null;
}
/**
* Determines whether the query was parsed.
*
* @return true
the query was parsed; false
otherwise
*/
public boolean hasQuery() {
return query != null &&
!query.isNull();
}
/**
* Determines whether a whitespace was parsed after ALL
.
*
* @return true
if a whitespace was parsed after ALL
;
* false
otherwise
*/
public boolean hasSpaceAfterAll() {
return hasSpaceAfterAll;
}
/**
* Determines whether a whitespace was parsed after the identifier.
*
* @return true
if a whitespace was parsed after the identifier; false
otherwise
*/
public boolean hasSpaceAfterIdentifier() {
return hasSpaceAfterIdentifier;
}
/**
* Determines whether this {@link UnionClause} uses the EXCEPT
identifier.
*
* @return true
if the identifier is EXCEPT
; false
otherwise
*/
public boolean isExcept() {
return getText() == EXCEPT;
}
/**
* Determines whether this {@link UnionClause} uses the INTERSECT
identifier.
*
* @return true
if the identifier is INTERSECT
; false
otherwise
*/
public boolean isIntersect() {
return getText() == INTERSECT;
}
@Override
protected boolean isParsingComplete(WordParser wordParser, String word, Expression expression) {
return word.equalsIgnoreCase(UNION) ||
word.equalsIgnoreCase(INTERSECT) ||
word.equalsIgnoreCase(EXCEPT) ||
super.isParsingComplete(wordParser, word, expression);
}
/**
* Determines whether this {@link UnionClause} uses the UNION
identifier.
*
* @return true
if the identifier is UNION
; false
otherwise
*/
public boolean isUnion() {
return getText() == UNION;
}
@Override
protected void parse(WordParser wordParser, boolean tolerant) {
// Parse the identifier
String identifier = parseIdentifier();
actualIdentifier = wordParser.moveForward(identifier);
setText(identifier);
hasSpaceAfterIdentifier = wordParser.skipLeadingWhitespace() > 0;
// Parse 'ALL'
if (wordParser.startsWithIdentifier(ALL)) {
allIdentifier = wordParser.moveForward(ALL);
hasSpaceAfterAll = wordParser.skipLeadingWhitespace() > 0;
}
// Query
if (tolerant) {
query = parse(wordParser, SubqueryBNF.ID, tolerant);
}
else {
query = new SimpleSelectStatement(this);
query.parse(wordParser, tolerant);
}
}
/**
* Parses the JPQL identifier identifier the type of union.
*
* @return Either {@link Expression#UNION UNION}, {@link Expression#INTERSECT INTERSECT} or
* {@link Expression#EXCEPT EXCEPT}
*/
protected String parseIdentifier() {
switch (getText().charAt(0)) {
case 'U': case 'u': return UNION;
case 'I': case 'i': return INTERSECT;
case 'E': case 'e': return EXCEPT;
default: return null; // Never happens
}
}
@Override
protected void toParsedText(StringBuilder writer, boolean actual) {
// 'UNION', 'INTERSECT' or 'EXCEPT'
writer.append(actual ? actualIdentifier : getText());
if (hasSpaceAfterIdentifier) {
writer.append(SPACE);
}
// 'ALL'
if (allIdentifier != null) {
writer.append(actual ? allIdentifier : ALL);
if (hasSpaceAfterAll) {
writer.append(SPACE);
}
}
// Query
if (query != null) {
query.toParsedText(writer, actual);
}
}
}