private DatabaseQuery populateQueryImp(
      CharSequence jpqlQuery, DatabaseQuery query, AbstractSession session) {

    try {
      // Parse the JPQL query with the most recent JPQL grammar
      JPQLExpression jpqlExpression =
          new JPQLExpression(jpqlQuery, DefaultEclipseLinkJPQLGrammar.instance(), isTolerant());

      // Create a context that caches the information contained in the JPQL query
      // (especially from the FROM clause)
      JPQLQueryContext queryContext = new JPQLQueryContext(jpqlGrammar());
      queryContext.cache(session, query, jpqlExpression, jpqlQuery);

      // Validate the JPQL query, which will use the JPQL grammar matching the validation level
      validate(queryContext, jpqlExpression);

      // Create the DatabaseQuery by visiting the parsed tree
      DatabaseQueryVisitor visitor = new DatabaseQueryVisitor(queryContext, jpqlQuery);
      jpqlExpression.accept(visitor);

      // Add the input parameter types to the DatabaseQuery
      if (query == null) {
        query = queryContext.getDatabaseQuery();
        addArguments(queryContext, query);
      }

      return query;
    } catch (JPQLException exception) {
      throw exception;
    } catch (Exception exception) {
      throw buildUnexpectedException(jpqlQuery, exception);
    }
  }
  /** {@inheritDoc} */
  @Override
  public Expression buildSelectionCriteria(
      String entityName, String selectionCriteria, AbstractSession session) {

    try {
      // Create the parsed tree representation of the selection criteria
      JPQLExpression jpqlExpression =
          new JPQLExpression(
              selectionCriteria,
              DefaultEclipseLinkJPQLGrammar.instance(),
              ConditionalExpressionBNF.ID,
              isTolerant());

      // Caches the info and add a virtual range variable declaration
      JPQLQueryContext queryContext = new JPQLQueryContext(jpqlGrammar());
      queryContext.cache(session, null, jpqlExpression, selectionCriteria);
      queryContext.addRangeVariableDeclaration(entityName, "this");

      // Validate the JPQL query, which will use the JPQL grammar matching the validation
      // level, for now, only validate the query statement because there could be an unknown
      // ending that is an order by clause
      validate(queryContext, jpqlExpression.getQueryStatement());

      // Create the Expression representing the selection criteria
      return queryContext.buildExpression(jpqlExpression.getQueryStatement());
    } catch (JPQLException exception) {
      throw exception;
    } catch (Exception exception) {
      throw buildUnexpectedException(selectionCriteria, exception);
    }
  }
  /**
   * Returns the {@link JPQLGrammar} that will help to validate the JPQL query grammatically and
   * semantically (contextually). It will also checks if any specific feature added to that grammar
   * is allowed. For instance, if the JPQL query has functions defined for EclipseLink grammar but
   * the validation level is set for generic JPA, then an exception will be thrown.
   *
   * @return The {@link JPQLGrammar} written for a specific JPA version or for the current version
   *     of EclipseLink
   */
  private JPQLGrammar jpqlGrammar() {

    if (validationLevel == ParserValidationType.EclipseLink) {
      return DefaultEclipseLinkJPQLGrammar.instance();
    }

    if (validationLevel == ParserValidationType.JPA10) {
      return JPQLGrammar1_0.instance();
    }

    if (validationLevel == ParserValidationType.JPA20) {
      return JPQLGrammar2_0.instance();
    }

    if (validationLevel == ParserValidationType.JPA21) {
      return JPQLGrammar2_1.instance();
    }

    return DefaultEclipseLinkJPQLGrammar.instance();
  }