Package org.eclipse.persistence.jpa.jpql

Source Code of org.eclipse.persistence.jpa.jpql.EclipseLinkGrammarValidator$InExpressionVisitor

/*******************************************************************************
* Copyright (c) 2006, 2014 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 org.eclipse.persistence.jpa.jpql.AbstractEclipseLinkSemanticValidator.EclipseLinkOwningClauseVisitor;
import org.eclipse.persistence.jpa.jpql.parser.AbstractEclipseLinkExpressionVisitor;
import org.eclipse.persistence.jpa.jpql.parser.AbstractSelectClause;
import org.eclipse.persistence.jpa.jpql.parser.AsOfClause;
import org.eclipse.persistence.jpa.jpql.parser.CastExpression;
import org.eclipse.persistence.jpa.jpql.parser.ConnectByClause;
import org.eclipse.persistence.jpa.jpql.parser.DatabaseType;
import org.eclipse.persistence.jpa.jpql.parser.DatabaseTypeFactory;
import org.eclipse.persistence.jpa.jpql.parser.DefaultEclipseLinkJPQLGrammar;
import org.eclipse.persistence.jpa.jpql.parser.EclipseLinkExpressionVisitor;
import org.eclipse.persistence.jpa.jpql.parser.Expression;
import org.eclipse.persistence.jpa.jpql.parser.ExtractExpression;
import org.eclipse.persistence.jpa.jpql.parser.HierarchicalQueryClause;
import org.eclipse.persistence.jpa.jpql.parser.InExpression;
import org.eclipse.persistence.jpa.jpql.parser.InputParameter;
import org.eclipse.persistence.jpa.jpql.parser.JPQLGrammar;
import org.eclipse.persistence.jpa.jpql.parser.OrderSiblingsByClause;
import org.eclipse.persistence.jpa.jpql.parser.PatternValueBNF;
import org.eclipse.persistence.jpa.jpql.parser.RegexpExpression;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectClause;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectStatement;
import org.eclipse.persistence.jpa.jpql.parser.StartWithClause;
import org.eclipse.persistence.jpa.jpql.parser.StringExpressionBNF;
import org.eclipse.persistence.jpa.jpql.parser.TableExpression;
import org.eclipse.persistence.jpa.jpql.parser.TableVariableDeclaration;
import org.eclipse.persistence.jpa.jpql.parser.UnionClause;
import static org.eclipse.persistence.jpa.jpql.JPQLQueryProblemMessages.*;
import static org.eclipse.persistence.jpa.jpql.parser.Expression.*;

/**
* This validator adds EclipseLink extension over what the JPA functional specification had defined.
* <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.
*
* @version 2.5
* @since 2.4
* @author Pascal Filion
*/
public class EclipseLinkGrammarValidator extends AbstractGrammarValidator
                                         implements EclipseLinkExpressionVisitor {

  private InExpressionVisitor inExpressionVisitor;
  private InExpressionWithNestedArrayVisitor inExpressionWithNestedArrayVisitor;

  /**
   * Creates a new <code>EclipseLinkGrammarValidator</code>.
   *
   * @param jpqlGrammar The {@link JPQLGrammar} that defines how the JPQL query was parsed
   */
  public EclipseLinkGrammarValidator(JPQLGrammar jpqlGrammar) {
    super(jpqlGrammar);
  }

  protected AbstractSingleEncapsulatedExpressionHelper<CastExpression> buildCastExpressionHelper() {
    return new AbstractSingleEncapsulatedExpressionHelper<CastExpression>(this) {
      @Override
      protected String encapsulatedExpressionInvalidKey(CastExpression expression) {
        return CastExpression_InvalidExpression;
      }
      @Override
      protected String encapsulatedExpressionMissingKey(CastExpression expression) {
        return CastExpression_MissingExpression;
      }
      public String leftParenthesisMissingKey(CastExpression expression) {
        return CastExpression_MissingLeftParenthesis;
      }
      public String rightParenthesisMissingKey(CastExpression expression) {
        return CastExpression_MissingRightParenthesis;
      }
    };
  }

  protected AbstractDoubleEncapsulatedExpressionHelper<DatabaseType> buildDatabaseTypeHelper() {
    return new AbstractDoubleEncapsulatedExpressionHelper<DatabaseType>(this) {
      @Override
      protected String firstExpressionInvalidKey() {
        return DatabaseType_InvalidFirstExpression;
      }
      @Override
      protected String firstExpressionMissingKey() {
        return DatabaseType_MissingFirstExpression;
      }
      @Override
      protected boolean hasComma(DatabaseType expression) {
        // If the second expression is not specified, then the comma is not needed
        return expression.hasComma() ||
              !expression.hasSecondExpression();
      }
      @Override
      protected boolean hasFirstExpression(DatabaseType expression) {
        return !expression.hasLeftParenthesis() ||
                expression.hasFirstExpression();
      }
      @Override
      public boolean hasLeftParenthesis(DatabaseType expression) {
        if (expression.hasLeftParenthesis()) {
          return true;
        }
        // The parenthesis are optional unless one the following
        // items is specified, then '(' is required
        return !(expression.hasFirstExpression()  ||
                 expression.hasComma()            ||
                 expression.hasSecondExpression() ||
                 expression.hasRightParenthesis());
      }
      @Override
      public boolean hasRightParenthesis(DatabaseType expression) {
        if (expression.hasRightParenthesis()) {
          return true;
        }
        // The parenthesis are optional unless one the following
        // items is specified, then ')' is required
        return !(expression.hasLeftParenthesis()  ||
                 expression.hasFirstExpression()  ||
                 expression.hasComma()            ||
                 expression.hasSecondExpression());
      }
      @Override
      protected boolean hasSecondExpression(DatabaseType expression) {
        return !expression.hasComma() ||
                expression.hasSecondExpression();
      }
      public String leftParenthesisMissingKey(DatabaseType expression) {
        return DatabaseType_MissingLeftParenthesis;
      }
      @Override
      protected String missingCommaKey() {
        return DatabaseType_MissingComma;
      }
      public String rightParenthesisMissingKey(DatabaseType expression) {
        return DatabaseType_MissingRightParenthesis;
      }
      @Override
      protected String secondExpressionInvalidKey() {
        return DatabaseType_InvalidSecondExpression;
      }
      @Override
      protected String secondExpressionMissingKey() {
        return DatabaseType_MissingSecondExpression;
      }
    };
  }

  protected AbstractSingleEncapsulatedExpressionHelper<ExtractExpression> buildExtractExpressionHelper() {
    return new AbstractSingleEncapsulatedExpressionHelper<ExtractExpression>(this) {
      @Override
      protected String encapsulatedExpressionInvalidKey(ExtractExpression expression) {
        return ExtractExpression_InvalidExpression;
      }
      @Override
      protected String encapsulatedExpressionMissingKey(ExtractExpression expression) {
        return ExtractExpression_MissingExpression;
      }
      public String leftParenthesisMissingKey(ExtractExpression expression) {
        return ExtractExpression_MissingLeftParenthesis;
      }
      @Override
      protected int lengthBeforeEncapsulatedExpression(ExtractExpression expression) {
        return expression.getDatePart().length() +
               (expression.hasSpaceAfterDatePart() ? 1 : 0) +
               (expression.hasFrom() ? 4 /* FROM */ : 0) +
               (expression.hasSpaceAfterFrom() ? 1 : 0);
      }
      public String rightParenthesisMissingKey(ExtractExpression expression) {
        return ExtractExpression_MissingRightParenthesis;
      }
    };
  }

  protected InExpressionVisitor buildInExpressionVisitor() {
    return new InExpressionVisitor();
  }

  protected InExpressionWithNestedArrayVisitor buildInExpressionWithNestedArrayVisitor() {
    return new InExpressionWithNestedArrayVisitor(this);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected LiteralVisitor buildLiteralVisitor() {
    return new EclipseLinkLiteralVisitor();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected OwningClauseVisitor buildOwningClauseVisitor() {
    return new EclipseLinkOwningClauseVisitor();
  }

  protected AbstractSingleEncapsulatedExpressionHelper<TableExpression> buildTableExpressionHelper() {
    return new AbstractSingleEncapsulatedExpressionHelper<TableExpression>(this) {
      @Override
      protected String encapsulatedExpressionInvalidKey(TableExpression expression) {
        return TableExpression_InvalidExpression;
      }
      @Override
      protected String encapsulatedExpressionMissingKey(TableExpression expression) {
        return TableExpression_MissingExpression;
      }
      public String leftParenthesisMissingKey(TableExpression expression) {
        return TableExpression_MissingLeftParenthesis;
      }
      public String rightParenthesisMissingKey(TableExpression expression) {
        return TableExpression_MissingRightParenthesis;
      }
    };
  }

  protected AbstractSingleEncapsulatedExpressionHelper<CastExpression> castExpressionHelper() {
    AbstractSingleEncapsulatedExpressionHelper<CastExpression> helper = getHelper(CAST);
    if (helper == null) {
      helper = buildCastExpressionHelper();
      registerHelper(CAST, helper);
    }
    return helper;
  }

  protected AbstractDoubleEncapsulatedExpressionHelper<DatabaseType> databaseTypeHelper() {
    AbstractDoubleEncapsulatedExpressionHelper<DatabaseType> helper = getHelper(DatabaseTypeFactory.ID);
    if (helper == null) {
      helper = buildDatabaseTypeHelper();
      registerHelper(DatabaseTypeFactory.ID, helper);
    }
    return helper;
  }

  protected AbstractSingleEncapsulatedExpressionHelper<ExtractExpression> extractExpressionHelper() {
    AbstractSingleEncapsulatedExpressionHelper<ExtractExpression> helper = getHelper(EXTRACT);
    if (helper == null) {
      helper = buildExtractExpressionHelper();
      registerHelper(EXTRACT, helper);
    }
    return helper;
  }

  protected InExpressionVisitor getInExpressionVisitor() {
    if (inExpressionVisitor == null) {
      inExpressionVisitor = buildInExpressionVisitor();
    }
    return inExpressionVisitor;
  }

  protected InExpressionWithNestedArrayVisitor getInExpressionWithNestedArray() {
    if (inExpressionWithNestedArrayVisitor == null) {
      inExpressionWithNestedArrayVisitor = buildInExpressionWithNestedArrayVisitor();
    }
    return inExpressionWithNestedArrayVisitor;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected EclipseLinkOwningClauseVisitor getOwningClauseVisitor() {
    return (EclipseLinkOwningClauseVisitor) super.getOwningClauseVisitor();
  }

  /**
   * Determines whether the persistence provider is EclipseLink or not.
   *
   * @return <code>true</code> if the persistence provider is EclipseLink; <code>false</code> otherwise
   */
  protected final boolean isEclipseLink() {
    return DefaultEclipseLinkJPQLGrammar.PROVIDER_NAME.equals(getProvider());
  }

  /**
   * Determines whether the subquery is part of an <code><b>IN</b></code> expression where the
   * left expression is a nested array.
   *
   * @param expression The {@link SimpleSelectClause} of the subquery
   * @return <code>true</code> if the subquery is in an <code><b>IN</b></code> expression and its
   * left expression is a nested array
   */
  protected boolean isInExpressionWithNestedArray(SimpleSelectClause expression) {
    InExpressionWithNestedArrayVisitor visitor = getInExpressionWithNestedArray();
    try {
      expression.accept(visitor);
      return visitor.valid;
    }
    finally {
      visitor.valid = false;
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected boolean isInputParameterInValidLocation(InputParameter expression) {
    return true;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected boolean isJoinFetchIdentifiable() {
    EclipseLinkVersion version = EclipseLinkVersion.value(getGrammar().getProviderVersion());
    return version.isNewerThanOrEqual(EclipseLinkVersion.VERSION_2_4);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected boolean isMultipleSubquerySelectItemsAllowed(SimpleSelectClause expression) {
    return isInExpressionWithNestedArray(expression);
  }

  protected boolean isOwnedByInExpression(Expression expression) {
    InExpressionVisitor visitor = getInExpressionVisitor();
    expression.accept(visitor);
    return visitor.expression != null;
  }

  /**
   * Determines whether the given {@link Expression} is a child of the <b>UNION</b> clause.
   *
   * @param expression The {@link Expression} to visit its parent hierarchy up to the clause
   * @return <code>true</code> if the first parent being a clause is the <b>UNION</b> clause;
   * <code>false</code> otherwise
   */
  protected boolean isOwnedByUnionClause(Expression expression) {
    EclipseLinkOwningClauseVisitor visitor = getOwningClauseVisitor();
    try {
      expression.accept(visitor);
      return visitor.unionClause != null;
    }
    finally {
      visitor.dispose();
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected boolean isSubqueryAllowedAnywhere() {
    EclipseLinkVersion version = EclipseLinkVersion.value(getProviderVersion());
    return version.isNewerThanOrEqual(EclipseLinkVersion.VERSION_2_4);
  }

  protected AbstractSingleEncapsulatedExpressionHelper<TableExpression> tableExpressionHelper() {
    AbstractSingleEncapsulatedExpressionHelper<TableExpression> helper = getHelper(TABLE);
    if (helper == null) {
      helper = buildTableExpressionHelper();
      registerHelper(TABLE, helper);
    }
    return helper;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void validateAbstractSelectClause(AbstractSelectClause expression,
                                              boolean multipleSelectItemsAllowed) {

    // A subquery can have multiple select items if it is
    // - used as a "root" object in the top-level FROM clause
    // - defined in a UNION clause
    // - used in an IN expression
    // If the flag is false, then the SELECT clause is from a subquery
    if (!multipleSelectItemsAllowed) {
      Expression parent = expression.getParent();
      multipleSelectItemsAllowed = isOwnedByFromClause  (parent) ||
                                   isOwnedByUnionClause (parent) ||
                                   isOwnedByInExpression(parent);
    }

    super.validateAbstractSelectClause(expression, multipleSelectItemsAllowed);
  }

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

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

    // Wrong JPA version
    if (!isEclipseLink()) {
      addProblem(expression, CastExpression_InvalidJPAVersion);
    }
    else {

      validateAbstractSingleEncapsulatedExpression(expression, castExpressionHelper());

      // Database type
      if (expression.hasExpression() || expression.hasAs()) {

        // Missing database type
        if (!expression.hasDatabaseType()) {

          int startPosition = position(expression) +
                              4 /* CAST */ +
                              (expression.hasLeftParenthesis() ? 1 : 0) +
                              length(expression.getExpression()) +
                              (expression.hasSpaceAfterExpression() ? 1 : 0) +
                              (expression.hasAs() ? 2 : 0) +
                              (expression.hasSpaceAfterAs() ? 1 : 0);

          addProblem(expression, startPosition, CastExpression_MissingDatabaseType);
        }
        // Validate database type
        else {
          expression.getDatabaseType().accept(this);
        }
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  public void visit(ConnectByClause expression) {
    // TODO: 2.5
  }

  /**
   * {@inheritDoc}
   */
  public void visit(DatabaseType expression) {
    validateAbstractDoubleEncapsulatedExpression(expression, databaseTypeHelper());
  }

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

    // Wrong JPA version
    if (!isEclipseLink()) {
      addProblem(expression, ExtractExpression_InvalidJPAVersion);
    }
    else {

        validateAbstractSingleEncapsulatedExpression(expression, extractExpressionHelper());

      // Missing date part
      if (expression.hasLeftParenthesis() && !expression.hasDatePart()) {

        int startPosition = position(expression) +
                            7 /* EXTRACT */ +
                            (expression.hasLeftParenthesis() ? 1 : 0);

        addProblem(expression, startPosition, ExtractExpression_MissingDatePart);
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  public void visit(HierarchicalQueryClause expression) {
    // TODO: 2.5
  }

  /**
   * {@inheritDoc}
   */
  public void visit(OrderSiblingsByClause expression) {
    // TODO
  }

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

    // Wrong JPA version
    if (!isEclipseLink()) {
      addProblem(expression, RegexpExpression_InvalidJPAVersion);
    }
    else {

      // Missing string expression
      if (!expression.hasStringExpression()) {

        int startPosition = position(expression);
        int endPosition   = startPosition;

        addProblem(
          expression,
          startPosition,
          endPosition,
          RegexpExpression_MissingStringExpression
        );
      }
      else {
        Expression stringExpression = expression.getStringExpression();

        // Invalid string expression
        if (!isValid(stringExpression, StringExpressionBNF.ID)) {

          int startPosition = position(stringExpression);
          int endPosition   = startPosition + length(stringExpression);

          addProblem(
            expression,
            startPosition,
            endPosition,
            RegexpExpression_InvalidStringExpression
          );
        }
        // Validate string expression
        else {
          stringExpression.accept(this);
        }
      }

      // Missing pattern value
      if (!expression.hasPatternValue()) {

        int startPosition = position(expression) +
                            length(expression.getStringExpression()) +
                            (expression.hasSpaceAfterStringExpression() ? 1 : 0) +
                            6 /* REGEXP */ +
                            (expression.hasSpaceAfterIdentifier() ? 1 : 0);

        int endPosition = startPosition;

        addProblem(expression, startPosition, endPosition, RegexpExpression_MissingPatternValue);
      }
      else {
        Expression patternValue = expression.getStringExpression();

        // Invalid string expression
        if (!isValid(patternValue, PatternValueBNF.ID)) {

          int startPosition = position(expression) +
                              length(expression.getStringExpression()) +
                              (expression.hasSpaceAfterStringExpression() ? 1 : 0) +
                              6 /* REGEXP */ +
                              (expression.hasSpaceAfterIdentifier() ? 1 : 0);

          int endPosition = startPosition + length(patternValue);

          addProblem(
            expression,
            startPosition,
            endPosition,
            RegexpExpression_InvalidPatternValue
          );
        }
        // Validate pattern value
        else {
          patternValue.accept(this);
        }
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  public void visit(StartWithClause expression) {
    // TODO: 2.5
  }

  /**
   * {@inheritDoc}
   */
  public void visit(TableExpression expression) {
    validateAbstractSingleEncapsulatedExpression(expression, tableExpressionHelper());
  }

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

    // Wrong JPA version
    if (!isEclipseLink()) {
      addProblem(expression, TableVariableDeclaration_InvalidJPAVersion);
    }
    else {

      TableExpression tableExpression = expression.getTableExpression();

      // Validate the table expression
      tableExpression.accept(this);

      // The identification variable is missing
      if (!expression.hasIdentificationVariable()) {

        int startPosition = position(expression) +
                            length(tableExpression) +
                            (expression.hasSpaceAfterTableExpression() ? 1 : 0) +
                            (expression.hasAs() ? 2 : 0) +
                            (expression.hasSpaceAfterAs() ? 1 : 0);

        addProblem(expression, startPosition, TableVariableDeclaration_MissingIdentificationVariable);
      }
      // Validate the identification variable
      else {
        expression.getIdentificationVariable().accept(this);
      }
    }
  }

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

    // Wrong JPA version
    if (!isEclipseLink()) {
      addProblem(expression, UnionClause_InvalidJPAVersion);
    }
    // Missing subquery
    else if (!expression.hasQuery()) {

      int startPosition = position(expression) +
                          expression.getIdentifier().length() +
                          (expression.hasSpaceAfterIdentifier() ? 1 : 0) +
                          (expression.hasAll() ? 3 : 0) +
                          (expression.hasSpaceAfterAll() ? 1 : 0);

      addProblem(expression, startPosition, UnionClause_MissingExpression);
    }
    // Validate the subquery
    else {
      expression.getQuery().accept(this);
    }
  }

    // Made static for performance reasons.
  protected static class InExpressionVisitor extends AbstractEclipseLinkExpressionVisitor {

    protected InExpression expression;

    @Override
    public void visit(InExpression expression) {
      this.expression = expression;
    }
  }

    // Made static final for performance reasons.
  protected static final class InExpressionWithNestedArrayVisitor extends AbstractEclipseLinkExpressionVisitor {

      private final EclipseLinkGrammarValidator visitor;

      protected InExpressionWithNestedArrayVisitor(EclipseLinkGrammarValidator visitor) {
          this.visitor = visitor;
      }

      /**
     * Determines whether the left expression of an <code><b>IN</b></code> expression is a nested
     * array when the <code><b>IN</b></code> item is a subquery.
     */
    public boolean valid;

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(InExpression expression) {
      valid = visitor.isNestedArray(expression.getExpression());
    }

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

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

Related Classes of org.eclipse.persistence.jpa.jpql.EclipseLinkGrammarValidator$InExpressionVisitor

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.