| /* |
| * Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2021 IBM Corporation. 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 |
| // |
| // 09/02/2019-3.0 - Alexandre Jacob |
| // - 527415: Fix code when locale is tr, az or lt |
| package org.eclipse.persistence.jpa.jpql; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import org.eclipse.persistence.jpa.jpql.JPQLQueryDeclaration.Type; |
| import org.eclipse.persistence.jpa.jpql.parser.AbsExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractExpressionVisitor; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractFromClause; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractSchemaName; |
| import org.eclipse.persistence.jpa.jpql.parser.AbstractSingleEncapsulatedExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.AdditionExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.AllOrAnyExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.AndExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.AnonymousExpressionVisitor; |
| import org.eclipse.persistence.jpa.jpql.parser.ArithmeticExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ArithmeticFactor; |
| import org.eclipse.persistence.jpa.jpql.parser.AvgFunction; |
| import org.eclipse.persistence.jpa.jpql.parser.BadExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.BetweenExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CaseExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CoalesceExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CollectionMemberDeclaration; |
| import org.eclipse.persistence.jpa.jpql.parser.CollectionMemberExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ComparisonExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CompoundExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ConcatExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ConstructorExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.CountFunction; |
| import org.eclipse.persistence.jpa.jpql.parser.DateTime; |
| import org.eclipse.persistence.jpa.jpql.parser.DeleteClause; |
| import org.eclipse.persistence.jpa.jpql.parser.DeleteStatement; |
| import org.eclipse.persistence.jpa.jpql.parser.DivisionExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.EmptyCollectionComparisonExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.EntityTypeLiteral; |
| import org.eclipse.persistence.jpa.jpql.parser.EntryExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ExistsExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.Expression; |
| import org.eclipse.persistence.jpa.jpql.parser.FromClause; |
| import org.eclipse.persistence.jpa.jpql.parser.FunctionExpression; |
| 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.IdentificationVariableDeclaration; |
| import org.eclipse.persistence.jpa.jpql.parser.InExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.IndexExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.InputParameter; |
| import org.eclipse.persistence.jpa.jpql.parser.JPQLExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar; |
| import org.eclipse.persistence.jpa.jpql.parser.Join; |
| import org.eclipse.persistence.jpa.jpql.parser.KeyExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.KeywordExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.LengthExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.LikeExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.LocateExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.LowerExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.MaxFunction; |
| import org.eclipse.persistence.jpa.jpql.parser.MinFunction; |
| import org.eclipse.persistence.jpa.jpql.parser.ModExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.MultiplicationExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.NotExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.NullComparisonExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.NullExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.NullIfExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.NumericLiteral; |
| import org.eclipse.persistence.jpa.jpql.parser.ObjectExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.OnClause; |
| import org.eclipse.persistence.jpa.jpql.parser.OrExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.OrderByClause; |
| import org.eclipse.persistence.jpa.jpql.parser.OrderByItem; |
| import org.eclipse.persistence.jpa.jpql.parser.RangeVariableDeclaration; |
| import org.eclipse.persistence.jpa.jpql.parser.ResultVariable; |
| 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.SizeExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.SqrtExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.StateFieldPathExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.StringLiteral; |
| import org.eclipse.persistence.jpa.jpql.parser.SubExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.SubstringExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.SubtractionExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.SumFunction; |
| import org.eclipse.persistence.jpa.jpql.parser.TreatExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.TrimExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.TypeExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.UnknownExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.UpdateClause; |
| import org.eclipse.persistence.jpa.jpql.parser.UpdateItem; |
| import org.eclipse.persistence.jpa.jpql.parser.UpdateStatement; |
| import org.eclipse.persistence.jpa.jpql.parser.UpperExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.ValueExpression; |
| import org.eclipse.persistence.jpa.jpql.parser.WhenClause; |
| import org.eclipse.persistence.jpa.jpql.parser.WhereClause; |
| import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.*; |
| |
| /** |
| * The base validator responsible to gather the problems found in a JPQL query by validating the |
| * content to make sure it is semantically valid, i.e. based on the information contained in the |
| * JPA application. The grammar is not validated by this visitor. |
| * <p> |
| * 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 |
| * |
| * @version 2.5.1 |
| * @since 2.4 |
| * @author Pascal Filion |
| */ |
| @SuppressWarnings("nls") |
| public abstract class AbstractSemanticValidator extends AbstractValidator { |
| |
| /** |
| * This visitor is responsible to retrieve the visited {@link Expression} if it is a |
| * {@link CollectionValuedPathExpression}. |
| */ |
| protected CollectionValuedPathExpressionVisitor collectionValuedPathExpressionVisitor; |
| |
| /** |
| * This visitor is responsible to check if the entity type literal (parsed as an identification |
| * variable) is part of a comparison expression. |
| */ |
| private ComparingEntityTypeLiteralVisitor comparingEntityTypeLiteralVisitor; |
| |
| /** |
| * This visitor visits the left and right expressions of a {@link ComparisonExpressionVisitor} |
| * and gather information that is used by {@link #validateComparisonExpression(ComparisonExpression)}. |
| */ |
| private ComparisonExpressionVisitor comparisonExpressionVisitor; |
| |
| /** |
| * The given helper allows this validator to access the JPA artifacts without using Hermes SPI. |
| */ |
| protected final SemanticValidatorHelper helper; |
| |
| /** |
| * This visitor makes sure the path expressions are properly validated. |
| */ |
| private InItemsVisitor inItemsVisitor; |
| |
| /** |
| * This flag is used to register the {@link IdentificationVariable IdentificationVariables} that |
| * are used throughout the query (top-level query and subqueries), except the identification |
| * variables defining an abstract schema name or a collection-valued path expression. |
| */ |
| protected boolean registerIdentificationVariable; |
| |
| /** |
| * This visitor is responsible to retrieve the visited {@link Expression} if it is a |
| * {@link StateFieldPathExpression}. |
| */ |
| protected StateFieldPathExpressionVisitor stateFieldPathExpressionVisitor; |
| |
| /** |
| * |
| */ |
| private SubqueryFirstDeclarationVisitor subqueryFirstDeclarationVisitor; |
| |
| /** |
| * |
| */ |
| private TopLevelFirstDeclarationVisitor topLevelFirstDeclarationVisitor; |
| |
| /** |
| * The {@link IdentificationVariable IdentificationVariables} that are used throughout the query |
| * (top-level query and subqueries), except the identification variables defining an abstract |
| * schema name or a collection-valued path expression. |
| */ |
| protected List<IdentificationVariable> usedIdentificationVariables; |
| |
| /** |
| * This finder is responsible to retrieve the virtual identification variable from the |
| * <b>UPDATE</b> range declaration since it is optional. |
| */ |
| protected BaseDeclarationIdentificationVariableFinder virtualIdentificationVariableFinder; |
| |
| /** |
| * Creates a new <code>AbstractSemanticValidator</code>. |
| * |
| * @param helper The given helper allows this validator to access the JPA artifacts without using |
| * Hermes SPI |
| * @exception NullPointerException The given {@link SemanticValidatorHelper} cannot be <code>null</code> |
| */ |
| protected AbstractSemanticValidator(SemanticValidatorHelper helper) { |
| super(); |
| Assert.isNotNull(helper, "The helper cannot be null"); |
| this.helper = helper; |
| } |
| |
| protected ComparingEntityTypeLiteralVisitor buildComparingEntityTypeLiteralVisitor() { |
| return new ComparingEntityTypeLiteralVisitor(); |
| } |
| |
| protected InItemsVisitor buildInItemsVisitor() { |
| return new InItemsVisitor(this); |
| } |
| |
| protected SubqueryFirstDeclarationVisitor buildSubqueryFirstDeclarationVisitor() { |
| return new SubqueryFirstDeclarationVisitor(); |
| } |
| |
| protected TopLevelFirstDeclarationVisitor buildTopLevelFirstDeclarationVisitor() { |
| return new TopLevelFirstDeclarationVisitor(); |
| } |
| |
| @Override |
| public void dispose() { |
| super.dispose(); |
| usedIdentificationVariables.clear(); |
| } |
| |
| /** |
| * Returns the {@link IdentificationVariable} that defines the identification variable for either |
| * a <code><b>DELETE</b></code> or an <code><b>UPDATE</b></code> query. |
| * |
| * @param expression The {@link AbstractSchemaName} that is being validated and that most likely |
| * representing an associated path expression and not an entity name |
| * @return The {@link IdentificationVariable} defining either the identification variable or the |
| * virtual identification variable for the <code><b>DELETE</b></code> or for the |
| * <code><b>UPDATE</b></code> query |
| */ |
| protected IdentificationVariable findVirtualIdentificationVariable(AbstractSchemaName expression) { |
| BaseDeclarationIdentificationVariableFinder visitor = getVirtualIdentificationVariableFinder(); |
| try { |
| expression.accept(visitor); |
| return visitor.expression; |
| } |
| finally { |
| visitor.expression = null; |
| } |
| } |
| |
| protected CollectionValuedPathExpression getCollectionValuedPathExpression(Expression expression) { |
| CollectionValuedPathExpressionVisitor visitor = getCollectionValuedPathExpressionVisitor(); |
| try { |
| expression.accept(visitor); |
| return visitor.expression; |
| } |
| finally { |
| visitor.expression = null; |
| } |
| } |
| |
| protected CollectionValuedPathExpressionVisitor getCollectionValuedPathExpressionVisitor() { |
| if (collectionValuedPathExpressionVisitor == null) { |
| collectionValuedPathExpressionVisitor = new CollectionValuedPathExpressionVisitor(); |
| } |
| return collectionValuedPathExpressionVisitor; |
| } |
| |
| protected ComparingEntityTypeLiteralVisitor getComparingEntityTypeLiteralVisitor() { |
| if (comparingEntityTypeLiteralVisitor == null) { |
| comparingEntityTypeLiteralVisitor = buildComparingEntityTypeLiteralVisitor(); |
| } |
| return comparingEntityTypeLiteralVisitor; |
| } |
| |
| protected ComparisonExpressionVisitor getComparisonExpressionVisitor() { |
| if (comparisonExpressionVisitor == null) { |
| comparisonExpressionVisitor = new ComparisonExpressionVisitor(this); |
| } |
| return comparisonExpressionVisitor; |
| } |
| |
| @Override |
| protected JPQLGrammar getGrammar() { |
| return helper.getGrammar(); |
| } |
| |
| protected InItemsVisitor getInItemsVisitor() { |
| if (inItemsVisitor == null) { |
| inItemsVisitor = buildInItemsVisitor(); |
| } |
| return inItemsVisitor; |
| } |
| |
| protected StateFieldPathExpression getStateFieldPathExpression(Expression expression) { |
| StateFieldPathExpressionVisitor visitor = getStateFieldPathExpressionVisitor(); |
| try { |
| expression.accept(visitor); |
| return visitor.expression; |
| } |
| finally { |
| visitor.expression = null; |
| } |
| } |
| |
| protected StateFieldPathExpressionVisitor getStateFieldPathExpressionVisitor() { |
| if (stateFieldPathExpressionVisitor == null) { |
| stateFieldPathExpressionVisitor = new StateFieldPathExpressionVisitor(); |
| } |
| return stateFieldPathExpressionVisitor; |
| } |
| |
| /** |
| * Returns the visitor that can find the {@link IdentificationVariable} of the {@link |
| * RangeVariableDeclaration}. This should be used when the query is either a <code><b>DELETE</b></code> |
| * or <code><b>UPDATE</b></code> query. |
| * |
| * @return The visitor that can traverse the query and returns the {@link IdentificationVariable} |
| */ |
| protected BaseDeclarationIdentificationVariableFinder getVirtualIdentificationVariableFinder() { |
| if (virtualIdentificationVariableFinder == null) { |
| virtualIdentificationVariableFinder = new BaseDeclarationIdentificationVariableFinder(); |
| } |
| return virtualIdentificationVariableFinder; |
| } |
| |
| @Override |
| protected void initialize() { |
| super.initialize(); |
| usedIdentificationVariables = new ArrayList<>(); |
| registerIdentificationVariable = true; |
| } |
| |
| /** |
| * Determines whether the given identification variable is used in a comparison expression: |
| * "expression = LargeProject". |
| * |
| * @param expression The {@link IdentificationVariable} used to determine its purpose |
| * @return <code>true</code> if the identification variable is used in a comparison expression; |
| * <code>false</code> otherwise |
| */ |
| protected boolean isComparingEntityTypeLiteral(IdentificationVariable expression) { |
| ComparingEntityTypeLiteralVisitor visitor = getComparingEntityTypeLiteralVisitor(); |
| try { |
| visitor.expression = expression; |
| expression.accept(visitor); |
| return visitor.result; |
| } |
| finally { |
| visitor.result = false; |
| visitor.expression = null; |
| } |
| } |
| |
| protected boolean isIdentificationVariableDeclaredAfter(String variableName, |
| int variableNameIndex, |
| int joinIndex, |
| List<? extends JPQLQueryDeclaration> declarations) { |
| |
| for (int index = variableNameIndex, declarationCount = declarations.size(); index < declarationCount; index++) { |
| |
| JPQLQueryDeclaration declaration = declarations.get(index); |
| |
| // Ignore the current declaration since it's the same identification variable |
| if (index != variableNameIndex) { |
| |
| // Check to make sure the Declaration is a known type |
| if (declaration.getType() != Type.UNKNOWN) { |
| String nextVariableName = declaration.getVariableName(); |
| |
| if (variableName.equalsIgnoreCase(nextVariableName)) { |
| return true; |
| } |
| } |
| // Some implementation could have a Declaration that is not a range, derived or a |
| // collection Declaration, it could represent a JOIN expression |
| else { |
| return false; |
| } |
| } |
| |
| // Scan the JOIN expressions |
| if (declaration.hasJoins()) { |
| |
| List<Join> joins = declaration.getJoins(); |
| int endIndex = (index == variableNameIndex) ? joinIndex : joins.size(); |
| |
| for (int subIndex = joinIndex; subIndex < endIndex; subIndex++) { |
| |
| Join join = joins.get(subIndex); |
| |
| String joinVariableName = literal( |
| join.getIdentificationVariable(), |
| LiteralType.IDENTIFICATION_VARIABLE |
| ); |
| |
| if (variableName.equalsIgnoreCase(joinVariableName)) { |
| return true; |
| } |
| } |
| } |
| } |
| |
| // The identification variable was not found after the declaration being validated, that means |
| // it was defined before or it was not defined (which is validated by another rule) |
| return false; |
| } |
| |
| /** |
| * Determines whether an identification variable can be used in a comparison expression when the |
| * operator is either {@literal '<', '<=', '>', '>='}. |
| * |
| * @param expression The {@link IdentificationVariable} that is mapped to either an entity, a |
| * singled-object value field, a collection-valued object field |
| * @return <code>true</code> if it can be used in a ordering comparison expression; <code>false</code> |
| * if it can't |
| */ |
| protected boolean isIdentificationVariableValidInComparison(IdentificationVariable expression) { |
| return helper.isIdentificationVariableValidInComparison(expression); |
| } |
| |
| /** |
| * Determines whether the given {@link ComparisonExpression} compares two expression using one of |
| * the following operators: {@literal '<', '<=', '>', '>='}. |
| * |
| * @param expression The {@link ComparisonExpression} to check what type of operator that is used |
| * @return <code>true</code> if the operator is used to check for order; <code>false</code> if it |
| * is not |
| */ |
| protected boolean isOrderComparison(ComparisonExpression expression) { |
| |
| String operator = expression.getComparisonOperator(); |
| |
| return operator == Expression.GREATER_THAN || |
| operator == Expression.GREATER_THAN_OR_EQUAL || |
| operator == Expression.LOWER_THAN || |
| operator == Expression.LOWER_THAN_OR_EQUAL; |
| } |
| |
| /** |
| * Determines whether the expression at the given index is valid or not. |
| * |
| * @param result The integer value containing the bit used to determine the state of an expression |
| * at a given position |
| * @param index The index that is used to determine the state of the expression |
| * @return <code>true</code> if the expression is valid at the given index |
| */ |
| protected final boolean isValid(int result, int index) { |
| return (result & (1 << index)) == 0; |
| } |
| |
| /** |
| * Returns the type of path expression that is allowed in the <code><b>SELECT</b></code> clause. |
| * |
| * @return The type of path expressions allowed. The spec defines it as basic or object mapping |
| * only, i.e. collection-valued path expression is not allowed |
| */ |
| protected abstract PathType selectClausePathExpressionPathType(); |
| |
| protected FirstDeclarationVisitor subqueryFirstDeclarationVisitor() { |
| if (subqueryFirstDeclarationVisitor == null) { |
| subqueryFirstDeclarationVisitor = buildSubqueryFirstDeclarationVisitor(); |
| } |
| return subqueryFirstDeclarationVisitor; |
| } |
| |
| protected FirstDeclarationVisitor topLevelFirstDeclarationVisitor() { |
| if (topLevelFirstDeclarationVisitor == null) { |
| topLevelFirstDeclarationVisitor = buildTopLevelFirstDeclarationVisitor(); |
| } |
| return topLevelFirstDeclarationVisitor; |
| } |
| |
| /** |
| * Updates the validation status of an expression at a specified position. The value is stored |
| * in an integer value. |
| * |
| * @param result The integer value that is used to store the validation status of an expression |
| * at the given position |
| * @param index The position to store the validation status |
| * @param valid The new validation status to store |
| * @return The updated integer value |
| */ |
| protected final int updateStatus(int result, int index, boolean valid) { |
| return valid ? (result & (0 << index)) : (result | (1 << index)); |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>ABS</b></code> expression. The |
| * test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link AbsExpression} to validate by validating its encapsulated |
| * expression |
| * @return <code>false</code> if the encapsulated expression was validated and is invalid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateAbsExpression(AbsExpression expression) { |
| return validateFunctionPathExpression(expression); |
| } |
| |
| /** |
| * Validates the given <code><b>FROM</b></code> clause. This will validate the order of |
| * identification variable declarations. |
| * |
| * @param expression The {@link AbstractFromClause} to validate |
| */ |
| protected void validateAbstractFromClause(AbstractFromClause expression, |
| FirstDeclarationVisitor visitor) { |
| |
| // The identification variable declarations are evaluated from left to right in |
| // the FROM clause, and an identification variable declaration can use the result |
| // of a preceding identification variable declaration of the query string |
| List<? extends JPQLQueryDeclaration> declarations = helper.getDeclarations(); |
| |
| for (int index = 0, count = declarations.size(); index < count; index++) { |
| |
| JPQLQueryDeclaration declaration = declarations.get(index); |
| |
| // Make sure the first declaration is valid |
| if (index == 0) { |
| validateFirstDeclaration(expression, declaration, visitor); |
| } |
| else { |
| Expression declarationExpression = declaration.getDeclarationExpression(); |
| |
| // A JOIN expression does not have a declaration and it should not be traversed here |
| if (declarationExpression != null) { |
| declarationExpression.accept(this); |
| } |
| } |
| |
| // Check the JOIN expressions in the identification variable declaration |
| if (declaration.hasJoins()) { |
| validateJoinsIdentificationVariable(expression, declarations, declaration, index); |
| } |
| // Check the collection member declaration or derived path expression |
| else { |
| |
| Type type = declaration.getType(); |
| |
| if (type == Type.DERIVED || |
| type == Type.COLLECTION) { |
| |
| // Retrieve the identification variable from the path expression |
| String variableName = literal( |
| declaration.getBaseExpression(), |
| LiteralType.PATH_EXPRESSION_IDENTIFICATION_VARIABLE |
| ); |
| |
| if (ExpressionTools.stringIsNotEmpty(variableName) && |
| isIdentificationVariableDeclaredAfter(variableName, index, -1, declarations)) { |
| |
| int startPosition = position(declaration.getDeclarationExpression()) - variableName.length(); |
| int endPosition = startPosition + variableName.length(); |
| |
| addProblem( |
| expression, |
| startPosition, |
| endPosition, |
| AbstractFromClause_WrongOrderOfIdentificationVariableDeclaration, |
| variableName |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Validates the given {@link AbstractSchemaName}. The tests to perform are: |
| * <ul> |
| * <li>Check to see the actual entity associated with the entity name does exist.</li> |
| * <li>If the abstract schema name is actually a path expression (which can be defined in a |
| * subquery but is always parsed as an abstract schema name), then make sure the path |
| * expression is resolving to a relationship mapping.</li> |
| * </ul> |
| * |
| * @param expression The {@link AbstractSchemaName} to validate |
| * @return <code>true</code> if the entity name was resolved; <code>false</code> otherwise |
| */ |
| protected boolean validateAbstractSchemaName(AbstractSchemaName expression) { |
| |
| String abstractSchemaName = expression.getText(); |
| Object managedType = helper.getEntityNamed(abstractSchemaName); |
| boolean valid = true; |
| |
| if (managedType == null) { |
| |
| // If a subquery is defined in a WHERE clause of an update query, |
| // then check for a path expression |
| if (isWithinSubquery(expression)) { |
| |
| // Find the identification variable from the UPDATE range declaration |
| IdentificationVariable identificationVariable = findVirtualIdentificationVariable(expression); |
| String variableName = (identificationVariable != null) ? identificationVariable.getText() : null; |
| |
| if (ExpressionTools.stringIsNotEmpty(variableName)) { |
| |
| Object mapping = helper.resolveMapping(variableName, abstractSchemaName); |
| Object type = helper.getMappingType(mapping); |
| |
| // Does not resolve to a valid path |
| if (!helper.isTypeResolvable(type)) { |
| addProblem(expression, StateFieldPathExpression_NotResolvable, abstractSchemaName); |
| valid = false; |
| } |
| // Not a relationship mapping |
| else if (!helper.isRelationshipMapping(mapping)) { |
| addProblem(expression, PathExpression_NotRelationshipMapping, abstractSchemaName); |
| valid = false; |
| } |
| } |
| else { |
| addProblem(expression, AbstractSchemaName_Invalid, abstractSchemaName); |
| valid = false; |
| } |
| } |
| // The managed type does not exist |
| else { |
| addProblem(expression, AbstractSchemaName_Invalid, abstractSchemaName); |
| valid = false; |
| } |
| } |
| |
| return valid; |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given addition expression. The test to perform is: |
| * <ul> |
| * <li>If the left or right expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the left or right expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be updated.</li> |
| * </ul> |
| * |
| * @param expression The {@link AdditionExpression} to validate by validating its encapsulated |
| * expression |
| * @return A number indicating the validation result. {@link #isValid(int, int)} can be used to |
| * determine the validation status of an expression based on its position |
| */ |
| protected int validateAdditionExpression(AdditionExpression expression) { |
| return validateArithmeticExpression( |
| expression, |
| AdditionExpression_LeftExpression_WrongType, |
| AdditionExpression_RightExpression_WrongType |
| ); |
| } |
| |
| /** |
| * Validates the given {@link AllOrAnyExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link AllOrAnyExpression} to validate |
| */ |
| protected void validateAllOrAnyExpression(AllOrAnyExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link AndExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link AndExpression} to validate |
| */ |
| protected void validateAndExpression(AndExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the type of the left and right expressions defined by the given {@link ArithmeticExpression}. |
| * The test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link ArithmeticExpression} to validate |
| * @param leftExpressionWrongTypeMessageKey The key used to describe the left expression does not |
| * have a valid type |
| * @param rightExpressionWrongTypeMessageKey The key used to describe the right expression does |
| * not have a valid type |
| * @return A number indicating the validation result. {@link #isValid(int, int)} can be used to |
| * determine the validation status of an expression based on its position |
| */ |
| protected int validateArithmeticExpression(ArithmeticExpression expression, |
| String leftExpressionWrongTypeMessageKey, |
| String rightExpressionWrongTypeMessageKey) { |
| |
| return validateFunctionPathExpression(expression, PathType.BASIC_FIELD_ONLY); |
| } |
| |
| /** |
| * Validates the arithmetic factor expression. The test to perform is: |
| * <ul> |
| * <li>If the arithmetic factor is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * </ul> |
| * |
| * @param expression The {@link ArithmeticFactor} to validate |
| * @return <code>false</code> if the arithmetic factor expression was validated and is invalid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateArithmeticExpression(ArithmeticFactor expression) { |
| |
| boolean valid = true; |
| |
| if (expression.hasExpression()) { |
| Expression factor = expression.getExpression(); |
| |
| // Special case for state field path expression, association field is not allowed |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(factor); |
| |
| if (pathExpression != null) { |
| valid = validateStateFieldPathExpression(pathExpression, PathType.BASIC_FIELD_ONLY); |
| } |
| else { |
| factor.accept(this); |
| } |
| } |
| |
| return valid; |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>AVG</b></code> expression. The |
| * test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link AvgFunction} to validate by validating its encapsulated expression |
| * @return <code>false</code> if the encapsulated expression was validated and is invalid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateAvgFunction(AvgFunction expression) { |
| return validateFunctionPathExpression(expression); |
| } |
| |
| /** |
| * Validates the given {@link BetweenExpression}. The test to perform is: |
| * <ul> |
| * <li>If the "first" expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * </ul> |
| * |
| * @param expression The {@link BetweenExpression} to validate |
| */ |
| protected int validateBetweenExpression(BetweenExpression expression) { |
| |
| int result = 0; |
| |
| // Validate the "first" expression |
| if (expression.hasExpression()) { |
| Expression firstExpression = expression.getExpression(); |
| |
| // Special case for state field path expression, association field is not allowed |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(firstExpression); |
| |
| if (pathExpression != null) { |
| boolean valid = validateStateFieldPathExpression(pathExpression, PathType.BASIC_FIELD_ONLY); |
| updateStatus(result, 0, valid); |
| } |
| else { |
| firstExpression.accept(this); |
| } |
| } |
| |
| // Validate the lower bound expression |
| expression.getLowerBoundExpression().accept(this); |
| |
| // Validate the upper bound expression |
| expression.getUpperBoundExpression().accept(this); |
| |
| return result; |
| } |
| |
| /** |
| * Validates the given {@link CaseExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link CaseExpression} to validate |
| */ |
| protected void validateCaseExpression(CaseExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link CoalesceExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link CoalesceExpression} to validate |
| */ |
| protected void validateCoalesceExpression(CoalesceExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link CollectionMemberDeclaration}. |
| * |
| * @param expression The {@link CollectionMemberDeclaration} to validate |
| */ |
| protected void validateCollectionMemberDeclaration(CollectionMemberDeclaration expression) { |
| |
| validateCollectionValuedPathExpression(expression.getCollectionValuedPathExpression(), true); |
| |
| try { |
| registerIdentificationVariable = false; |
| expression.getIdentificationVariable().accept(this); |
| } |
| finally { |
| registerIdentificationVariable = true; |
| } |
| } |
| |
| /** |
| * Validates the given {@link CollectionMemberExpression}. Only the collection-valued path |
| * expression is validated. |
| * |
| * @param expression The {@link CollectionMemberExpression} to validate |
| * @return A number indicating the validation result. {@link #isValid(int, int)} can be used to |
| * determine the validation status of an expression based on its position |
| */ |
| protected int validateCollectionMemberExpression(CollectionMemberExpression expression) { |
| |
| int result = 0; |
| |
| // Validate the entity expression |
| if (expression.hasEntityExpression()) { |
| Expression entityExpression = expression.getEntityExpression(); |
| |
| // Special case for state field path expression, association field is allowed |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(entityExpression); |
| |
| if (pathExpression != null) { |
| boolean valid = validateStateFieldPathExpression(pathExpression, PathType.ASSOCIATION_FIELD_ONLY); |
| updateStatus(result, 0, valid); |
| } |
| else { |
| entityExpression.accept(this); |
| } |
| } |
| |
| // Validate the collection-valued path expression |
| boolean valid = validateCollectionValuedPathExpression(expression.getCollectionValuedPathExpression(), true); |
| updateStatus(result, 1, valid); |
| |
| return result; |
| } |
| |
| /** |
| * Validates the given {@link Expression} and makes sure it's a valid collection value path expression. |
| * |
| * @param expression The {@link Expression} to validate |
| * @param collectionTypeOnly <code>true</code> to make sure the path expression resolves to a |
| * collection mapping only; <code>false</code> if it can simply resolves to a relationship mapping |
| */ |
| protected boolean validateCollectionValuedPathExpression(Expression expression, |
| boolean collectionTypeOnly) { |
| |
| boolean valid = true; |
| |
| // The path expression resolves to a collection-valued path expression |
| CollectionValuedPathExpression collectionValuedPathExpression = getCollectionValuedPathExpression(expression); |
| |
| if (collectionValuedPathExpression != null && |
| collectionValuedPathExpression.hasIdentificationVariable() && |
| !collectionValuedPathExpression.endsWithDot()) { |
| |
| // A collection_valued_field is designated by the name of an association field in a |
| // one-to-many or a many-to-many relationship or by the name of an element collection field |
| Object mapping = helper.resolveMapping(expression); |
| Object type = helper.getMappingType(mapping); |
| |
| // Does not resolve to a valid path |
| if (!helper.isTypeResolvable(type) || (mapping == null)) { |
| |
| int startPosition = position(expression); |
| int endPosition = startPosition + length(expression); |
| |
| addProblem( |
| expression, |
| startPosition, |
| endPosition, |
| CollectionValuedPathExpression_NotResolvable, |
| expression.toParsedText() |
| ); |
| |
| valid = false; |
| } |
| else if (collectionTypeOnly && !helper.isCollectionMapping(mapping) || |
| !collectionTypeOnly && !helper.isRelationshipMapping(mapping)) { |
| |
| int startPosition = position(expression); |
| int endPosition = startPosition + length(expression); |
| |
| addProblem( |
| expression, |
| startPosition, |
| endPosition, |
| CollectionValuedPathExpression_NotCollectionType, |
| expression.toParsedText() |
| ); |
| |
| valid = false; |
| } |
| } |
| |
| return valid; |
| } |
| |
| /** |
| * Validates the given {@link Expression} and makes sure it's a valid collection value path expression. |
| * |
| * join_collection_valued_path_expression::= |
| * identification_variable.{single_valued_embeddable_object_field.}*collection_valued_field |
| * join_single_valued_path_expression::= |
| * identification_variable.{single_valued_embeddable_object_field.}*single_valued_object_field |
| * |
| * @param expression The {@link Expression} to validate |
| * @param collectionTypeOnly <code>true</code> to make sure the path expression resolves to a |
| * collection mapping only; <code>false</code> if it can simply resolves to a relationship mapping |
| */ |
| protected boolean validateJoinCollectionValuedPathExpression(Expression expression, |
| boolean collectionTypeOnly) { |
| |
| boolean valid = true; |
| |
| // The path expression resolves to a collection-valued path expression |
| CollectionValuedPathExpression collectionValuedPathExpression = getCollectionValuedPathExpression(expression); |
| |
| if (collectionValuedPathExpression != null && |
| collectionValuedPathExpression.hasIdentificationVariable() && |
| !collectionValuedPathExpression.endsWithDot()) { |
| |
| // A collection_valued_field is designated by the name of an association field in a |
| // one-to-many or a many-to-many relationship or by the name of an element collection field |
| |
| // A single_valued_object_field is designated by the name of an association field in a one-to-one or |
| // many-to-one relationship or a field of embeddable class type |
| Object mapping = helper.resolveMapping(expression); |
| Object type = helper.getMappingType(mapping); |
| |
| // Does not resolve to a valid path |
| if (!helper.isTypeResolvable(type) || (mapping == null)) { |
| |
| int startPosition = position(expression); |
| int endPosition = startPosition + length(expression); |
| |
| addProblem( |
| expression, |
| startPosition, |
| endPosition, |
| CollectionValuedPathExpression_NotResolvable, |
| expression.toParsedText() |
| ); |
| |
| valid = false; |
| } |
| else if (!helper.isCollectionMapping(mapping) && |
| !helper.isRelationshipMapping(mapping) && |
| !helper.isEmbeddableMapping(mapping)) { |
| |
| int startPosition = position(expression); |
| int endPosition = startPosition + length(expression); |
| |
| addProblem( |
| expression, |
| startPosition, |
| endPosition, |
| CollectionValuedPathExpression_NotCollectionType, |
| expression.toParsedText() |
| ); |
| |
| valid = false; |
| } |
| } |
| |
| return valid; |
| } |
| |
| /** |
| * Validates the left and right expressions of the given {@link ComparisonExpression}. The tests |
| * to perform are: |
| * <ul> |
| * <li>If the comparison operator is either '=' or {@literal '<>'}. The expressions can only be |
| * <ul> |
| * <li>Two identification variables;</li> |
| * <li>Two path expressions resolving to an association field;</li> |
| * <li>One can be a path expression resolving to a basic field and the other one has to |
| * resolve to a basic value.</li> |
| * </ul> |
| * </li> |
| * <li>If the comparison operator is either {@literal '<', '<=', '>=', '>'}. The expressions cannot be |
| * <ul> |
| * <li>Two identification variables;</li> |
| * <li>Two path expressions resolving to an association field;</li> |
| * </ul> |
| * </li> |
| * </ul> |
| * |
| * @param expression The {@link ConcatExpression} to validate by validating its |
| * left and right expressions |
| * @return The status of the comparison between the left and right expression: <code>true</code> |
| * if the two expressions pass the rules defined by this method; <code>false</code> otherwise |
| */ |
| protected boolean validateComparisonExpression(ComparisonExpression expression) { |
| |
| Expression leftExpression = expression.getLeftExpression(); |
| Expression rightExpression = expression.getRightExpression(); |
| boolean valid = true; |
| |
| // First determine what is being compared and validate them as well |
| ComparisonExpressionVisitor validator = getComparisonExpressionVisitor(); |
| |
| try { |
| // Visit the left expression and gather its information |
| validator.validatingLeftExpression = true; |
| leftExpression.accept(validator); |
| |
| // Visit the right expression and gather its information |
| validator.validatingLeftExpression = false; |
| rightExpression.accept(validator); |
| |
| // '<', '<=', '>=', '>' |
| if (isOrderComparison(expression)) { |
| |
| // The left expression cannot be an identification variable |
| if (validator.leftIdentificationVariable && |
| validator.leftIdentificationVariableValid) { |
| |
| IdentificationVariable variable = (IdentificationVariable) leftExpression; |
| |
| // There is a specific EclipseLink case where it is valid to use an |
| // identification variable, which is when the identification variable |
| // maps to a direct collection mapping |
| if (!isIdentificationVariableValidInComparison(variable)) { |
| |
| addProblem( |
| leftExpression, |
| ComparisonExpression_IdentificationVariable, |
| leftExpression.toActualText(), |
| expression.getComparisonOperator() |
| ); |
| |
| valid = false; |
| } |
| } |
| // The left expression is a path expression |
| else if (validator.leftStateFieldPathExpression && |
| validator.leftStateFieldPathExpressionValid) { |
| |
| Object mapping = helper.resolveMapping(leftExpression); |
| |
| // The path expression cannot be a non-basic mapping |
| if ((mapping != null) && !helper.isPropertyMapping(mapping)) { |
| |
| addProblem( |
| leftExpression, |
| ComparisonExpression_AssociationField, |
| leftExpression.toActualText(), |
| expression.getComparisonOperator() |
| ); |
| |
| valid = false; |
| } |
| } |
| |
| // The right expression cannot be an identification variable |
| if (validator.rightIdentificationVariable && |
| validator.rightIdentificationVariableValid) { |
| |
| IdentificationVariable variable = (IdentificationVariable) rightExpression; |
| |
| // There is a specific EclipseLink case where it is valid to use an |
| // identification variable, which is when the identification variable |
| // maps to a direct collection mapping |
| if (!isIdentificationVariableValidInComparison(variable)) { |
| |
| addProblem( |
| rightExpression, |
| ComparisonExpression_IdentificationVariable, |
| rightExpression.toActualText(), |
| expression.getComparisonOperator() |
| ); |
| |
| valid = false; |
| } |
| } |
| // The right expression is a path expression |
| else if (validator.rightStateFieldPathExpression && |
| validator.rightStateFieldPathExpressionValid) { |
| |
| Object mapping = helper.resolveMapping(rightExpression); |
| |
| // The path expression cannot be a non-basic mapping |
| if ((mapping != null) && !helper.isPropertyMapping(mapping)) { |
| |
| addProblem( |
| rightExpression, |
| ComparisonExpression_AssociationField, |
| rightExpression.toActualText(), |
| expression.getComparisonOperator() |
| ); |
| |
| valid = false; |
| } |
| } |
| } |
| // '=', '<>' |
| else { |
| |
| // The left expression is an identification variable |
| // The right expression is a path expression |
| if (validator.leftIdentificationVariable && |
| validator.leftIdentificationVariableValid && |
| validator.rightStateFieldPathExpression && |
| validator.rightStateFieldPathExpressionValid) { |
| |
| Object mapping = helper.resolveMapping(rightExpression); |
| IdentificationVariable variable = (IdentificationVariable) leftExpression; |
| |
| // The path expression can only be a non-basic mapping. |
| // There is a specific EclipseLink case where it is valid to use an |
| // identification variable, which is when the identification variable |
| // maps to a direct collection mapping |
| if ((mapping != null) && helper.isPropertyMapping(mapping) && |
| !isIdentificationVariableValidInComparison(variable)) { |
| |
| addProblem( |
| rightExpression, |
| ComparisonExpression_BasicField, |
| rightExpression.toActualText(), |
| expression.getComparisonOperator() |
| ); |
| |
| valid = false; |
| } |
| } |
| // The left expression is a path expression |
| // The right expression is an identification variable |
| else if (validator.rightIdentificationVariable && |
| validator.rightIdentificationVariableValid && |
| validator.leftStateFieldPathExpression && |
| validator.leftStateFieldPathExpressionValid) { |
| |
| Object mapping = helper.resolveMapping(leftExpression); |
| IdentificationVariable variable = (IdentificationVariable) rightExpression; |
| |
| // The path expression can only be a non-basic mapping. |
| // There is a specific EclipseLink case where it is valid to use an |
| // identification variable, which is when the identification variable |
| // maps to a direct collection mapping |
| if ((mapping != null) && helper.isPropertyMapping(mapping) && |
| !isIdentificationVariableValidInComparison(variable)) { |
| |
| addProblem( |
| leftExpression, |
| ComparisonExpression_BasicField, |
| leftExpression.toActualText(), |
| expression.getComparisonOperator() |
| ); |
| |
| valid = false; |
| } |
| } |
| } |
| |
| return valid; |
| } |
| finally { |
| validator.dispose(); |
| } |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>CONCAT</b></code> expression. |
| * The tests to perform are: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a |
| * basic mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected |
| * to that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link ConcatExpression} to validate by validating its encapsulated expression |
| * @return <code>false</code> if the first encapsulated expression was validated and is invalid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateConcatExpression(ConcatExpression expression) { |
| return validateFunctionPathExpression(expression); |
| } |
| |
| /** |
| * Validates the given {@link ConstructorExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link ConstructorExpression} to validate |
| */ |
| protected void validateConstructorExpression(ConstructorExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link CountFunction}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link CountFunction} to validate |
| */ |
| protected void validateCountFunction(CountFunction expression) { |
| |
| if (expression.hasExpression()) { |
| |
| Expression leftExpression = expression.getExpression(); |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(leftExpression); |
| |
| if (pathExpression != null) { |
| validateStateFieldPathExpression(pathExpression, validPathExpressionTypeForCountFunction()); |
| } |
| else { |
| leftExpression.accept(this); |
| } |
| } |
| } |
| |
| /** |
| * Validates the given {@link DateTime}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link DateTime} to validate |
| */ |
| protected void validateDateTime(DateTime expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link DeleteClause}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link DeleteClause} to validate |
| */ |
| protected void validateDeleteClause(DeleteClause expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link DeleteStatement}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link DeleteStatement} to validate |
| */ |
| protected void validateDeleteStatement(DeleteStatement expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given division expression. The test to perform is: |
| * <ul> |
| * <li>If the left or right expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the left or right expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be updated.</li> |
| * </ul> |
| * |
| * @param expression The {@link DivisionExpression} to validate by validating its encapsulated |
| * expression |
| * @return A number indicating the validation result. {@link #isValid(int, int)} can be used to |
| * determine the validation status of an expression based on its position |
| */ |
| protected int validateDivisionExpression(DivisionExpression expression) { |
| return validateArithmeticExpression( |
| expression, |
| DivisionExpression_LeftExpression_WrongType, |
| DivisionExpression_RightExpression_WrongType |
| ); |
| } |
| |
| protected boolean validateEntityTypeLiteral(EntityTypeLiteral expression) { |
| |
| String entityTypeName = expression.getEntityTypeName(); |
| boolean valid = true; |
| |
| if (ExpressionTools.stringIsNotEmpty(entityTypeName)) { |
| Object entity = helper.getEntityNamed(entityTypeName); |
| |
| if (entity == null) { |
| int startIndex = position(expression); |
| int endIndex = startIndex + entityTypeName.length(); |
| addProblem(expression, startIndex, endIndex, EntityTypeLiteral_NotResolvable, entityTypeName); |
| valid = false; |
| } |
| } |
| |
| return valid; |
| } |
| |
| /** |
| * Validates the given {@link EntryExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link EntryExpression} to validate |
| */ |
| protected void validateEntryExpression(EntryExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link ExistsExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link ExistsExpression} to validate |
| */ |
| protected void validateExistsExpression(ExistsExpression expression) { |
| super.visit(expression); |
| } |
| |
| protected void validateFirstDeclaration(AbstractFromClause expression, |
| JPQLQueryDeclaration declaration, |
| FirstDeclarationVisitor visitor) { |
| |
| Expression declarationExpression = declaration.getDeclarationExpression(); |
| Expression baseExpression = declaration.getBaseExpression(); |
| |
| try { |
| baseExpression.accept(visitor); |
| |
| if (!visitor.valid) { |
| |
| int startPosition = position(declarationExpression); |
| int endPosition = startPosition + length(declarationExpression); |
| |
| addProblem( |
| expression, |
| startPosition, |
| endPosition, |
| AbstractFromClause_InvalidFirstIdentificationVariableDeclaration, |
| baseExpression.toActualText() |
| ); |
| } |
| else { |
| declarationExpression.accept(this); |
| } |
| } |
| finally { |
| visitor.valid = false; |
| } |
| } |
| |
| /** |
| * Validates the given {@link FromClause}. |
| * |
| * @param expression The {@link FromClause} to validate |
| */ |
| protected void validateFromClause(FromClause expression) { |
| validateAbstractFromClause(expression, topLevelFirstDeclarationVisitor()); |
| } |
| |
| /** |
| * Validates the given {@link FunctionExpression}. |
| * |
| * @param expression The {@link FunctionExpression} to validate |
| */ |
| protected void validateFunctionExpression(FunctionExpression expression) { |
| } |
| |
| /** |
| * Validates the given {@link AbstractSingleEncapsulatedExpression}'s encapsulated expression if |
| * it is a state field path expression and makes sure it is mapping to a basic mapping. That |
| * means relationship field mapping is not allowed. |
| * |
| * @param expression The {@link AbstractSingleEncapsulatedExpression} to validate its encapsulated |
| * expression if it's a state field path expression, otherwise does nothing |
| * @return <code>false</code> if the encapsulated expression was validated and is invalid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateFunctionPathExpression(AbstractSingleEncapsulatedExpression expression) { |
| |
| boolean valid = true; |
| |
| if (expression.hasEncapsulatedExpression()) { |
| Expression encapsulatedExpression = expression.getExpression(); |
| |
| // Special case for state field path expression, association field is not allowed |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(encapsulatedExpression); |
| |
| if (pathExpression != null) { |
| valid = validateStateFieldPathExpression(pathExpression, PathType.BASIC_FIELD_ONLY); |
| } |
| else { |
| encapsulatedExpression.accept(this); |
| } |
| } |
| |
| return valid; |
| } |
| |
| /** |
| * Validates the left and right expressions of the given compound expression. The test to perform is: |
| * <ul> |
| * <li>If the left or the right expression is a path expression, validation makes sure it is a |
| * basic mapping, an association field is not allowed.</li> |
| * </ul> |
| * |
| * @param expression The {@link CompoundExpression} to validate by validating its left and right |
| * expressions |
| * @param pathType The type of field that is allowed |
| * @return A number indicating the validation result. {@link #isValid(int, int)} can be used to |
| * determine the validation status of an expression based on its position |
| */ |
| protected int validateFunctionPathExpression(CompoundExpression expression, PathType pathType) { |
| |
| int result = 0; |
| |
| // Left expression |
| if (expression.hasLeftExpression()) { |
| Expression leftExpression = expression.getLeftExpression(); |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(leftExpression); |
| if (pathExpression != null) { |
| boolean valid = validateStateFieldPathExpression(pathExpression, pathType); |
| updateStatus(result, 0, valid); |
| } |
| else { |
| leftExpression.accept(this); |
| } |
| } |
| |
| // Right expression |
| if (expression.hasRightExpression()) { |
| Expression rightExpression = expression.getRightExpression(); |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(rightExpression); |
| if (pathExpression != null) { |
| boolean valid = validateStateFieldPathExpression(pathExpression, pathType); |
| updateStatus(result, 1, valid); |
| } |
| else { |
| rightExpression.accept(this); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Validates the given {@link GroupByClause}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link GroupByClause} to validate |
| */ |
| protected void validateGroupByClause(GroupByClause expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link HavingClause}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link HavingClause} to validate |
| */ |
| protected void validateHavingClause(HavingClause expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link IdentificationVariable}. The test to perform are: |
| * <ul> |
| * <li>If the identification variable resolves to an entity type literal, then no validation |
| * is performed.</li> |
| * <li></li> |
| * </ul> |
| * |
| * @param expression The identification variable to be validated |
| * @return <code>true</code> if the given identification variable is valid; <code>false</code> |
| * otherwise |
| */ |
| protected boolean validateIdentificationVariable(IdentificationVariable expression) { |
| |
| boolean valid = true; |
| |
| // Only a non-virtual identification variable is validated |
| if (!expression.isVirtual()) { |
| |
| String variable = expression.getText(); |
| boolean continueValidating = true; |
| |
| // A entity literal type is parsed as an identification variable, check for that case |
| if (isComparingEntityTypeLiteral(expression)) { |
| |
| // The identification variable (or entity type literal) does not |
| // correspond to an entity name, then continue validation |
| Object entity = helper.getEntityNamed(variable); |
| continueValidating = (entity == null); |
| } |
| |
| // Validate a real identification variable |
| if (continueValidating) { |
| |
| if (registerIdentificationVariable) { |
| usedIdentificationVariables.add(expression); |
| } |
| |
| valid = validateIdentificationVariable(expression, variable); |
| } |
| } |
| // The identification variable actually represents a state field path expression that has |
| // a virtual identification, validate that state field path expression instead |
| else { |
| StateFieldPathExpression pathExpression = expression.getStateFieldPathExpression(); |
| |
| if (pathExpression != null) { |
| pathExpression.accept(this); |
| } |
| } |
| |
| return valid; |
| } |
| |
| /** |
| * Validates the given identification variable. The default behavior is to not validate it. |
| * |
| * @param expression The {@link IdentificationVariable} that is being visited |
| * @param variable The actual identification variable, which is never an empty string |
| * @return <code>true</code> if the given identification variable is valid; <code>false</code> |
| * otherwise |
| */ |
| protected boolean validateIdentificationVariable(IdentificationVariable expression, String variable) { |
| return true; |
| } |
| |
| /** |
| * Validates the given {@link InExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link InExpression} to validate |
| */ |
| protected void validateIdentificationVariableDeclaration(IdentificationVariableDeclaration expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the identification variables: |
| * <ul> |
| * <li>Assures those used throughout the query have been defined in the <code><b>FROM</b></code> |
| * clause in the current subquery or in a superquery.</li> |
| * <li>They have been defined only once.</li> |
| * </ul> |
| */ |
| protected void validateIdentificationVariables() { |
| |
| // Collect the identification variables from the Declarations |
| Map<String, List<IdentificationVariable>> identificationVariables = new HashMap<>(); |
| helper.collectLocalDeclarationIdentificationVariables(identificationVariables); |
| |
| // Check for duplicate identification variables |
| for (Map.Entry<String, List<IdentificationVariable>> entry : identificationVariables.entrySet()) { |
| |
| List<IdentificationVariable> variables = entry.getValue(); |
| |
| // More than one identification variable was used in a declaration |
| if (variables.size() > 1) { |
| for (IdentificationVariable variable : variables) { |
| addProblem(variable, IdentificationVariable_Invalid_Duplicate, variable.getText()); |
| } |
| } |
| } |
| |
| // Now collect the identification variables from the parent queries |
| identificationVariables.clear(); |
| helper.collectAllDeclarationIdentificationVariables(identificationVariables); |
| |
| // Check for undeclared identification variables |
| for (IdentificationVariable identificationVariable : usedIdentificationVariables) { |
| String variableName = identificationVariable.getText(); |
| |
| if (ExpressionTools.stringIsNotEmpty(variableName) && |
| !identificationVariables.containsKey(variableName.toUpperCase(Locale.ROOT))) { |
| |
| addProblem(identificationVariable, IdentificationVariable_Invalid_NotDeclared, variableName); |
| } |
| } |
| } |
| |
| /** |
| * Validates the given {@link IndexExpression}. It validates the identification variable and |
| * makes sure is it defined in <code><b>IN</b></code> or <code><b>IN</b></code> expression. |
| * |
| * @param expression The {@link IndexExpression} to validate |
| */ |
| protected boolean validateIndexExpression(IndexExpression expression) { |
| |
| boolean valid = true; |
| |
| // The INDEX function can only be applied to identification variables denoting types for |
| // which an order column has been specified |
| String variableName = literal( |
| expression.getExpression(), |
| LiteralType.IDENTIFICATION_VARIABLE |
| ); |
| |
| // The identification variable is not defined in a JOIN or IN expression |
| if (ExpressionTools.stringIsNotEmpty(variableName) && |
| !helper.isCollectionIdentificationVariable(variableName)) { |
| |
| addProblem(expression.getExpression(), IndexExpression_WrongVariable, variableName); |
| valid = false; |
| } |
| |
| return valid; |
| } |
| |
| /** |
| * Validates the given {@link InExpression}. The test to perform is: |
| * <ul> |
| * <li>If the expression is a path expression, validation makes sure it is an association mapping, |
| * a basic field is not allowed.</li> |
| * </ul> |
| * |
| * @param expression The {@link InExpression} to validate |
| */ |
| protected void validateInExpression(InExpression expression) { |
| |
| // Validate the left expression |
| if (expression.hasExpression()) { |
| Expression stringExpression = expression.getExpression(); |
| |
| // Special case for state field path expression |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(stringExpression); |
| |
| if (pathExpression != null) { |
| validateStateFieldPathExpression(pathExpression, validPathExpressionTypeForInExpression()); |
| } |
| else { |
| stringExpression.accept(this); |
| } |
| } |
| |
| // Validate the items |
| expression.getInItems().accept(getInItemsVisitor()); |
| } |
| |
| /** |
| * Validates the given {@link Join}. |
| * |
| * @param expression The {@link ValueExpression} to validate |
| */ |
| protected void validateJoin(Join expression) { |
| |
| if (expression.hasJoinAssociationPath()) { |
| Expression joinAssociationPath = expression.getJoinAssociationPath(); |
| validateJoinCollectionValuedPathExpression(joinAssociationPath, false); |
| joinAssociationPath.accept(this); |
| } |
| |
| if (expression.hasIdentificationVariable()) { |
| try { |
| registerIdentificationVariable = false; |
| expression.getIdentificationVariable().accept(this); |
| } |
| finally { |
| registerIdentificationVariable = true; |
| } |
| } |
| } |
| |
| protected void validateJoinsIdentificationVariable(AbstractFromClause expression, |
| List<? extends JPQLQueryDeclaration> declarations, |
| JPQLQueryDeclaration declaration, |
| int index) { |
| |
| List<Join> joins = declaration.getJoins(); |
| |
| for (int joinIndex = 0, joinCount = joins.size(); joinIndex < joinCount; joinIndex++) { |
| |
| Join join = joins.get(joinIndex); |
| |
| // Retrieve the identification variable from the join association path |
| String variableName = literal( |
| join.getJoinAssociationPath(), |
| LiteralType.PATH_EXPRESSION_IDENTIFICATION_VARIABLE |
| ); |
| |
| // Make sure the identification variable is defined before the JOIN expression |
| if (ExpressionTools.stringIsNotEmpty(variableName) && |
| isIdentificationVariableDeclaredAfter(variableName, index, joinIndex, declarations)) { |
| |
| int startPosition = position(join.getJoinAssociationPath()); |
| int endPosition = startPosition + variableName.length(); |
| |
| addProblem( |
| expression, |
| startPosition, |
| endPosition, |
| AbstractFromClause_WrongOrderOfIdentificationVariableDeclaration, |
| variableName |
| ); |
| } |
| } |
| } |
| |
| /** |
| * Validates the given {@link KeyExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link KeyExpression} to validate |
| */ |
| protected void validateKeyExpression(KeyExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>LENGTH</b></code> expression. The |
| * test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link LengthExpression} to validate by validating its encapsulated expression |
| * @return <code>false</code> if the encapsulated expression was validated and is invalid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateLengthExpression(LengthExpression expression) { |
| return validateFunctionPathExpression(expression); |
| } |
| |
| /** |
| * Validates the string expression of the given <code><b>LIKE</b></code> expression. The test to |
| * perform is: |
| * <ul> |
| * <li>If the string expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link LengthExpression} to validate by validating its string expression |
| * @return A number indicating the validation result. {@link #isValid(int, int)} can be used to |
| * determine the validation status of an expression based on its position |
| */ |
| protected int validateLikeExpression(LikeExpression expression) { |
| |
| int result = 0; |
| |
| // Validate the "first" expression |
| if (expression.hasStringExpression()) { |
| Expression stringExpression = expression.getStringExpression(); |
| |
| // Special case for state field path expression, association field is not allowed |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(stringExpression); |
| |
| if (pathExpression != null) { |
| boolean valid = validateStateFieldPathExpression(pathExpression, validPathExpressionTypeForStringExpression()); |
| updateStatus(result, 0, valid); |
| } |
| else { |
| stringExpression.accept(this); |
| } |
| } |
| |
| // Validate the pattern value |
| expression.getPatternValue().accept(this); |
| |
| // Validate the escape character |
| expression.getEscapeCharacter().accept(this); |
| |
| return result; |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>LOCATE</b></code> expression. The |
| * test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link LocateExpression} to validate by validating its encapsulated expression |
| * @return A number indicating the validation result. {@link #isValid(int, int)} can be used to |
| * determine the validation status of an expression based on its position |
| */ |
| protected int validateLocateExpression(LocateExpression expression) { |
| |
| int result = 0; |
| |
| // Validate the first expression |
| if (expression.hasFirstExpression()) { |
| Expression firstExpression = expression.getFirstExpression(); |
| |
| // Special case for state field path expression, association field is not allowed |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(firstExpression); |
| |
| if (pathExpression != null) { |
| boolean valid = validateStateFieldPathExpression(pathExpression, PathType.BASIC_FIELD_ONLY); |
| updateStatus(result, 0, valid); |
| } |
| else { |
| firstExpression.accept(this); |
| } |
| } |
| |
| // Validate the second expression |
| expression.getSecondExpression().accept(this); |
| |
| // Validate the third expression |
| expression.getThirdExpression().accept(this); |
| |
| return result; |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>LOWER</b></code> expression. The |
| * test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link LowerExpression} to validate by validating its encapsulated expression |
| * @return <code>false</code> if the encapsulated expression was validated and is invalid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateLowerExpression(LowerExpression expression) { |
| return validateFunctionPathExpression(expression); |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>MAX</b></code> expression. The |
| * test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link MaxFunction} to validate by validating its encapsulated expression |
| * @return <code>false</code> if the encapsulated expression was validated and is not valid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateMaxFunction(MaxFunction expression) { |
| // Arguments to the functions MAX must correspond to orderable state field types (i.e., |
| // numeric types, string types, character types, or date types) |
| return validateFunctionPathExpression(expression); |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>MIN</b></code> expression. The |
| * test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link MinFunction} to validate by validating its encapsulated expression |
| * @return <code>false</code> if the encapsulated expression was validated and is not valid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateMinFunction(MinFunction expression) { |
| // Arguments to the functions MIN must correspond to orderable state field types (i.e., |
| // numeric types, string types, character types, or date types) |
| return validateFunctionPathExpression(expression); |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>MOD</b></code> expression. The |
| * test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link ModExpression} to validate by validating its encapsulated expression |
| * @return A number indicating the validation result. {@link #isValid(int, int)} can be used to |
| * determine the validation status of an expression based on its position |
| */ |
| protected int validateModExpression(ModExpression expression) { |
| |
| int result = 0; |
| |
| // Validate the first expression |
| if (expression.hasFirstExpression()) { |
| Expression firstExpression = expression.getFirstExpression(); |
| |
| // Special case for state field path expression, association field is not allowed |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(firstExpression); |
| |
| if (pathExpression != null) { |
| boolean valid = validateStateFieldPathExpression(pathExpression, PathType.BASIC_FIELD_ONLY); |
| updateStatus(result, 0, valid); |
| } |
| else { |
| firstExpression.accept(this); |
| } |
| } |
| |
| // Validate the second expression |
| expression.getSecondExpression().accept(this); |
| |
| return result; |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given multiplication expression. The test to |
| * perform is: |
| * <ul> |
| * <li>If the left or right expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the left or right expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be updated.</li> |
| * </ul> |
| * |
| * @param expression The {@link MultiplicationExpression} to validate by validating its encapsulated |
| * expression |
| * @return A number indicating the validation result. {@link #isValid(int, int)} can be used to |
| * determine the validation status of an expression based on its position |
| */ |
| protected int validateMultiplicationExpression(MultiplicationExpression expression) { |
| return validateArithmeticExpression( |
| expression, |
| MultiplicationExpression_LeftExpression_WrongType, |
| MultiplicationExpression_RightExpression_WrongType |
| ); |
| } |
| |
| /** |
| * Validates the given {@link NotExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link NotExpression} to validate |
| */ |
| protected void validateNotExpression(NotExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link NullComparisonExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link NullComparisonExpression} to validate |
| */ |
| protected void validateNullComparisonExpression(NullComparisonExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link NullIfExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link NullIfExpression} to validate |
| */ |
| protected void validateNullIfExpression(NullIfExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link ObjectExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link ObjectExpression} to validate |
| */ |
| protected void validateObjectExpression(ObjectExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link OnClause}. The default behavior does not require to semantically |
| * validate it. |
| * |
| * @param expression The {@link OnClause} to validate |
| */ |
| protected void validateOnClause(OnClause expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link OrderByItem}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link OrderByItem} to validate |
| */ |
| protected void validateOrderByClause(OrderByClause expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link OrderByItem}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link OrderByItem} to validate |
| */ |
| protected void validateOrderByItem(OrderByItem expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link OrExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link OrExpression} to validate |
| */ |
| protected void validateOrExpression(OrExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link RangeVariableDeclaration}. |
| * |
| * @param expression The {@link RangeVariableDeclaration} to validate |
| */ |
| protected void validateRangeVariableDeclaration(RangeVariableDeclaration expression) { |
| |
| validateRangeVariableDeclarationRootObject(expression); |
| |
| try { |
| registerIdentificationVariable = false; |
| expression.getIdentificationVariable().accept(this); |
| } |
| finally { |
| registerIdentificationVariable = true; |
| } |
| } |
| |
| /** |
| * Validates the "root" object of the given {@link RangeVariableDeclaration}. |
| * |
| * @param expression The {@link RangeVariableDeclaration} that needs its "root" object |
| * to be validated |
| */ |
| protected void validateRangeVariableDeclarationRootObject(RangeVariableDeclaration expression) { |
| expression.getRootObject().accept(this); |
| } |
| |
| /** |
| * Validates the given {@link ResultVariable}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link ResultVariable} to validate |
| */ |
| protected void validateResultVariable(ResultVariable expression) { |
| // TODO: Validate identification to make sure it does not |
| // collide with one defined in the FROM clause |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link SelectClause}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link SelectClause} to validate |
| */ |
| protected void validateSelectClause(SelectClause expression) { |
| |
| Expression selectExpression = expression.getSelectExpression(); |
| |
| // Special case for state field path expression, all types are allowed |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(selectExpression); |
| |
| if (pathExpression != null) { |
| validateStateFieldPathExpression(pathExpression, selectClausePathExpressionPathType()); |
| } |
| else { |
| selectExpression.accept(this); |
| } |
| } |
| |
| /** |
| * Validates the given {@link SelectStatement}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link SelectStatement} to validate |
| */ |
| protected void validateSelectStatement(SelectStatement expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link SimpleFromClause}. |
| * |
| * @param expression The {@link SimpleFromClause} to validate |
| */ |
| protected void validateSimpleFromClause(SimpleFromClause expression) { |
| validateAbstractFromClause(expression, subqueryFirstDeclarationVisitor()); |
| } |
| |
| /** |
| * Validates the given {@link SimpleSelectClause}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link SimpleSelectClause} to validate |
| */ |
| protected void validateSimpleSelectClause(SimpleSelectClause expression) { |
| super.visit(expression); |
| } |
| |
| protected void validateSimpleSelectStatement(SimpleSelectStatement expression) { |
| |
| // Keep a copy of the identification variables that are used throughout the parent query |
| List<IdentificationVariable> oldUsedIdentificationVariables = new ArrayList<>(usedIdentificationVariables); |
| |
| // Create a context for the subquery |
| helper.newSubqueryContext(expression); |
| |
| try { |
| super.visit(expression); |
| |
| // Validate the identification variables that are used within the subquery |
| validateIdentificationVariables(); |
| } |
| finally { |
| // Revert back to the parent context |
| helper.disposeSubqueryContext(); |
| |
| // Revert the list to what it was |
| usedIdentificationVariables.retainAll(oldUsedIdentificationVariables); |
| } |
| } |
| |
| /** |
| * Validates the given {@link SizeExpression}. |
| * |
| * @param expression The {@link SizeExpression} to validate |
| * @return <code>false</code> if the encapsulated expression is a collection-valued path expression |
| * and it was found to be invalid; <code>true</code> otherwise |
| */ |
| protected boolean validateSizeExpression(SizeExpression expression) { |
| // The SIZE function returns an integer value, the number of elements of the collection |
| return validateCollectionValuedPathExpression(expression.getExpression(), true); |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>SQRT</b></code> expression. The |
| * test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link SqrtExpression} to validate by validating its encapsulated expression |
| * @return <code>false</code> if the encapsulated expression was validated and is invalid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateSqrtExpression(SqrtExpression expression) { |
| return validateFunctionPathExpression(expression); |
| } |
| |
| /** |
| * Validates the given {@link StateFieldPathExpression}. |
| * |
| * @param expression The {@link StateFieldPathExpression} the validate |
| * @param pathType The type of field that is allowed |
| * @return <code>true</code> if the given {@link StateFieldPathExpression} resolves to a valid |
| * path; <code>false</code> otherwise |
| */ |
| protected boolean validateStateFieldPathExpression(StateFieldPathExpression expression, |
| PathType pathType) { |
| |
| boolean valid = true; |
| |
| if (expression.hasIdentificationVariable()) { |
| expression.getIdentificationVariable().accept(this); |
| |
| // A single_valued_object_field is designated by the name of an association field in a |
| // one-to-one or many-to-one relationship or a field of embeddable class type |
| if (!expression.endsWithDot()) { |
| |
| Object mapping = helper.resolveMapping(expression); |
| |
| // Validate the mapping |
| if (mapping != null) { |
| |
| // It is syntactically illegal to compose a path expression from a path expression |
| // that evaluates to a collection |
| if (helper.isCollectionMapping(mapping)) { |
| if (pathType != PathType.ANY_FIELD_INCLUDING_COLLECTION) { |
| addProblem(expression, StateFieldPathExpression_CollectionType, expression.toActualText()); |
| valid = false; |
| } |
| } |
| // A transient mapping is not allowed |
| else if (helper.isTransient(mapping)) { |
| addProblem(expression, StateFieldPathExpression_NoMapping, expression.toParsedText()); |
| valid = false; |
| } |
| // Only a basic field is allowed |
| else if ((pathType == PathType.BASIC_FIELD_ONLY) && |
| !helper.isPropertyMapping(mapping)) { |
| |
| addProblem(expression, StateFieldPathExpression_AssociationField, expression.toActualText()); |
| valid = false; |
| } |
| // Only an association field is allowed |
| else if ((pathType == PathType.ASSOCIATION_FIELD_ONLY) && |
| helper.isPropertyMapping(mapping)) { |
| |
| addProblem(expression, StateFieldPathExpression_BasicField, expression.toActualText()); |
| valid = false; |
| } |
| } |
| // Attempts to resolve something that does not represent a mapping |
| else { |
| |
| Object type = helper.getType(expression.toParsedText(0, expression.pathSize() - 1)); |
| |
| // An enum constant could have been parsed as a state field path expression |
| if (helper.isEnumType(type)) { |
| |
| // Search for the enum constant |
| String enumConstant = expression.getPath(expression.pathSize() - 1); |
| boolean found = false; |
| |
| for (String constant : helper.getEnumConstants(type)) { |
| if (constant.equals(enumConstant)) { |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) { |
| int startIndex = position(expression) + helper.getTypeName(type).length() + 1; |
| int endIndex = startIndex + enumConstant.length(); |
| addProblem(expression, startIndex, endIndex, StateFieldPathExpression_InvalidEnumConstant, enumConstant); |
| valid = false; |
| } |
| |
| // Remove the used identification variable since it is the first |
| // package name of the fully qualified enum constant |
| usedIdentificationVariables.remove(expression.getIdentificationVariable()); |
| } |
| else { |
| |
| // Special case for EclipseLink, path expression formed with the identification variable |
| // mapped to a subquery or database table cannot be resolved, thus cannot be validated |
| Boolean status = validateThirdPartyStateFieldPathExpression(expression); |
| |
| // Third path extension validated the path expression |
| if (status != null) { |
| valid = status; |
| } |
| // Third path extension did not validate the path expression, continue validating it |
| else { |
| type = helper.getType(expression); |
| |
| // Does not resolve to a valid path |
| if (!helper.isTypeResolvable(type)) { |
| addProblem(expression, StateFieldPathExpression_NotResolvable, expression.toActualText()); |
| valid = false; |
| } |
| // No mapping can be found for that path |
| else { |
| addProblem(expression, StateFieldPathExpression_NoMapping, expression.toActualText()); |
| valid = false; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| return valid; |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>SUBSTRING</b></code> expression. |
| * The test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link SubstringExpression} to validate by validating its encapsulated expression |
| * @return A number indicating the validation result. {@link #isValid(int, int)} can be used to |
| * determine the validation status of an expression based on its position |
| */ |
| protected int validateSubstringExpression(SubstringExpression expression) { |
| |
| int result = 0; |
| |
| // Validate the first expression |
| if (expression.hasFirstExpression()) { |
| Expression firstExpression = expression.getFirstExpression(); |
| |
| // Special case for state field path expression, association field is not allowed |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(firstExpression); |
| |
| if (pathExpression != null) { |
| boolean valid = validateStateFieldPathExpression(pathExpression, PathType.BASIC_FIELD_ONLY); |
| updateStatus(result, 0, valid); |
| } |
| else { |
| firstExpression.accept(this); |
| } |
| } |
| |
| // Validate the second expression |
| expression.getSecondExpression().accept(this); |
| |
| // Validate the third expression |
| expression.getThirdExpression().accept(this); |
| |
| return result; |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given subtraction expression. The test to perform is: |
| * <ul> |
| * <li>If the left or right expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the left or right expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be updated.</li> |
| * </ul> |
| * |
| * @param expression The {@link SubtractionExpression} to validate by validating its encapsulated |
| * expression |
| * @return A number indicating the validation result. {@link #isValid(int, int)} can be used to |
| * determine the validation status of an expression based on its position |
| */ |
| protected int validateSubtractionExpression(SubtractionExpression expression) { |
| return validateArithmeticExpression( |
| expression, |
| SubtractionExpression_LeftExpression_WrongType, |
| SubtractionExpression_RightExpression_WrongType |
| ); |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>MOD</b></code> expression. The |
| * test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link ModExpression} to validate by validating its encapsulated expression |
| * @return <code>false</code> if the encapsulated expression was validated and is invalid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateSumFunction(SumFunction expression) { |
| return validateFunctionPathExpression(expression); |
| } |
| |
| /** |
| * Validates the given {@link StateFieldPathExpression}, which means the path does not represent |
| * a mapping, or an enum constant. |
| * |
| * @param expression The {@link StateFieldPathExpression} the validate |
| * @return <code>Boolean.TRUE</code> or <code>Boolean.FALSE</code> if the given {@link |
| * StateFieldPathExpression} was validated by this method; <code>null</code> if it could not be |
| * validated (as being valid or invalid) |
| */ |
| protected Boolean validateThirdPartyStateFieldPathExpression(StateFieldPathExpression expression) { |
| return null; |
| } |
| |
| /** |
| * Validates the given {@link TreatExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link TreatExpression} to validate |
| */ |
| protected void validateTreatExpression(TreatExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>TRIM</b></code> expression. The |
| * test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link TrimExpression} to validate by validating its encapsulated expression |
| * @return <code>false</code> if the encapsulated expression was validated and is invalid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateTrimExpression(TrimExpression expression) { |
| return validateFunctionPathExpression(expression); |
| } |
| |
| /** |
| * Validates the given {@link TypeExpression}. The test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is an |
| * association field, a basic field is not allowed.</li> |
| * </ul> |
| * |
| * @param expression The {@link TypeExpression} to validate |
| * @return <code>false</code> if the encapsulated expression was validated and is invalid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateTypeExpression(TypeExpression expression) { |
| |
| // Validate the expression |
| if (expression.hasEncapsulatedExpression()) { |
| Expression encapsulatedExpression = expression.getExpression(); |
| |
| // Special case for state field path expression, only association field is allowed |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(encapsulatedExpression); |
| |
| if (pathExpression != null) { |
| return validateStateFieldPathExpression(pathExpression, PathType.ASSOCIATION_FIELD_ONLY); |
| } |
| else { |
| encapsulatedExpression.accept(this); |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Validates the given {@link UpdateClause}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link UpdateClause} to validate |
| */ |
| protected void validateUpdateClause(UpdateClause expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link UpdateItem} by validating the traversability of the path |
| * expression. The path expression is valid if it follows one of the following rules: |
| * <ul> |
| * <li>The identification variable is omitted if it's not defined in the <b>FROM</b> clause; |
| * <li>The last path is a state field; |
| * <li>Only embedded field can be traversed. |
| * </ul> |
| * |
| * @param expression {@link UpdateItem} to validate its path expression |
| * @return <code>true</code> if the path expression is valid; <code>false</code> otherwise |
| */ |
| protected boolean validateUpdateItem(UpdateItem expression) { |
| |
| boolean valid = true; |
| |
| if (expression.hasStateFieldPathExpression()) { |
| |
| // Retrieve the state field path expression |
| StateFieldPathExpression pathExpression = getStateFieldPathExpression(expression.getStateFieldPathExpression()); |
| |
| if ((pathExpression != null) && |
| (pathExpression.hasIdentificationVariable() || |
| pathExpression.hasVirtualIdentificationVariable())) { |
| |
| // Start traversing the path expression by first retrieving the managed type |
| Object managedType = helper.getManagedType(pathExpression.getIdentificationVariable()); |
| |
| if (managedType != null) { |
| |
| // Continue to traverse the path expression |
| for (int index = pathExpression.hasVirtualIdentificationVariable() ? 0 : 1, count = pathExpression.pathSize(); index < count; index++) { |
| |
| // Retrieve the mapping |
| String path = pathExpression.getPath(index); |
| Object mapping = helper.getMappingNamed(managedType, path); |
| |
| // Not resolvable |
| if (mapping == null) { |
| addProblem(pathExpression, UpdateItem_NotResolvable, pathExpression.toParsedText()); |
| valid = false; |
| break; |
| } |
| // A collection mapping cannot be traversed or a value cannot be assigned to it |
| else if (helper.isCollectionMapping(mapping)) { |
| addProblem(pathExpression, UpdateItem_RelationshipPathExpression); |
| valid = false; |
| break; |
| } |
| // Validate an intermediate path (n + 1, ..., n - 2) |
| else if (index + 1 < count) { |
| |
| // A relationship mapping cannot be traversed |
| if (helper.isRelationshipMapping(mapping)) { |
| addProblem(pathExpression, UpdateItem_RelationshipPathExpression); |
| valid = false; |
| break; |
| } |
| // A basic mapping cannot be traversed |
| else if (helper.isPropertyMapping(mapping)) { |
| addProblem(pathExpression, UpdateItem_RelationshipPathExpression); |
| valid = false; |
| break; |
| } |
| // Retrieve the embeddable mapping's reference managed type |
| else { |
| managedType = helper.getReferenceManagedType(mapping); |
| |
| if (managedType == null) { |
| addProblem(pathExpression, UpdateItem_NotResolvable, pathExpression.toParsedText()); |
| valid = false; |
| break; |
| } |
| } |
| } |
| } |
| } |
| else { |
| addProblem(pathExpression, UpdateItem_NotResolvable, pathExpression.toParsedText()); |
| valid = false; |
| } |
| } |
| else { |
| addProblem(pathExpression, UpdateItem_NotResolvable, expression.getStateFieldPathExpression().toParsedText()); |
| valid = false; |
| } |
| } |
| |
| return valid; |
| } |
| |
| /** |
| * Validates the given {@link UpdateStatement}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link UpdateStatement} to validate |
| */ |
| protected void validateUpdateStatement(UpdateStatement expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the encapsulated expression of the given <code><b>UPPER</b></code> expression. The |
| * test to perform is: |
| * <ul> |
| * <li>If the encapsulated expression is a path expression, validation makes sure it is a basic |
| * mapping, an association field is not allowed.</li> |
| * <li>If the encapsulated expression is not a path expression, validation will be redirected to |
| * that expression but the returned status will not be changed.</li> |
| * </ul> |
| * |
| * @param expression The {@link UpperExpression} to validate by validating its encapsulated expression |
| * @return <code>false</code> if the encapsulated expression was validated and is invalid; |
| * <code>true</code> otherwise |
| */ |
| protected boolean validateUpperExpression(UpperExpression expression) { |
| return validateFunctionPathExpression(expression); |
| } |
| |
| /** |
| * Validates the given {@link ValueExpression}. The default behavior does not require to |
| * semantically validate it. |
| * |
| * @param expression The {@link ValueExpression} to validate |
| */ |
| protected void validateValueExpression(ValueExpression expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link WhenClause}. The default behavior does not require to semantically |
| * validate it. |
| * |
| * @param expression The {@link WhenClause} to validate |
| */ |
| protected void validateWhenClause(WhenClause expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Validates the given {@link WhereClause}. The default behavior does not require to semantically |
| * validate it. |
| * |
| * @param expression The {@link WhereClause} to validate |
| */ |
| protected void validateWhereClause(WhereClause expression) { |
| super.visit(expression); |
| } |
| |
| /** |
| * Returns the type of path expression that is valid for a count function; which is the left |
| * expression in a <code><b>COUNT</b></code> expression. |
| * |
| * @return By default, any field are allowed except collection |
| */ |
| protected PathType validPathExpressionTypeForCountFunction() { |
| return PathType.ANY_FIELD; |
| } |
| |
| /** |
| * Returns the type of path expression that is valid for the expression being tested by an |
| * <code><b>IN</b></code> expression; which is the left expression. |
| * |
| * @return By default, any field (without collection) is allowed |
| */ |
| protected PathType validPathExpressionTypeForInExpression() { |
| return PathType.ANY_FIELD; |
| } |
| |
| /** |
| * Returns the type of path expression that is valid for an <code>IN</code> items; which is the |
| * left expression in a <code><b>IN</b></code> expression. |
| * |
| * @return By default, any field are allowed except collection |
| */ |
| protected PathType validPathExpressionTypeForInItem() { |
| return PathType.ANY_FIELD; |
| } |
| |
| /** |
| * Returns the type of path expression that is valid for a string expression; which is the left |
| * expression in a <code><b>LIKE</b></code> expression. |
| * |
| * @return By default, only basic field are allowed |
| */ |
| protected PathType validPathExpressionTypeForStringExpression() { |
| return PathType.BASIC_FIELD_ONLY; |
| } |
| |
| @Override |
| public final void visit(AbsExpression expression) { |
| validateAbsExpression(expression); |
| } |
| |
| @Override |
| public final void visit(AbstractSchemaName expression) { |
| validateAbstractSchemaName(expression); |
| } |
| |
| @Override |
| public final void visit(AdditionExpression expression) { |
| validateAdditionExpression(expression); |
| } |
| |
| @Override |
| public final void visit(AllOrAnyExpression expression) { |
| validateAllOrAnyExpression(expression); |
| } |
| |
| @Override |
| public final void visit(AndExpression expression) { |
| validateAndExpression(expression); |
| } |
| |
| @Override |
| public final void visit(ArithmeticFactor expression) { |
| validateArithmeticExpression(expression); |
| } |
| |
| @Override |
| public final void visit(AvgFunction expression) { |
| validateAvgFunction(expression); |
| } |
| |
| @Override |
| public final void visit(BadExpression expression) { |
| // Nothing to validate semantically |
| } |
| |
| @Override |
| public final void visit(BetweenExpression expression) { |
| validateBetweenExpression(expression); |
| } |
| |
| @Override |
| public final void visit(CaseExpression expression) { |
| validateCaseExpression(expression); |
| } |
| |
| @Override |
| public final void visit(CoalesceExpression expression) { |
| validateCoalesceExpression(expression); |
| } |
| |
| @Override |
| public final void visit(CollectionExpression expression) { |
| // Nothing to validate semantically |
| super.visit(expression); |
| } |
| |
| @Override |
| public final void visit(CollectionMemberDeclaration expression) { |
| validateCollectionMemberDeclaration(expression); |
| } |
| |
| @Override |
| public final void visit(CollectionMemberExpression expression) { |
| validateCollectionMemberExpression(expression); |
| } |
| |
| @Override |
| public final void visit(CollectionValuedPathExpression expression) { |
| // Validated by the parent of the expression |
| } |
| |
| @Override |
| public final void visit(ComparisonExpression expression) { |
| validateComparisonExpression(expression); |
| } |
| |
| @Override |
| public final void visit(ConcatExpression expression) { |
| validateConcatExpression(expression); |
| } |
| |
| @Override |
| public final void visit(ConstructorExpression expression) { |
| validateConstructorExpression(expression); |
| } |
| |
| @Override |
| public final void visit(CountFunction expression) { |
| validateCountFunction(expression); |
| } |
| |
| @Override |
| public final void visit(DateTime expression) { |
| validateDateTime(expression); |
| } |
| |
| @Override |
| public final void visit(DeleteClause expression) { |
| validateDeleteClause(expression); |
| } |
| |
| @Override |
| public final void visit(DeleteStatement expression) { |
| validateDeleteStatement(expression); |
| } |
| |
| @Override |
| public final void visit(DivisionExpression expression) { |
| validateDivisionExpression(expression); |
| } |
| |
| @Override |
| public final void visit(EmptyCollectionComparisonExpression expression) { |
| validateCollectionValuedPathExpression(expression.getExpression(), true); |
| } |
| |
| @Override |
| public final void visit(EntityTypeLiteral expression) { |
| validateEntityTypeLiteral(expression); |
| } |
| |
| @Override |
| public final void visit(EntryExpression expression) { |
| validateEntryExpression(expression); |
| } |
| |
| @Override |
| public final void visit(ExistsExpression expression) { |
| validateExistsExpression(expression); |
| } |
| |
| @Override |
| public final void visit(FromClause expression) { |
| validateFromClause(expression); |
| } |
| |
| @Override |
| public final void visit(FunctionExpression expression) { |
| validateFunctionExpression(expression); |
| } |
| |
| @Override |
| public final void visit(GroupByClause expression) { |
| validateGroupByClause(expression); |
| } |
| |
| @Override |
| public final void visit(HavingClause expression) { |
| validateHavingClause(expression); |
| } |
| |
| @Override |
| public final void visit(IdentificationVariable expression) { |
| validateIdentificationVariable(expression); |
| } |
| |
| @Override |
| public final void visit(IdentificationVariableDeclaration expression) { |
| validateIdentificationVariableDeclaration(expression); |
| } |
| |
| @Override |
| public final void visit(IndexExpression expression) { |
| validateIndexExpression(expression); |
| } |
| |
| @Override |
| public final void visit(InExpression expression) { |
| validateInExpression(expression); |
| } |
| |
| @Override |
| public final void visit(InputParameter expression) { |
| // Nothing to validate semantically |
| } |
| |
| @Override |
| public final void visit(Join expression) { |
| validateJoin(expression); |
| } |
| |
| @Override |
| public final void visit(JPQLExpression expression) { |
| if (expression.hasQueryStatement()) { |
| expression.getQueryStatement().accept(this); |
| validateIdentificationVariables(); |
| } |
| } |
| |
| @Override |
| public final void visit(KeyExpression expression) { |
| validateKeyExpression(expression); |
| } |
| |
| @Override |
| public final void visit(KeywordExpression expression) { |
| // Nothing semantically to validate |
| } |
| |
| @Override |
| public final void visit(LengthExpression expression) { |
| validateLengthExpression(expression); |
| } |
| |
| @Override |
| public final void visit(LikeExpression expression) { |
| validateLikeExpression(expression); |
| } |
| |
| @Override |
| public final void visit(LocateExpression expression) { |
| validateLocateExpression(expression); |
| } |
| |
| @Override |
| public final void visit(LowerExpression expression) { |
| validateLowerExpression(expression); |
| } |
| |
| @Override |
| public final void visit(MaxFunction expression) { |
| validateMaxFunction(expression); |
| } |
| |
| @Override |
| public final void visit(MinFunction expression) { |
| validateMinFunction(expression); |
| } |
| |
| @Override |
| public final void visit(ModExpression expression) { |
| validateModExpression(expression); |
| } |
| |
| @Override |
| public final void visit(MultiplicationExpression expression) { |
| validateMultiplicationExpression(expression); |
| } |
| |
| @Override |
| public final void visit(NotExpression expression) { |
| validateNotExpression(expression); |
| } |
| |
| @Override |
| public final void visit(NullComparisonExpression expression) { |
| validateNullComparisonExpression(expression); |
| } |
| |
| @Override |
| public final void visit(NullExpression expression) { |
| // Nothing semantically to validate |
| } |
| |
| @Override |
| public final void visit(NullIfExpression expression) { |
| validateNullIfExpression(expression); |
| } |
| |
| @Override |
| public final void visit(NumericLiteral expression) { |
| // Nothing semantically to validate |
| } |
| |
| @Override |
| public final void visit(ObjectExpression expression) { |
| validateObjectExpression(expression); |
| } |
| |
| @Override |
| public final void visit(OnClause expression) { |
| validateOnClause(expression); |
| } |
| |
| @Override |
| public final void visit(OrderByClause expression) { |
| validateOrderByClause(expression); |
| } |
| |
| @Override |
| public final void visit(OrderByItem expression) { |
| validateOrderByItem(expression); |
| } |
| |
| @Override |
| public final void visit(OrExpression expression) { |
| validateOrExpression(expression); |
| } |
| |
| @Override |
| public final void visit(RangeVariableDeclaration expression) { |
| validateRangeVariableDeclaration(expression); |
| } |
| |
| @Override |
| public final void visit(ResultVariable expression) { |
| |
| try { |
| registerIdentificationVariable = false; |
| validateResultVariable(expression); |
| } |
| finally { |
| registerIdentificationVariable = true; |
| } |
| } |
| |
| @Override |
| public final void visit(SelectClause expression) { |
| validateSelectClause(expression); |
| } |
| |
| @Override |
| public final void visit(SelectStatement expression) { |
| validateSelectStatement(expression); |
| } |
| |
| @Override |
| public final void visit(SimpleFromClause expression) { |
| validateSimpleFromClause(expression); |
| } |
| |
| @Override |
| public final void visit(SimpleSelectClause expression) { |
| validateSimpleSelectClause(expression); |
| } |
| |
| @Override |
| public final void visit(SimpleSelectStatement expression) { |
| validateSimpleSelectStatement(expression); |
| } |
| |
| @Override |
| public final void visit(SizeExpression expression) { |
| validateSizeExpression(expression); |
| } |
| |
| @Override |
| public final void visit(SqrtExpression expression) { |
| validateSqrtExpression(expression); |
| } |
| |
| @Override |
| public final void visit(StateFieldPathExpression expression) { |
| validateStateFieldPathExpression(expression, PathType.ANY_FIELD); |
| } |
| |
| @Override |
| public final void visit(StringLiteral expression) { |
| // Nothing semantically to validate |
| } |
| |
| @Override |
| public final void visit(SubExpression expression) { |
| // Nothing semantically to validate |
| super.visit(expression); |
| } |
| |
| @Override |
| public final void visit(SubstringExpression expression) { |
| validateSubstringExpression(expression); |
| } |
| |
| @Override |
| public final void visit(SubtractionExpression expression) { |
| validateSubtractionExpression(expression); |
| } |
| |
| @Override |
| public final void visit(SumFunction expression) { |
| validateSumFunction(expression); |
| } |
| |
| @Override |
| public final void visit(TreatExpression expression) { |
| validateTreatExpression(expression); |
| } |
| |
| @Override |
| public final void visit(TrimExpression expression) { |
| validateTrimExpression(expression); |
| } |
| |
| @Override |
| public final void visit(TypeExpression expression) { |
| validateTypeExpression(expression); |
| } |
| |
| @Override |
| public final void visit(UnknownExpression expression) { |
| // Nothing semantically to validate |
| } |
| |
| @Override |
| public final void visit(UpdateClause expression) { |
| validateUpdateClause(expression); |
| } |
| |
| @Override |
| public final void visit(UpdateItem expression) { |
| validateUpdateItem(expression); |
| } |
| |
| @Override |
| public final void visit(UpdateStatement expression) { |
| validateUpdateStatement(expression); |
| } |
| |
| @Override |
| public final void visit(UpperExpression expression) { |
| validateUpperExpression(expression); |
| } |
| |
| @Override |
| public final void visit(ValueExpression expression) { |
| validateValueExpression(expression); |
| } |
| |
| @Override |
| public final void visit(WhenClause expression) { |
| validateWhenClause(expression); |
| } |
| |
| @Override |
| public final void visit(WhereClause expression) { |
| validateWhereClause(expression); |
| } |
| |
| // Made static final for performance reasons. |
| /** |
| * This visitor is meant to retrieve an {@link CollectionValuedPathExpression} if the visited |
| * {@link Expression} is that object. |
| */ |
| protected static final class CollectionValuedPathExpressionVisitor extends AbstractExpressionVisitor { |
| |
| /** |
| * The {@link CollectionValuedPathExpression} that was visited; <code>null</code> if he was not. |
| */ |
| protected CollectionValuedPathExpression expression; |
| |
| /** |
| * Default constructor. |
| */ |
| protected CollectionValuedPathExpressionVisitor() { |
| } |
| |
| @Override |
| public void visit(CollectionValuedPathExpression expression) { |
| this.expression = expression; |
| } |
| } |
| |
| // Made static final for performance reasons. |
| protected static final class ComparingEntityTypeLiteralVisitor extends AbstractExpressionVisitor { |
| |
| protected IdentificationVariable expression; |
| public boolean result; |
| |
| /** |
| * Default constructor. |
| */ |
| protected ComparingEntityTypeLiteralVisitor() { |
| } |
| |
| @Override |
| public void visit(ComparisonExpression expression) { |
| result = true; |
| } |
| |
| @Override |
| public void visit(IdentificationVariable expression) { |
| if (this.expression == expression) { |
| expression.getParent().accept(this); |
| } |
| } |
| |
| @Override |
| public void visit(SubExpression expression) { |
| // Make sure to bypass any sub expression |
| expression.getParent().accept(this); |
| } |
| } |
| |
| // Made static final for performance reasons. |
| /** |
| * This visitor compares the left and right expressions of a comparison expression and gathers |
| * information about those expressions if they are an identification variable or a path expression. |
| */ |
| protected static final class ComparisonExpressionVisitor extends AnonymousExpressionVisitor { |
| |
| private final AbstractSemanticValidator validator; |
| public boolean leftIdentificationVariable; |
| public boolean leftIdentificationVariableValid; |
| public boolean leftStateFieldPathExpression; |
| public boolean leftStateFieldPathExpressionValid; |
| public boolean rightIdentificationVariable; |
| public boolean rightIdentificationVariableValid; |
| public boolean rightStateFieldPathExpression; |
| public boolean rightStateFieldPathExpressionValid; |
| public boolean validatingLeftExpression; |
| |
| private ComparisonExpressionVisitor(AbstractSemanticValidator validator) { |
| this.validator = validator; |
| } |
| |
| /** |
| * Resets the flags. |
| */ |
| private void dispose() { |
| leftIdentificationVariable = false; |
| leftIdentificationVariableValid = false; |
| leftStateFieldPathExpression = false; |
| leftStateFieldPathExpressionValid = false; |
| rightIdentificationVariable = false; |
| rightIdentificationVariableValid = false; |
| rightStateFieldPathExpression = false; |
| rightStateFieldPathExpressionValid = false; |
| } |
| |
| @Override |
| protected void visit(Expression expression) { |
| // Redirect to the validator, nothing special is required |
| expression.accept(validator); |
| } |
| |
| @Override |
| public void visit(IdentificationVariable expression) { |
| |
| // Make sure the identification variable is not a result variable |
| if (!validator.helper.isResultVariable(expression.getVariableName())) { |
| |
| if (validatingLeftExpression) { |
| leftIdentificationVariable = !expression.isVirtual(); |
| |
| // Make sure what was parsed is a valid identification variable |
| leftIdentificationVariableValid = validator.validateIdentificationVariable(expression); |
| } |
| else { |
| rightIdentificationVariable = !expression.isVirtual(); |
| |
| // Make sure what was parsed is a valid identification variable |
| rightIdentificationVariableValid = validator.validateIdentificationVariable(expression); |
| } |
| } |
| } |
| |
| @Override |
| public void visit(StateFieldPathExpression expression) { |
| |
| if (validatingLeftExpression) { |
| leftStateFieldPathExpression = true; |
| |
| // Make sure what was parsed is a valid path expression |
| leftStateFieldPathExpressionValid = validator.validateStateFieldPathExpression( |
| expression, |
| PathType.ANY_FIELD_INCLUDING_COLLECTION |
| ); |
| } |
| else { |
| rightStateFieldPathExpression = true; |
| |
| // Make sure what was parsed is a valid path expression |
| rightStateFieldPathExpressionValid = validator.validateStateFieldPathExpression( |
| expression, |
| PathType.ANY_FIELD_INCLUDING_COLLECTION |
| ); |
| } |
| } |
| } |
| |
| // Made static for performance reasons. |
| protected static class FirstDeclarationVisitor extends AnonymousExpressionVisitor { |
| |
| protected boolean valid; |
| |
| /** |
| * Default constructor. |
| */ |
| protected FirstDeclarationVisitor() { |
| } |
| |
| @Override |
| public void visit(AbstractSchemaName expression) { |
| valid = true; |
| } |
| |
| @Override |
| public void visit(BadExpression expression) { |
| // Already validated, no need to duplicate the issue |
| valid = false; |
| } |
| |
| @Override |
| protected void visit(Expression expression) { |
| valid = false; |
| } |
| |
| @Override |
| public void visit(IdentificationVariableDeclaration expression) { |
| expression.getRangeVariableDeclaration().accept(this); |
| } |
| |
| @Override |
| public void visit(NullExpression expression) { |
| // Already validated, no need to duplicate the issue |
| valid = false; |
| } |
| |
| @Override |
| public void visit(RangeVariableDeclaration expression) { |
| expression.getRootObject().accept(this); |
| } |
| } |
| |
| // Made static final for performance reasons. |
| protected static final class InItemsVisitor extends AnonymousExpressionVisitor { |
| |
| private final AbstractSemanticValidator validator; |
| |
| protected InItemsVisitor(AbstractSemanticValidator validator) { |
| this.validator = validator; |
| } |
| |
| @Override |
| protected void visit(Expression expression) { |
| expression.accept(validator); |
| } |
| |
| @Override |
| public void visit(StateFieldPathExpression expression) { |
| validator.validateStateFieldPathExpression(expression, validator.validPathExpressionTypeForInItem()); |
| } |
| } |
| |
| // Made static for performance reasons. |
| /** |
| * This enumeration allows {@link AbstractSemanticValidator#validateStateFieldPathExpression( |
| * StateFieldPathExpression, PathType)} to validate the type of the mapping and to make sure it |
| * is allowed based on its location. |
| */ |
| protected static enum PathType { |
| |
| /** |
| * This will allow basic, and association fields to be specified. |
| */ |
| ANY_FIELD, |
| |
| /** |
| * This will allow basic, and association fields to be specified. |
| */ |
| ANY_FIELD_INCLUDING_COLLECTION, |
| |
| /** |
| * This will allow association fields to be specified but basic mappings are not valid. |
| */ |
| ASSOCIATION_FIELD_ONLY, |
| |
| /** |
| * This will allow basic fields to be specified but association mappings are not valid. |
| */ |
| BASIC_FIELD_ONLY |
| } |
| |
| // Made static final for performance reasons. |
| /** |
| * This visitor is meant to retrieve an {@link StateFieldPathExpressionVisitor} if the visited |
| * {@link Expression} is that object. |
| */ |
| protected static final class StateFieldPathExpressionVisitor extends AbstractExpressionVisitor { |
| |
| /** |
| * The {@link StateFieldPathExpression} that was visited; <code>null</code> if it was not. |
| */ |
| protected StateFieldPathExpression expression; |
| |
| /** |
| * Default constructor. |
| */ |
| protected StateFieldPathExpressionVisitor() { |
| } |
| |
| @Override |
| public void visit(StateFieldPathExpression expression) { |
| this.expression = expression; |
| } |
| } |
| |
| // Made static final for performance reasons. |
| protected static final class SubqueryFirstDeclarationVisitor extends FirstDeclarationVisitor { |
| |
| /** |
| * Default constructor. |
| */ |
| protected SubqueryFirstDeclarationVisitor() { |
| } |
| |
| @Override |
| public void visit(CollectionValuedPathExpression expression) { |
| valid = true; |
| } |
| } |
| |
| // Made static for performance reasons. |
| protected static class TopLevelFirstDeclarationVisitor extends FirstDeclarationVisitor { |
| |
| /** |
| * Default constructor. |
| */ |
| protected TopLevelFirstDeclarationVisitor() { |
| } |
| |
| @Override |
| public void visit(AbstractSchemaName expression) { |
| // TODO |
| valid = true; |
| } |
| } |
| } |