org.eclipse.persistence.jpa.jpql.AbstractValidator Maven / Gradle / Ivy
Show all versions of eclipselink Show documentation
/*
* Copyright (c) 2006, 2024 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;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.persistence.jpa.jpql.parser.AbstractExpressionVisitor;
import org.eclipse.persistence.jpa.jpql.parser.AbstractTraverseParentVisitor;
import org.eclipse.persistence.jpa.jpql.parser.AnonymousExpressionVisitor;
import org.eclipse.persistence.jpa.jpql.parser.BadExpression;
import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression;
import org.eclipse.persistence.jpa.jpql.parser.DeleteClause;
import org.eclipse.persistence.jpa.jpql.parser.DeleteStatement;
import org.eclipse.persistence.jpa.jpql.parser.Expression;
import org.eclipse.persistence.jpa.jpql.parser.ExpressionRegistry;
import org.eclipse.persistence.jpa.jpql.parser.ExpressionVisitor;
import org.eclipse.persistence.jpa.jpql.parser.FromClause;
import org.eclipse.persistence.jpa.jpql.parser.GroupByClause;
import org.eclipse.persistence.jpa.jpql.parser.HavingClause;
import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariable;
import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar;
import org.eclipse.persistence.jpa.jpql.parser.JPQLQueryBNF;
import org.eclipse.persistence.jpa.jpql.parser.NullExpression;
import org.eclipse.persistence.jpa.jpql.parser.OrderByClause;
import org.eclipse.persistence.jpa.jpql.parser.SelectClause;
import org.eclipse.persistence.jpa.jpql.parser.SelectStatement;
import org.eclipse.persistence.jpa.jpql.parser.SimpleFromClause;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectClause;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectStatement;
import org.eclipse.persistence.jpa.jpql.parser.StateFieldPathExpression;
import org.eclipse.persistence.jpa.jpql.parser.SubExpression;
import org.eclipse.persistence.jpa.jpql.parser.UnionClause;
import org.eclipse.persistence.jpa.jpql.parser.UnknownExpression;
import org.eclipse.persistence.jpa.jpql.parser.UpdateClause;
import org.eclipse.persistence.jpa.jpql.parser.UpdateStatement;
import org.eclipse.persistence.jpa.jpql.parser.WhereClause;
import org.eclipse.persistence.jpa.jpql.utility.CollectionTools;
/**
* The abstract definition of a validator, which provides helper methods and visitors.
*
* Provisional API: This interface is part of an interim API that is still under development and
* expected to change significantly before reaching stability. It is available at this early stage
* to solicit feedback from pioneering adopters on the understanding that any code that uses this
* API will almost certainly be broken (repeatedly) as the API evolves.
*
* @see AbstractGrammarValidator
* @see AbstractSemanticValidator
*
* @version 2.5
* @since 2.4
* @author Pascal Filion
*/
@SuppressWarnings("nls")
public abstract class AbstractValidator extends AnonymousExpressionVisitor {
/**
* This visitor is responsible to traverse the children of a {@link CollectionExpression} in
* order to properly validate the {@link Expression}.
*/
private BypassChildCollectionExpressionVisitor bypassChildCollectionExpressionVisitor;
/**
* This visitor is responsible to traverse the parent hierarchy and to skip {@link SubExpression}
* if it's a parent.
*/
private BypassParentSubExpressionVisitor bypassParentSubExpressionVisitor;
/**
* This visitor gathers the children of a {@link CollectionExpression} or a single visited
* {@link Expression}.
*/
private ChildrenCollectorVisitor childrenCollectorVisitor;
/**
* This visitor is used to retrieve a variable name from various type of
* {@link org.eclipse.persistence.jpa.jpql.parser.Expression JPQL Expression}.
*/
private LiteralVisitor literalVisitor;
/**
*
*
* @since 2.5
*/
private NestedArrayVisitor nestedArrayVisitor;
/**
* This visitor is responsible to traverse the parent hierarchy and to retrieve the owning clause
* of the {@link Expression} being visited.
*/
private OwningClauseVisitor owningClauseVisitor;
/**
* This visitor is responsible to traverse the parent hierarchy and to retrieve the owning
* statement of the {@link Expression} being visited.
*
* @since 2.4
*/
private OwningStatementVisitor owningStatementVisitor;
/**
* The list of {@link JPQLQueryProblem} describing grammatical and semantic issues found in the query.
*/
private Collection problems;
/**
* This visitor determines whether the visited {@link Expression} is a subquery or not.
*
* @since 2.5
*/
private SubqueryVisitor subqueryVisitor;
/**
* The {@link JPQLQueryBNFValidator} mapped by the BNF IDs.
*/
private Map validators;
/**
* Creates a new AbstractValidator
.
*/
@SuppressWarnings("this-escape")
protected AbstractValidator() {
super();
initialize();
}
/**
* Adds a new validation problem that was found in the given {@link Expression}.
*
* @param expression The {@link Expression} that is either not following the BNF grammar or that
* has semantic problems
* @param startPosition The position where the problem was encountered
* @param endPosition The position where the problem ends, inclusively
* @param messageKey The key used to retrieve the localized message describing the problem
* @param messageArguments The list of arguments that can be used to format the localized
* description of the problem
*/
protected void addProblem(Expression expression,
int startPosition,
int endPosition,
String messageKey,
String... messageArguments) {
problems.add(buildProblem(expression, startPosition, endPosition, messageKey, messageArguments));
}
/**
* Adds a new validation problem that was found in the given {@link Expression}.
*
* @param expression The {@link Expression} that is either not following the BNF grammar or that
* has semantic problems
* @param startPosition The position where the problem was encountered
* @param messageKey The key used to retrieve the localized message describing the problem
* @param messageArguments The list of arguments that can be used to format the localized
* description of the problem
*/
protected void addProblem(Expression expression,
int startPosition,
String messageKey,
String... messageArguments) {
addProblem(expression, startPosition, startPosition, messageKey, messageArguments);
}
/**
* Adds a new validation problem that was found in the given {@link Expression}. The start index
* is the position of the given {@link Expression} within the JPQL query and the end index is
* the end position of the {@link Expression} within the JPQL query.
*
* @param expression The {@link Expression} that is either not following the BNF grammar or that
* has semantic problems
* @param messageKey The key used to retrieve the localized message describing the problem
*/
protected void addProblem(Expression expression, String messageKey) {
addProblem(expression, messageKey, ExpressionTools.EMPTY_STRING_ARRAY);
}
/**
* Adds a new validation problem that was found in the given {@link Expression}. The start index
* is the position of the given {@link Expression} within the JPQL query and the end index is
* the end position of the {@link Expression} within the JPQL query.
*
* @param expression The {@link Expression} that is either not following the BNF grammar or that
* has semantic problems
* @param messageKey The key used to retrieve the localized message describing the problem
* @param arguments The list of arguments that can be used to format the localized description of
* the problem
*/
protected void addProblem(Expression expression, String messageKey, String... arguments) {
int startPosition = expression.getOffset();
int endPosition = startPosition + length(expression);
addProblem(expression, startPosition, endPosition, messageKey, arguments);
}
protected ChildrenCollectorVisitor buildChildrenCollector() {
return new ChildrenCollectorVisitor();
}
/**
* Creates the visitor that can retrieve some information about various literal.
*
* @return A new {@link LiteralVisitor}
*/
protected abstract LiteralVisitor buildLiteralVisitor();
/**
* Creates the visitor that traverses an {@link Expression} and determines if it's a nested array
* or not.
*
* @return A new {@link NestedArrayVisitor}
* @since 2.5
*/
protected NestedArrayVisitor buildNestedArrayVisitor() {
return new NestedArrayVisitor();
}
/**
* Creates the visitor that traverses the parent hierarchy of any {@link Expression} and stops at
* the first {@link Expression} that is a clause.
*
* @return A new {@link OwningClauseVisitor}
*/
protected OwningClauseVisitor buildOwningClauseVisitor() {
return new OwningClauseVisitor();
}
/**
* Creates the visitor that traverses the parent hierarchy of any {@link Expression} and stops at
* the first {@link Expression} that is a statement.
*
* @return A new {@link OwningStatementVisitor}
* @since 2.4
*/
protected OwningStatementVisitor buildOwningStatementVisitor() {
return new OwningStatementVisitor();
}
/**
* Creates a new validation problem that was found in the given {@link Expression}.
*
* @param expression The {@link Expression} that is either not following the BNF grammar or that
* has semantic problems
* @param startPosition The position where the problem was encountered
* @param endPosition The position where the problem ends, inclusively
* @param messageKey The key used to retrieve the localized message describing the problem
* @param messageArguments The list of arguments that can be used to format the localized
* description of the problem
* @return The {@link JPQLQueryProblem} describing a problem
*/
protected JPQLQueryProblem buildProblem(Expression expression,
int startPosition,
int endPosition,
String messageKey,
String... messageArguments) {
return new DefaultJPQLQueryProblem(
expression,
startPosition,
endPosition,
messageKey,
messageArguments
);
}
/**
* Creates the visitor that checks if the visited expression is a subquery or not..
*
* @return A new {@link SubqueryVisitor}
* @since 2.5
*/
protected SubqueryVisitor buildSubqueryVisitor() {
return new SubqueryVisitor();
}
/**
* Disposes this visitor.
*/
public void dispose() {
problems = null;
}
protected BypassChildCollectionExpressionVisitor getBypassChildCollectionExpressionVisitor() {
if (bypassChildCollectionExpressionVisitor == null) {
bypassChildCollectionExpressionVisitor = new BypassChildCollectionExpressionVisitor();
}
return bypassChildCollectionExpressionVisitor;
}
protected BypassParentSubExpressionVisitor getBypassParentSubExpressionVisitor() {
if (bypassParentSubExpressionVisitor == null) {
bypassParentSubExpressionVisitor = new BypassParentSubExpressionVisitor();
}
return bypassParentSubExpressionVisitor;
}
/**
* Returns a list containing either the given {@link Expression} if it's not a {@link
* CollectionExpression} or the children of the given {@link CollectionExpression}.
*
* @param expression The {@link Expression} to visit
* @return A list containing either the given {@link Expression} or the children of {@link
* CollectionExpression}
*/
protected List getChildren(Expression expression) {
ChildrenCollectorVisitor visitor = getChildrenCollectorVisitor();
try {
visitor.expressions = new LinkedList<>();
expression.accept(visitor);
return visitor.expressions;
}
finally {
visitor.expressions = null;
}
}
protected ChildrenCollectorVisitor getChildrenCollectorVisitor() {
if (childrenCollectorVisitor == null) {
childrenCollectorVisitor = buildChildrenCollector();
}
return childrenCollectorVisitor;
}
/**
* Returns the registry containing the {@link JPQLQueryBNF JPQLQueryBNFs} and the {@link
* org.eclipse.persistence.jpa.jpql.parser.ExpressionFactory ExpressionFactories} that are used
* to properly parse a JPQL query.
*
* @return The registry containing the information related to the JPQL grammar
*/
protected ExpressionRegistry getExpressionRegistry() {
return getGrammar().getExpressionRegistry();
}
protected JPQLQueryBNFValidator getExpressionValidator(String queryBNF) {
JPQLQueryBNFValidator validator = validators.get(queryBNF);
if (validator == null) {
validator = new JPQLQueryBNFValidator(getExpressionRegistry().getQueryBNF(queryBNF));
validators.put(queryBNF, validator);
}
return validator;
}
/**
* Returns the {@link JPQLGrammar} that defines how the JPQL query was parsed.
*
* @return The {@link JPQLGrammar} that was used to parse the JPQL query
*/
protected abstract JPQLGrammar getGrammar();
/**
* Returns the version of the Java Persistence this entity for which it was defined.
*
* @return The version of the Java Persistence being used
*/
protected JPAVersion getJPAVersion() {
return getGrammar().getJPAVersion();
}
/**
* Returns the {@link JPQLQueryBNFValidator} that can be used to validate an {@link Expression}
* by making sure its BNF is part of the given BNF.
*
* @param queryBNF The BNF used to determine the validity of an {@link Expression}
* @return A {@link JPQLQueryBNFValidator} that can determine if an {@link Expression} follows
* the given BNF
*/
protected JPQLQueryBNFValidator getJPQLQueryBNFValidator(JPQLQueryBNF queryBNF) {
JPQLQueryBNFValidator validator = validators.get(queryBNF.getId());
if (validator == null) {
validator = new JPQLQueryBNFValidator(queryBNF);
validators.put(queryBNF.getId(), validator);
}
return validator;
}
/**
* Returns the {@link JPQLQueryBNFValidator} that can be used to validate an {@link Expression}
* by making sure its BNF is part of the given BNF.
*
* @param queryBNF The BNF used to determine the validity of an {@link Expression}
* @return A {@link JPQLQueryBNFValidator} that can determine if an {@link Expression} follows
* the given BNF
*/
protected JPQLQueryBNFValidator getJPQLQueryBNFValidator(String queryBNF) {
return getJPQLQueryBNFValidator(getQueryBNF(queryBNF));
}
/**
* Returns the visitor that can retrieve some information about various literal.
*
* @return A {@link LiteralVisitor}
*/
protected LiteralVisitor getLiteralVisitor() {
if (literalVisitor == null) {
literalVisitor = buildLiteralVisitor();
}
return literalVisitor;
}
/**
* Returns the visitor that can determine if an {@link Expression} represents a nested array.
*
* @return A {@link NestedArrayVisitor}
* @since 2.5
*/
protected NestedArrayVisitor getNestedArrayVisitor() {
if (nestedArrayVisitor == null) {
nestedArrayVisitor = buildNestedArrayVisitor();
}
return nestedArrayVisitor;
}
/**
* Returns the visitor that traverses the parent hierarchy of any {@link Expression} and stops at
* the first {@link Expression} that is a clause.
*
* @return {@link OwningClauseVisitor}
*/
protected OwningClauseVisitor getOwningClauseVisitor() {
if (owningClauseVisitor == null) {
owningClauseVisitor = buildOwningClauseVisitor();
}
return owningClauseVisitor;
}
/**
* Returns the visitor that traverses the parent hierarchy of any {@link Expression} and stops at
* the first {@link Expression} that is a statement.
*
* @return {@link OwningStatementVisitor}
* @since 2.4
*/
protected OwningStatementVisitor getOwningStatementVisitor() {
if (owningStatementVisitor == null) {
owningStatementVisitor = buildOwningStatementVisitor();
}
return owningStatementVisitor;
}
/**
* Returns the persistence provider name.
*
* @return The name of the persistence provider, null
should never be returned
* @since 2.5
*/
protected String getProvider() {
return getGrammar().getProvider();
}
/**
* Returns the version of the persistence provider.
*
* @return The version of the persistence provider, if one is extending the default JPQL grammar
* defined in the Java Persistence specification, otherwise returns an empty string
* @since 2.4
*/
protected String getProviderVersion() {
return getGrammar().getProviderVersion();
}
/**
* Retrieves the BNF object that was registered for the given unique identifier.
*
* @param queryBNFId The unique identifier of the {@link JPQLQueryBNF} to retrieve
* @return The {@link JPQLQueryBNF} representing a section of the grammar
*/
protected JPQLQueryBNF getQueryBNF(String queryBNFId) {
return getGrammar().getExpressionRegistry().getQueryBNF(queryBNFId);
}
/**
* Returns the visitor that checks if the visited expression is a subquery or not.
*
* @return {@link SubqueryVisitor}
* @since 2.5
*/
protected SubqueryVisitor getSubqueryVisitor() {
if (subqueryVisitor == null) {
subqueryVisitor = buildSubqueryVisitor();
}
return subqueryVisitor;
}
/**
* Initializes this validator.
*/
protected void initialize() {
validators = new HashMap<>();
}
/**
* Determines whether the given {@link Expression} represents a nested array or not. To be a
* nested array, the given {@link Expression} is a {@link SubExpression} and its child is a
* {@link CollectionExpression}.
*
* @param expression The {@link Expression} to check its size
* @return true
if the given {@link Expression} is a nested array; false
otherwise
* @since 2.5
*/
protected boolean isNestedArray(Expression expression) {
return nestedArraySize(expression) > -1;
}
/**
* Determines whether the given {@link Expression} is a subquery.
*
* @param expression The {@link Expression} to check its type
* @return true
if the given {@link Expression} is a subquery; false
otherwise
* @since 2.5
*/
protected boolean isSubquery(Expression expression) {
SubqueryVisitor visitor = getSubqueryVisitor();
try {
expression.accept(visitor);
return visitor.expression != null;
}
finally {
visitor.expression = null;
}
}
/**
* Determines whether the given {@link Expression} is valid by checking its {@link JPQLQueryBNF}
* with the given {@link JPQLQueryBNF}.
*
* @param expression The {@link Expression} to validate based on the query BNF
* @param queryBNF The {@link JPQLQueryBNF} that determines if the given {@link Expression} is valid
* @return true
if the {@link Expression}'s {@link JPQLQueryBNF} is either the
* {@link JPQLQueryBNF} or a child of it; false
otherwise
*/
protected boolean isValid(Expression expression, JPQLQueryBNF queryBNF) {
JPQLQueryBNFValidator validator = getJPQLQueryBNFValidator(queryBNF);
try {
expression.accept(validator);
return validator.valid;
}
finally {
validator.valid = false;
}
}
/**
* Determines whether the given {@link Expression} is valid by checking its {@link JPQLQueryBNF}
* with the {@link JPQLQueryBNF} associated with the given unique identifier.
*
* @param expression The {@link Expression} to validate based on the query BNF
* @param queryBNFId The unique identifier of the {@link JPQLQueryBNF} that determines if the
* given {@link Expression} is valid
* @return true
if the {@link Expression}'s {@link JPQLQueryBNF} is either the
* {@link JPQLQueryBNF} or a child of it; false
otherwise
*/
protected boolean isValid(Expression expression, String queryBNFId) {
return isValid(expression, getQueryBNF(queryBNFId));
}
/**
* Determines whether the given {@link Expression} is valid by checking its {@link JPQLQueryBNF}
* with the list of {@link JPQLQueryBNF} associated with the given unique identifiers.
*
* @param expression The {@link Expression} to validate based on the query BNF
* @param queryBNFIds The unique identifier of the {@link JPQLQueryBNF} that determines if the
* given {@link Expression} is valid
* @return true
if the {@link Expression}'s {@link JPQLQueryBNF} is either the
* {@link JPQLQueryBNF} or a child of it; false
otherwise
*/
protected boolean isValid(Expression expression, String... queryBNFIds) {
for (String queryBNFId : queryBNFIds) {
if (isValid(expression, queryBNFId)) {
return true;
}
}
return false;
}
/**
* Determines whether the given {@link Expression} part is an expression of the given query BNF.
* The {@link CollectionExpression} that may be the direct child of the given {@link Expression}
* will be bypassed.
*
* @param expression The {@link Expression} to validate based on the query BNF
* @param queryBNF The unique identifier of the {@link JPQLQueryBNF} that looks up
* the {@link JPQLQueryBNFValidator}
* @return true
if the {@link Expression} part is a child of the given query BNF;
* false
otherwise
*/
protected boolean isValidWithChildCollectionBypass(Expression expression, String queryBNF) {
JPQLQueryBNFValidator validator = getExpressionValidator(queryBNF);
BypassChildCollectionExpressionVisitor bypassValidator = getBypassChildCollectionExpressionVisitor();
try {
bypassValidator.visitor = validator;
expression.accept(bypassValidator);
return validator.valid;
}
finally {
bypassValidator.visitor = null;
validator.valid = false;
}
}
/**
* Determines whether the given {@link Expression} is part of a subquery.
*
* @param expression The {@link Expression} to start scanning its location
* @return true
if the given {@link Expression} is part of a subquery; false
* if it's part of the top-level query
* @since 2.4
*/
protected boolean isWithinSubquery(Expression expression) {
OwningStatementVisitor visitor = getOwningStatementVisitor();
try {
expression.accept(visitor);
return visitor.simpleSelectStatement != null;
}
finally {
visitor.dispose();
}
}
/**
* Determines whether the given {@link Expression} is part of the top-level query.
*
* @param expression The {@link Expression} to start scanning its location
* @return true
if the given {@link Expression} is part of the top-level query;
* false
if it's part of a subquery
* @since 2.4
*/
protected boolean isWithinTopLevelQuery(Expression expression) {
OwningStatementVisitor visitor = getOwningStatementVisitor();
try {
expression.accept(visitor);
return visitor.deleteStatement != null ||
visitor.selectStatement != null ||
visitor.updateStatement != null;
}
finally {
visitor.dispose();
}
}
/**
* Returns the length of the string representation of the given {@link Expression}.
*
* @param expression The {@link Expression} to retrieve the length of its string
* @return The length of the string representation of the given {@link Expression}
*/
protected int length(Expression expression) {
return expression.getLength();
}
/**
* Retrieves the "literal" from the given {@link Expression}. The literal to retrieve depends on
* the given {@link LiteralType type}. The literal is basically a string value like an
* identification variable name, an input parameter, a path expression, an abstract schema name,
* etc.
*
* @param expression The {@link Expression} to visit
* @param type The {@link LiteralType} helps to determine what to retrieve from the visited
* {@link Expression}
* @return A value from the given {@link Expression} or an empty string if the given {@link
* Expression} and the {@link LiteralType} do not match
*/
protected String literal(Expression expression, LiteralType type) {
LiteralVisitor visitor = getLiteralVisitor();
try {
visitor.setType(type);
expression.accept(visitor);
return visitor.literal;
}
finally {
visitor.literal = ExpressionTools.EMPTY_STRING;
}
}
/**
* Returns the number of items in the nested array if the given {@link Expression} represents one.
* To be a nested array, the given {@link Expression} is a {@link SubExpression} and its child is
* a {@link CollectionExpression}.
*
* @param expression The {@link Expression} to visit
* @return The number of items in the array or -1 if the {@link Expression} is not a nested array
* @since 2.5
*/
protected int nestedArraySize(Expression expression) {
NestedArrayVisitor visitor = getNestedArrayVisitor();
try {
visitor.nestedArraySize = -1;
expression.accept(visitor);
return visitor.nestedArraySize;
}
finally {
visitor.nestedArraySize = -1;
}
}
/**
* Calculates the position of the given expression by calculating the length of what is before.
*
* @param expression The expression to determine its position within the parsed tree
* @return The length of the string representation of what comes before the given expression
*/
protected int position(Expression expression) {
return expression.getOffset();
}
/**
* Returns the current number of problems that were registered during validation.
*
* @return The current number of problems
* @since 2.4
*/
public final int problemsSize() {
return problems.size();
}
/**
* Sets the collection that will be used to store {@link JPQLQueryProblem problems} this
* validator will find in the JPQL query.
*
* @param problems A non-null
collection that will be used to store the {@link
* JPQLQueryProblem problems} if any was found
* @exception NullPointerException The Collection cannot be null
*/
public void setProblems(Collection problems) {
Assert.isNotNull(problems, "The Collection cannot be null");
this.problems = problems;
}
@Override
protected void visit(Expression expression) {
expression.acceptChildren(this);
}
/**
* This visitor is responsible to traverse the children of a {@link CollectionExpression} in
* order to properly validate the {@link Expression}.
*/
public static class BypassChildCollectionExpressionVisitor extends AnonymousExpressionVisitor {
/**
* The visitor that will visit the {@link Expression}.
*/
public JPQLQueryBNFValidator visitor;
/**
* Creates a new BypassChildCollectionExpressionVisitor
.
*/
public BypassChildCollectionExpressionVisitor() {
super();
}
@Override
public void visit(CollectionExpression expression) {
for (Expression child : expression.children()) {
child.accept(this);
if (!visitor.valid) {
break;
}
}
}
@Override
protected void visit(Expression expression) {
expression.accept(visitor);
}
@Override
public void visit(NullExpression expression) {
// Ignore this, it should be validated by another validator
}
}
/**
* This visitor is responsible to traverse the parent hierarchy and to skip {@link SubExpression}
* if it's a parent.
*/
public static class BypassParentSubExpressionVisitor extends AnonymousExpressionVisitor {
/**
* The {@link ExpressionVisitor} that will visit the {@link Expression}.
*/
public ExpressionVisitor visitor;
/**
* Creates a new BypassParentSubExpressionVisitor
.
*/
public BypassParentSubExpressionVisitor() {
super();
}
@Override
protected void visit(Expression expression) {
expression.accept(visitor);
}
@Override
public void visit(SubExpression expression) {
expression.getParent().accept(this);
}
}
/**
* This visitor gathers the children of a {@link CollectionExpression} or a single visited
* {@link Expression}.
*/
public static class ChildrenCollectorVisitor extends AnonymousExpressionVisitor {
/**
* The unique {@link Expression} that was visited or the children of {@link CollectionExpression}.
*/
protected List expressions;
/**
* Creates a new ChildrenCollectorVisitor
.
*/
public ChildrenCollectorVisitor() {
super();
}
@Override
public void visit(CollectionExpression expression) {
CollectionTools.addAll(expressions, expression.children());
}
@Override
protected void visit(Expression expression) {
expressions.add(expression);
}
@Override
public void visit(NullExpression expression) {
// Don't add it
}
}
/**
* This visitor validates any {@link Expression} by checking its BNF against some BNFs.
*/
public static class JPQLQueryBNFValidator extends AnonymousExpressionVisitor {
/**
*
*/
protected boolean bypassCompound;
/**
* The {@link JPQLQueryBNF} used to determine if the expression's BNF is valid.
*/
private JPQLQueryBNF queryBNF;
/**
* Determines whether the visited {@link Expression}'s BNF is valid based on the BNF that was
* used for validation.
*/
protected boolean valid;
/**
* Creates a new JPQLQueryBNFValidator
.
*
* @param queryBNF The {@link JPQLQueryBNF} used to determine if the expression's BNF is valid
*/
public JPQLQueryBNFValidator(JPQLQueryBNF queryBNF) {
super();
this.queryBNF = queryBNF;
}
private void allJPQLQueryBNFs(Set queryBNFIds, JPQLQueryBNF queryBNF) {
if (queryBNFIds.add(queryBNF.getId()) && (bypassCompound || !queryBNF.isCompound())) {
for (JPQLQueryBNF childQueryBNF : queryBNF.nonCompoundChildren()) {
allJPQLQueryBNFs(queryBNFIds, childQueryBNF);
}
}
}
/**
* Disposes of the internal data.
*/
public void dispose() {
valid = false;
bypassCompound = false;
}
/**
* Determines whether the visited {@link Expression} is valid or not based on the {@link
* JPQLQueryBNF} that was specified.
*
* @return true
if the {@link Expression} is valid; false
otherwise
*/
public boolean isValid() {
return valid;
}
/**
* Sets bypassCompound
*
* @param bypassCompound Indicates whether a {@link JPQLQueryBNF} representing a compound
* expression should be considered when doing the validation
*/
public void setBypassCompound(boolean bypassCompound) {
this.bypassCompound = bypassCompound;
}
/**
* Validates the given {@link JPQLQueryBNF} by making sure it is the one expected or one of
* the children from the "root" BNF passed to this validator's constructor.
*
* @param queryBNF The {@link JPQLQueryBNF} to validate
*/
public void validate(JPQLQueryBNF queryBNF) {
// By setting the flag to false will assure that if this validator is used for
// more than one item, it will reflect the global validity state. If all are
// valid, then the last expression will set the flag to true
valid = false;
// Quick check
if (queryBNF.getId() == this.queryBNF.getId()) {
valid = true;
}
// Retrieve all the children from the "root" JPQLQueryBNF and
// check if the BNF to validate is one of those children
else {
Set allQueryBNFIds = new HashSet<>();
allJPQLQueryBNFs(allQueryBNFIds, this.queryBNF);
valid = allQueryBNFIds.contains(queryBNF.getId());
}
}
@Override
public void visit(BadExpression expression) {
// This is not a valid expression
}
@Override
public void visit(CollectionExpression expression) {
// A collection expression is never valid
valid = false;
}
@Override
protected void visit(Expression expression) {
validate(expression.getQueryBNF());
}
@Override
public void visit(NullExpression expression) {
// The missing expression is validated by GrammarValidator
valid = true;
}
@Override
public void visit(StateFieldPathExpression expression) {
JPQLQueryBNF originQueryBNF = queryBNF;
if (Expression.THIS.equalsIgnoreCase(expression.toString()) &&
expression.getParentExpression().isGenerateImplicitThisAlias() &&
expression.getIdentificationVariable() != null &&
((IdentificationVariable)(expression.getIdentificationVariable())).isVirtual()) {
queryBNF = expression.getQueryBNF();
}
visit((Expression) expression);
queryBNF = originQueryBNF;
}
@Override
public void visit(SubExpression expression) {
if (expression.hasExpression()) {
expression.getExpression().accept(this);
}
}
@Override
public void visit(UnknownExpression expression) {
// This is not a valid expression
}
}
protected static class NestedArrayVisitor extends AbstractExpressionVisitor {
/**
* The number of items contained in the nested array or -1 if the {@link Expression} does not
* represent a nested array.
*/
public int nestedArraySize;
/**
* Internal flag used to determine if a sub-expression is traversed, which is required when
* representing a nested array.
*/
protected boolean subExpression;
/**
* Default constructor.
*/
protected NestedArrayVisitor() {
}
@Override
public void visit(CollectionExpression expression) {
nestedArraySize = subExpression ? expression.childrenSize() : -1;
}
@Override
public void visit(SubExpression expression) {
subExpression = true;
expression.getExpression().accept(this);
subExpression = false;
}
}
/**
* This visitor retrieves the clause owning the visited {@link Expression}.
*/
public static class OwningClauseVisitor extends AbstractTraverseParentVisitor {
public DeleteClause deleteClause;
public FromClause fromClause;
public GroupByClause groupByClause;
public HavingClause havingClause;
public OrderByClause orderByClause;
public SelectClause selectClause;
public SimpleFromClause simpleFromClause;
public SimpleSelectClause simpleSelectClause;
public UpdateClause updateClause;
public WhereClause whereClause;
public UnionClause unionClause;
/**
* Creates a new OwningClauseVisitor
.
*/
public OwningClauseVisitor() {
super();
}
/**
* Disposes the internal data.
*/
public void dispose() {
deleteClause = null;
fromClause = null;
groupByClause = null;
havingClause = null;
orderByClause = null;
selectClause = null;
simpleFromClause = null;
simpleSelectClause = null;
updateClause = null;
whereClause = null;
unionClause = null;
}
@Override
public void visit(DeleteClause expression) {
deleteClause = expression;
}
@Override
public void visit(FromClause expression) {
fromClause = expression;
}
@Override
public void visit(GroupByClause expression) {
groupByClause = expression;
}
@Override
public void visit(HavingClause expression) {
havingClause = expression;
}
@Override
public void visit(OrderByClause expression) {
orderByClause = expression;
}
@Override
public void visit(SelectClause expression) {
selectClause = expression;
}
@Override
public void visit(SimpleFromClause expression) {
simpleFromClause = expression;
}
@Override
public void visit(SimpleSelectClause expression) {
simpleSelectClause = expression;
}
@Override
public void visit(UpdateClause expression) {
updateClause = expression;
}
@Override
public void visit(WhereClause expression) {
whereClause = expression;
}
@Override
public void visit(UnionClause expression) {
this.unionClause = expression;
}
}
/**
* This visitor retrieves the statement owning the visited {@link Expression}.
*/
protected static class OwningStatementVisitor extends AbstractTraverseParentVisitor {
public DeleteStatement deleteStatement;
public SelectStatement selectStatement;
public SimpleSelectStatement simpleSelectStatement;
public UpdateStatement updateStatement;
/**
* Default constructor.
*/
protected OwningStatementVisitor() {
}
/**
* Disposes the internal data.
*/
protected void dispose() {
deleteStatement = null;
selectStatement = null;
simpleSelectStatement = null;
updateStatement = null;
}
@Override
public void visit(DeleteStatement expression) {
deleteStatement = expression;
}
@Override
public void visit(SelectStatement expression) {
selectStatement = expression;
}
@Override
public void visit(SimpleSelectStatement expression) {
simpleSelectStatement = expression;
}
@Override
public void visit(UpdateStatement expression) {
updateStatement = expression;
}
}
/**
* This visitor retrieves the statement owning the visited {@link Expression}.
*/
protected static class SubqueryVisitor extends AbstractExpressionVisitor {
/**
* The subquery is the visited {@link Expression} is a subquery.
*/
private SimpleSelectStatement expression;
/**
* Default constructor.
*/
protected SubqueryVisitor() {
}
@Override
public void visit(SimpleSelectStatement expression) {
this.expression = expression;
}
}
}