Package org.eclipse.persistence.jpa.jpql

Source Code of org.eclipse.persistence.jpa.jpql.DeclarationResolver$RootObjectExpressionVisitor

/*******************************************************************************
* Copyright (c) 2006, 2012 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;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.persistence.jpa.jpql.parser.AbstractExpressionVisitor;
import org.eclipse.persistence.jpa.jpql.parser.AbstractSchemaName;
import org.eclipse.persistence.jpa.jpql.parser.AnonymousExpressionVisitor;
import org.eclipse.persistence.jpa.jpql.parser.CollectionExpression;
import org.eclipse.persistence.jpa.jpql.parser.CollectionMemberDeclaration;
import org.eclipse.persistence.jpa.jpql.parser.CollectionValuedPathExpression;
import org.eclipse.persistence.jpa.jpql.parser.DeleteClause;
import org.eclipse.persistence.jpa.jpql.parser.DeleteStatement;
import org.eclipse.persistence.jpa.jpql.parser.Expression;
import org.eclipse.persistence.jpa.jpql.parser.FromClause;
import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariable;
import org.eclipse.persistence.jpa.jpql.parser.IdentificationVariableDeclaration;
import org.eclipse.persistence.jpa.jpql.parser.JPQLExpression;
import org.eclipse.persistence.jpa.jpql.parser.Join;
import org.eclipse.persistence.jpa.jpql.parser.NullExpression;
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.SubExpression;
import org.eclipse.persistence.jpa.jpql.parser.UpdateClause;
import org.eclipse.persistence.jpa.jpql.parser.UpdateStatement;
import org.eclipse.persistence.jpa.jpql.spi.IMapping;
import org.eclipse.persistence.jpa.jpql.spi.IQuery;
import org.eclipse.persistence.jpa.jpql.spi.IType;
import org.eclipse.persistence.jpa.jpql.spi.ITypeDeclaration;

/**
* This {@link Resolver} is responsible to visit the current query (which is either the top-level
* query or a subquery) and gathers the information from the declaration clause. For a <b>SELECT</b>
* or <b>DELETE</b> clause, the information will be retrieved from the <b>FROM</b> clause. For an
* <code>UDPATE</code> clause, it will be retrieved from the unique identification range variable
* declaration.
*
* @version 2.5
* @since 2.3
* @author Pascal Filion
*/
public class DeclarationResolver extends Resolver {

  /**
   * The {@link Declaration Declarations} of the current query that was parsed.
   */
  private List<Declaration> declarations;

  /**
   * This visitor is responsible to visit the current query's declaration and populate this
   * resolver with the list of declarations.
   */
  private DeclarationVisitor declarationVisitor;

  /**
   * This visitor is responsible to convert the abstract schema name into a path expression.
   */
  private QualifyRangeDeclarationVisitor qualifyRangeDeclarationVisitor;

  /**
   * The context used to query information about the query.
   */
  private JPQLQueryContext queryContext;

  /**
   * The identification variable names mapped to their resolvers.
   */
  private Map<String, Resolver> resolvers;

  /**
   * The variables identifying the select expressions, if any was defined or an empty set if none
   * were defined.
   */
  private Map<IdentificationVariable, String> resultVariables;

  /**
   * This visitor resolves the "root" object represented by the given {@link Expression}. This will
   * also handle using a subquery in the <code><b>FROM</b></code> clause. This is only supported by
   * EclipseLink.
   */
  private RootObjectExpressionVisitor rootObjectExpressionVisitor;

  /**
   * Creates a new <code>DeclarationResolver</code>.
   *
   * @param parent The parent resolver if this is used for a subquery or null if it's used for the
   * top-level query
   * @param queryContext The context used to query information about the query
   */
  public DeclarationResolver(DeclarationResolver parent, JPQLQueryContext queryContext) {
    super(parent);
    initialize(queryContext);
  }

  /**
   * Adds the given {@link Declaration} at the end of the list.
   *
   * @param declaration The {@link Declaration} representing a single variable declaration
   */
  protected final void addDeclaration(Declaration declaration) {
    declarations.add(declaration);
  }

  /**
   * Registers a range variable declaration that will be used when a JPQL fragment is parsed.
   *
   * @param entityName The name of the entity to be accessible with the given variable name
   * @param variableName The identification variable used to navigate to the entity
   */
  public void addRangeVariableDeclaration(String entityName, String variableName) {

    // Always make the identification variable be upper case since it's
    // case insensitive, the get will also use upper case
    String internalVariableName = variableName.toUpperCase();

    // Make sure the identification variable was not declared more than once,
    // this could cause issues when trying to resolve it
    if (!resolvers.containsKey(internalVariableName)) {

      // Resolve the expression and map it with the identification variable
      Resolver resolver = new EntityResolver(this, entityName);
      resolver = new IdentificationVariableResolver(resolver, variableName);
      resolvers.put(internalVariableName, resolver);

      RangeDeclaration declaration = new RangeDeclaration();
      declaration.rootPath               = entityName;
      declaration.identificationVariable = new IdentificationVariable(null, variableName);
      declarations.add(declaration);
    }
  }

  protected DeclarationVisitor buildDeclarationVisitor() {
    return new DeclarationVisitor();
  }

  protected RootObjectExpressionVisitor buildRootObjectExpressionVisitor() {
    return new RootObjectExpressionVisitor();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected IType buildType() {
    return getTypeHelper().unknownType();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected ITypeDeclaration buildTypeDeclaration() {
    return getTypeHelper().unknownTypeDeclaration();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void checkParent(Resolver parent) {
    // Don't do anything, this is the root
  }

  /**
   * Converts the given {@link Declaration} from being set as a range variable declaration to
   * a path expression declaration.
   * <p>
   * In this query "<code>UPDATE Employee SET firstName = 'MODIFIED' WHERE (SELECT COUNT(m) FROM
   * managedEmployees m) > 0</code>" <em>managedEmployees</em> is an unqualified collection-valued
   * path expression (<code>employee.managedEmployees</code>).
   *
   * @param declaration The {@link Declaration} that was parsed to range over an abstract schema
   * name but is actually ranging over a path expression
   * @param outerVariableName The identification variable coming from the parent identification
   * variable declaration
   */
  public void convertUnqualifiedDeclaration(Declaration declaration, String outerVariableName) {

    QualifyRangeDeclarationVisitor visitor = qualifyRangeDeclarationVisitor();

    try {
      visitor.oldDeclaration    = declaration;
      visitor.outerVariableName = outerVariableName;

      declaration.declarationExpression.accept(visitor);
    }
    finally {
      visitor.oldDeclaration    = null;
      visitor.newDeclaration    = null;
      visitor.outerVariableName = null;
    }
  }

  /**
   * Disposes the internal data.
   */
  public void dispose() {
    resolvers      .clear();
    declarations   .clear();
    resultVariables.clear();
  }

  /**
   * Retrieves the {@link Declaration} for which the given variable name is used to navigate to the
   * "root" object.
   *
   * @param variableName The name of the identification variable that is used to navigate a "root" object
   * @return The {@link Declaration} containing the information about the identification variable declaration
   * @since 2.5
   */
  public Declaration getDeclaration(String variableName) {

    for (Declaration declaration : declarations) {
      if (declaration.getVariableName().equalsIgnoreCase(variableName)) {
        return declaration;
      }
    }

    return null;
  }

  /**
   * Returns the ordered list of {@link Declaration Declarations}.
   *
   * @return The {@link Declaration Declarations} of the current query that was parsed
   */
  public List<Declaration> getDeclarations() {
    return declarations;
  }

  protected DeclarationVisitor getDeclarationVisitor() {
    if (declarationVisitor == null) {
      declarationVisitor = buildDeclarationVisitor();
    }
    return declarationVisitor;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public DeclarationResolver getParent() {
    return (DeclarationResolver) super.getParent();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected IQuery getQuery() {
    return queryContext.getQuery();
  }

  /**
   * Returns the {@link JPQLQueryContext} that is used by this visitor.
   *
   * @return The {@link JPQLQueryContext} holding onto the JPQL query and the cached information
   */
  protected JPQLQueryContext getQueryContext() {
    return queryContext;
  }

  /**
   * Retrieves the {@link Resolver} mapped with the given identification variable. If the
   * identification is not defined in the declaration traversed by this resolver, than the search
   * will traverse the parent hierarchy.
   *
   * @param variableName The identification variable that maps a {@link Resolver}
   * @return The {@link Resolver} mapped with the given identification variable
   */
  public Resolver getResolver(String variableName) {

    variableName = variableName.toUpperCase();
    Resolver resolver = getResolverImp(variableName);

    if ((resolver == null) && (getParent() != null)) {
      resolver = getParent().getResolver(variableName);
    }

    if (resolver == null) {
      resolver = new NullResolver(this);
      resolvers.put(variableName, resolver);
    }

    return resolver;
  }

  /**
   * Retrieves the {@link Resolver} mapped with the given identification variable. The search does
   * not traverse the parent hierarchy.
   *
   * @param variableName The identification variable that maps a {@link Resolver}
   * @return The {@link Resolver} mapped with the given identification variable
   */
  protected Resolver getResolverImp(String variableName) {
    return resolvers.get(variableName);
  }

  /**
   * Returns the variables that got defined in the select expression. This only applies to JPQL
   * queries built for JPA 2.0 or later.
   *
   * @return The variables identifying the select expressions, if any was defined or an empty set
   * if none were defined
   */
  public Set<String> getResultVariables() {
    return new HashSet<String>(resultVariables.values());
  }

  /**
   * Returns the map of result variables that got used to define a select expression. This only
   * applies to JPQL queries built for JPA 2.0.
   *
   * @return The variables identifying the select expressions, if any was defined or an empty map
   * if none were defined
   */
  public Map<IdentificationVariable, String> getResultVariablesMap() {
    return resultVariables;
  }

  protected RootObjectExpressionVisitor getRootObjectExpressionVisitor() {
    if (rootObjectExpressionVisitor == null) {
      rootObjectExpressionVisitor = buildRootObjectExpressionVisitor();
    }
    return rootObjectExpressionVisitor;
  }

  /**
   * Determines whether the JPQL expression has <b>JOIN</b> expressions.
   *
   * @return <code>true</code> if the query or subquery being traversed contains <b>JOIN</b>
   * expressions; <code>false</code> otherwise
   */
  public boolean hasJoins() {

    for (Declaration declaration : declarations) {
      if (declaration.hasJoins()) {
        return true;
      }
    }

    return false;
  }

  /**
   * Initializes this <code>DeclarationResolver</code>.
   *
   * @param queryContext The context used to query information about the query
   */
  protected void initialize(JPQLQueryContext queryContext) {
    this.queryContext    = queryContext;
    this.resolvers       = new HashMap<String, Resolver>();
    this.declarations    = new LinkedList<Declaration>();
    this.resultVariables = new HashMap<IdentificationVariable, String>();
  }

  /**
   * Determines whether the given identification variable is defining a <b>JOIN</b> expression or
   * in a <code>IN</code> expressions for a collection-valued field. If the search didn't find the
   * identification in this resolver, then it will traverse the parent hierarchy.
   *
   * @param variableName The identification variable to check for what it maps
   * @return <code>true</code> if the given identification variable maps a collection-valued field
   * defined in a <code>JOIN</code> or <code>IN</code> expression; <code>false</code> otherwise
   */
  public boolean isCollectionIdentificationVariable(String variableName) {

    boolean result = isCollectionIdentificationVariableImp(variableName);

    if (!result && (getParent() != null)) {
      result = getParent().isCollectionIdentificationVariableImp(variableName);
    }

    return result;
  }

  /**
   * Determines whether the given identification variable is defining a <b>JOIN</b> expression or
   * in a <code>IN</code> expressions for a collection-valued field. The search does not traverse
   * the parent hierarchy.
   *
   * @param variableName The identification variable to check for what it maps
   * @return <code>true</code> if the given identification variable maps a collection-valued field
   * defined in a <code>JOIN</code> or <code>IN</code> expression; <code>false</code> otherwise
   */
  protected boolean isCollectionIdentificationVariableImp(String variableName) {

    for (Declaration declaration : declarations) {

      switch (declaration.getType()) {

        case COLLECTION: {
          if (declaration.getVariableName().equalsIgnoreCase(variableName)) {
            return true;
          }
          return false;
        }

        case RANGE:
        case DERIVED: {

          AbstractRangeDeclaration rangeDeclaration = (AbstractRangeDeclaration) declaration;

          // Check the JOIN expressions
          for (Join join : rangeDeclaration.getJoins()) {

            String joinVariableName = queryContext.literal(
              join.getIdentificationVariable(),
              LiteralType.IDENTIFICATION_VARIABLE
            );

            if (joinVariableName.equalsIgnoreCase(variableName)) {
              // Make sure the JOIN expression maps a collection mapping
              Resolver resolver = getResolver(variableName);
              IMapping mapping = (resolver != null) ? resolver.getMapping() : null;
              return (mapping != null) && mapping.isCollection();
            }
          }
        }
      }
    }

    return false;
  }

  /**
   * Determines whether the given variable name is an identification variable name mapping an
   * entity. The search traverses the parent resolver if it is not found in this resolver, which
   * represents the current JPQL query.
   *
   * @param variableName The name of the variable to verify if it's defined in a range variable
   * declaration in the current query or any parent query
   * @return <code>true</code> if the variable name is mapping an abstract schema name; <code>false</code>
   * if it's defined in a collection member declaration
   */
  public boolean isRangeIdentificationVariable(String variableName) {

    boolean result = isRangeIdentificationVariableImp(variableName);

    if (!result && (getParent() != null)) {
      result = getParent().isRangeIdentificationVariableImp(variableName);
    }

    return result;
  }

  /**
   * Determines whether the given variable name is an identification variable name mapping an
   * entity. The search only searches in this resolver, which represents the current JPQL query.
   *
   * @param variableName The name of the variable to verify if it's defined in a range variable
   * declaration in the current query or any parent query
   * @return <code>true</code> if the variable name is mapping an abstract schema name; <code>false</code>
   * if it's defined in a collection member declaration
   */
  protected boolean isRangeIdentificationVariableImp(String variableName) {

    for (Declaration declaration : declarations) {

      if (declaration.getType().isRange() &&
          declaration.getVariableName().equalsIgnoreCase(variableName)) {

        return true;
      }
    }

    return false;
  }

  /**
   * Determines if the given variable is a result variable.
   *
   * @param variable The variable to check if it's a result variable
   * @return <code>true</code> if the given variable is defined as a result variable; <code>false</code>
   * otherwise
   */
  public boolean isResultVariable(String variable) {
    return resultVariables.containsValue(variable.toUpperCase());
  }

  /**
   * Visits the current query (which is either the top-level query or a subquery) and gathers the
   * information from the declaration clause.
   *
   * @param expression The {@link Expression} to visit in order to retrieve the information
   * contained in the given query's declaration
   */
  public void populate(Expression expression) {
    DeclarationVisitor visitor = getDeclarationVisitor();
    try {
      expression.accept(visitor);
    }
    finally {
      visitor.currentDeclaration = null;
    }
  }

  protected QualifyRangeDeclarationVisitor qualifyRangeDeclarationVisitor() {
    if (qualifyRangeDeclarationVisitor == null) {
      qualifyRangeDeclarationVisitor = new QualifyRangeDeclarationVisitor();
    }
    return qualifyRangeDeclarationVisitor;
  }

  /**
   * Resolves the "root" object represented by the given {@link Expression}. This will also handle
   * using a subquery in the <code><b>FROM</b></code> clause. This is only supported by EclipseLink.
   *
   * @param expression The {@link Expression} to visit, which represents the "root" object of a
   * declaration
   * @return The {@link Resolver} for the given {@link Expression}
   */
  protected Resolver resolveRootObject(Expression expression) {
    RootObjectExpressionVisitor visitor = getRootObjectExpressionVisitor();
    try {
      expression.accept(visitor);
      return visitor.resolver;
    }
    finally {
      visitor.resolver = null;
    }
  }

  protected String visitDeclaration(Expression expression, Expression identificationVariable) {

    // Visit the identification variable expression and retrieve the identification variable name
    String variableName = queryContext.literal(
      identificationVariable,
      LiteralType.IDENTIFICATION_VARIABLE
    );

    // If it's not empty, then we can create a Resolver
    if (variableName != ExpressionTools.EMPTY_STRING) {

      // Always make the identification variable be upper case since it's
      // case insensitive, the get will also use upper case
      String internalVariableName = variableName.toUpperCase();

      // Make sure the identification variable was not declared more than once,
      // this could cause issues when trying to resolve it
      if (!resolvers.containsKey(internalVariableName)) {

        // Resolve the expression and map it with the identification variable
        Resolver resolver = resolveRootObject(expression);
        resolver = new IdentificationVariableResolver(resolver, variableName);
        resolvers.put(internalVariableName, resolver);
      }

      return variableName;
    }

    return ExpressionTools.EMPTY_STRING;
  }

  protected class DeclarationVisitor extends AbstractExpressionVisitor {

    /**
     * This flag is used to determine what to do in {@link #visit(SimpleSelectStatement)}.
     */
    protected boolean buildingDeclaration;

    /**
     * Flag used to determine if the {@link IdentificationVariable} to visit is a result variable.
     */
    protected boolean collectResultVariable;

    /**
     * The {@link Declaration} being populated.
     */
    protected Declaration currentDeclaration;

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(AbstractSchemaName expression) {
      currentDeclaration = new RangeDeclaration();
      currentDeclaration.rootPath = expression.getText();
      declarations.add(currentDeclaration);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(CollectionExpression expression) {
      expression.acceptChildren(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(CollectionMemberDeclaration expression) {

      CollectionDeclaration declaration = new CollectionDeclaration();
      declaration.declarationExpression = expression;
      declaration.baseExpression        = expression.getCollectionValuedPathExpression();
      declaration.rootPath              = declaration.baseExpression.toActualText();
      declarations.add(declaration);

      // Retrieve the IdentificationVariable
      Expression identificationVariable = expression.getIdentificationVariable();
      String variableName = visitDeclaration(expression, identificationVariable);

      if (variableName != ExpressionTools.EMPTY_STRING) {
        declaration.identificationVariable = (IdentificationVariable) identificationVariable;
      }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(CollectionValuedPathExpression expression) {
      currentDeclaration = new DerivedDeclaration();
      currentDeclaration.rootPath = expression.toActualText();
      declarations.add(currentDeclaration);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(DeleteClause expression) {

      try {
        buildingDeclaration = true;
        expression.getRangeVariableDeclaration().accept(this);
        buildingDeclaration = false;

        currentDeclaration.declarationExpression = expression;
      }
      finally {
        currentDeclaration = null;
      }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(DeleteStatement expression) {
      expression.getDeleteClause().accept(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(FromClause expression) {
      expression.getDeclaration().accept(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(IdentificationVariable expression) {
      if (collectResultVariable) {
        resultVariables.put(expression, expression.getText().toUpperCase());
      }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(IdentificationVariableDeclaration expression) {

      try {
        expression.getRangeVariableDeclaration().accept(this);
        expression.getJoins().accept(this);
        currentDeclaration.declarationExpression = expression;
      }
      finally {
        currentDeclaration = null;
      }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(Join expression) {

      AbstractRangeDeclaration rangeDeclaration = (AbstractRangeDeclaration) currentDeclaration;
      Expression identificationVariable = expression.getIdentificationVariable();
      visitDeclaration(expression, identificationVariable);
      rangeDeclaration.addJoin(expression);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(JPQLExpression expression) {
      expression.getQueryStatement().accept(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(NullExpression expression) {
      if (buildingDeclaration) {
        currentDeclaration = new UnknownDeclaration();
        currentDeclaration.baseExpression = expression;
        currentDeclaration.rootPath       = ExpressionTools.EMPTY_STRING;
        declarations.add(currentDeclaration);
      }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(RangeVariableDeclaration expression) {

      // Visit the "root" object, which will create the right Declaration
      Expression rootObject = expression.getRootObject();
      buildingDeclaration = true;
      rootObject.accept(this);
      buildingDeclaration = false;

      // Set the base expression
      currentDeclaration.baseExpression = expression;

      // Set the identification variable
      Expression identificationVariable = expression.getIdentificationVariable();
      String variableName = visitDeclaration(rootObject, identificationVariable);

      if (variableName != ExpressionTools.EMPTY_STRING) {
        currentDeclaration.identificationVariable = (IdentificationVariable) identificationVariable;
      }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(ResultVariable expression) {
      collectResultVariable = true;
      expression.getResultVariable().accept(this);
      collectResultVariable = false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SelectClause expression) {
      expression.getSelectExpression().accept(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SelectStatement expression) {
      expression.getFromClause().accept(this);
      expression.getSelectClause().accept(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SimpleFromClause expression) {
      expression.getDeclaration().accept(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SimpleSelectClause expression) {
      expression.getSelectExpression().accept(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SimpleSelectStatement expression) {
      expression.getFromClause().accept(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(UpdateClause expression) {

      try {
        buildingDeclaration = true;
        expression.getRangeVariableDeclaration().accept(this);
        buildingDeclaration = false;

        currentDeclaration.declarationExpression = expression;
      }
      finally {
        currentDeclaration = null;
      }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(UpdateStatement expression) {
      expression.getUpdateClause().accept(this);
    }
  }

  protected class QualifyRangeDeclarationVisitor extends AbstractExpressionVisitor {

    /**
     * The new {@link Declaration}.
     */
    protected Declaration newDeclaration;

    /**
     * The {@link Declaration} being modified.
     */
    protected Declaration oldDeclaration;

    /**
     * The identification variable coming from the parent identification variable declaration.
     */
    protected String outerVariableName;

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(CollectionValuedPathExpression expression) {
      newDeclaration.rootPath       = expression.toActualText();
      newDeclaration.baseExpression = expression;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(IdentificationVariableDeclaration expression) {
      expression.getRangeVariableDeclaration().accept(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(RangeVariableDeclaration expression) {

      newDeclaration = new DerivedDeclaration();

      expression.setVirtualIdentificationVariable(outerVariableName, newDeclaration.rootPath);
      expression.getRootObject().accept(this);
    }
  }

  /**
   * This visitor takes care to support a subquery defined as a "root" object.
   */
  protected class RootObjectExpressionVisitor extends AnonymousExpressionVisitor {

    /**
     * The {@link Resolver} of the "root" object.
     */
    protected Resolver resolver;

    /**
     * {@inheritDoc}
     */
    @Override
    protected void visit(Expression expression) {
      resolver = queryContext.getResolver(expression);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SimpleSelectStatement expression) {
      resolver = new FromSubqueryResolver(
        DeclarationResolver.this,
        DeclarationResolver.this.queryContext,
        expression
      );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(SubExpression expression) {
      expression.getExpression().accept(this);
    }
  }
}
TOP

Related Classes of org.eclipse.persistence.jpa.jpql.DeclarationResolver$RootObjectExpressionVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
cs.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');