/******************************************************************************* * Copyright (c) 2006, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation * ******************************************************************************/ package org.eclipse.persistence.jpa.jpql.parser; /** * This visitor makes sure that all path expressions are fully qualified with a "virtual" * identification variable if the range variable declaration does not define one. This only applies * to an <code><b>UPDATE</b></code> or <code><b>DELETE</b></code> queries. * * @version 2.5 * @since 2.3 * @author Pascal Filion */ public final class FullyQualifyPathExpressionVisitor extends AbstractTraverseChildrenVisitor { /** * The "virtual" identification variable if none was defined. */ private String variableName; /** * Caches this visitor, which is used to determine if the general identification variable is not * a map key, map value or map entry expression. */ private GeneralIdentificationVariableVisitor visitor; private GeneralIdentificationVariableVisitor generalIdentificationVariableVisitor() { if (visitor == null) { visitor = new GeneralIdentificationVariableVisitor(); } return visitor; } /** * {@inheritDoc} */ @Override public void visit(AbstractSchemaName expression) { // The "virtual" variable name will be the entity name variableName = expression.toActualText().toLowerCase(); } /** * {@inheritDoc} */ @Override public void visit(CollectionMemberDeclaration expression) { // Do nothing, prevent to do anything for invalid queries } /** * {@inheritDoc} */ @Override public void visit(CollectionValuedPathExpression expression) { visitAbstractPathExpression(expression); } /** * {@inheritDoc} */ @Override public void visit(DeleteClause expression) { expression.getRangeVariableDeclaration().accept(this); } /** * {@inheritDoc} */ @Override public void visit(DeleteStatement expression) { expression.getDeleteClause().accept(this); // Don't traverse the tree if the path expressions don't need to be virtually qualified if ((variableName != null) && expression.hasWhereClause()) { expression.getWhereClause().accept(this); } } /** * {@inheritDoc} */ @Override public void visit(IdentificationVariable expression) { // A null check is required because the query could be invalid/incomplete // The identification variable should become a state field path expression expression.setVirtualIdentificationVariable(variableName); } /** * {@inheritDoc} */ @Override public void visit(Join expression) { // Do nothing, prevent to do anything for invalid queries } /** * {@inheritDoc} */ @Override public void visit(RangeVariableDeclaration expression) { // The "root" object does not have an identification variable, // then we'll assume all path expressions are unqualified if (!expression.hasIdentificationVariable()) { expression.getRootObject().accept(this); expression.setVirtualIdentificationVariable(variableName); } } /** * {@inheritDoc} */ @Override public void visit(SelectStatement expression) { // Nothing to do because a SELECT query has to have its path expressions fully qualified } /** * {@inheritDoc} */ @Override public void visit(SimpleSelectStatement expression) { // Nothing to do because a subquery query has to have its path expressions fully qualified } /** * {@inheritDoc} */ @Override public void visit(StateFieldPathExpression expression) { // A null check is required because the query could be invalid/incomplete visitAbstractPathExpression(expression); } /** * {@inheritDoc} */ @Override public void visit(UpdateClause expression) { expression.getRangeVariableDeclaration().accept(this); // Don't traverse the tree if the path expressions don't need to be virtually qualified if ((variableName != null) && expression.hasUpdateItems()) { expression.getUpdateItems().accept(this); } } /** * {@inheritDoc} */ @Override public void visit(UpdateStatement expression) { expression.getUpdateClause().accept(this); // Don't traverse the tree if the path expressions don't need to be virtually qualified if ((variableName != null) && expression.hasWhereClause()) { expression.getWhereClause().accept(this); } } private void visitAbstractPathExpression(AbstractPathExpression expression) { if (!expression.startsWithDot()) { // Visit the general identification variable to make sure it's not a map key, map entry // or map value expression GeneralIdentificationVariableVisitor visitor = generalIdentificationVariableVisitor(); expression.getIdentificationVariable().accept(visitor); if (visitor.expression == null) { expression.setVirtualIdentificationVariable(variableName); } } } // Made static final for performance reasons. private static final class GeneralIdentificationVariableVisitor extends AbstractExpressionVisitor { /** * The {@link Expression} that was visited, which is a general identification variable but is * not a identification variable, it's either a map value, map key or map value expression. */ private Expression expression; /** * {@inheritDoc} */ @Override public void visit(EntryExpression expression) { this.expression = expression; } /** * {@inheritDoc} */ @Override public void visit(KeyExpression expression) { this.expression = expression; } /** * {@inheritDoc} */ @Override public void visit(ValueExpression expression) { this.expression = expression; } } }