content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  package org.apache.asterix.lang.sqlpp.visitor.base;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import org.apache.asterix.common.exceptions.CompilationException;
  import org.apache.asterix.lang.common.base.AbstractClause;
  import org.apache.asterix.lang.common.base.Expression;
  import org.apache.asterix.lang.common.base.ILangExpression;
  import org.apache.asterix.lang.common.base.IVisitorExtension;
  import org.apache.asterix.lang.common.clause.GroupbyClause;
  import org.apache.asterix.lang.common.clause.LetClause;
  import org.apache.asterix.lang.common.clause.LimitClause;
  import org.apache.asterix.lang.common.clause.OrderbyClause;
  import org.apache.asterix.lang.common.clause.WhereClause;
  import org.apache.asterix.lang.common.expression.CallExpr;
  import org.apache.asterix.lang.common.expression.FieldAccessor;
  import org.apache.asterix.lang.common.expression.FieldBinding;
  import org.apache.asterix.lang.common.expression.GbyVariableExpressionPair;
  import org.apache.asterix.lang.common.expression.IfExpr;
  import org.apache.asterix.lang.common.expression.IndexAccessor;
  import org.apache.asterix.lang.common.expression.ListConstructor;
  import org.apache.asterix.lang.common.expression.ListSliceExpression;
  import org.apache.asterix.lang.common.expression.LiteralExpr;
  import org.apache.asterix.lang.common.expression.OperatorExpr;
  import org.apache.asterix.lang.common.expression.QuantifiedExpression;
  import org.apache.asterix.lang.common.expression.RecordConstructor;
  import org.apache.asterix.lang.common.expression.UnaryExpr;
  import org.apache.asterix.lang.common.expression.VariableExpr;
  import org.apache.asterix.lang.common.statement.CopyToStatement;
  import org.apache.asterix.lang.common.statement.FunctionDecl;
  import org.apache.asterix.lang.common.statement.InsertStatement;
  import org.apache.asterix.lang.common.statement.Query;
  import org.apache.asterix.lang.common.struct.Identifier;
  import org.apache.asterix.lang.common.struct.QuantifiedPair;
  import org.apache.asterix.lang.sqlpp.clause.AbstractBinaryCorrelateClause;
  import org.apache.asterix.lang.sqlpp.clause.FromClause;
  import org.apache.asterix.lang.sqlpp.clause.FromTerm;
  import org.apache.asterix.lang.sqlpp.clause.HavingClause;
  import org.apache.asterix.lang.sqlpp.clause.JoinClause;
  import org.apache.asterix.lang.sqlpp.clause.NestClause;
  import org.apache.asterix.lang.sqlpp.clause.Projection;
  import org.apache.asterix.lang.sqlpp.clause.SelectBlock;
  import org.apache.asterix.lang.sqlpp.clause.SelectClause;
  import org.apache.asterix.lang.sqlpp.clause.SelectElement;
  import org.apache.asterix.lang.sqlpp.clause.SelectRegular;
  import org.apache.asterix.lang.sqlpp.clause.SelectSetOperation;
  import org.apache.asterix.lang.sqlpp.clause.UnnestClause;
  import org.apache.asterix.lang.sqlpp.expression.CaseExpression;
  import org.apache.asterix.lang.sqlpp.expression.SelectExpression;
  import org.apache.asterix.lang.sqlpp.expression.WindowExpression;
  import org.apache.asterix.lang.sqlpp.struct.SetOperationRight;
  import org.apache.hyracks.algebricks.common.utils.Pair;
  
  public class AbstractSqlppSimpleExpressionVisitor
          extends AbstractSqlppQueryExpressionVisitor<Expression, ILangExpression> {
  
      @Override
      public Expression visit(FromClause fromClause, ILangExpression arg) throws CompilationException {
          for (FromTerm fromTerm : fromClause.getFromTerms()) {
              visit(fromTerm, arg);
          }
          return null;
      }
  
      @Override
      public Expression visit(FromTerm fromTerm, ILangExpression arg) throws CompilationException {
          // Visit the left expression of a from term.
          fromTerm.setLeftExpression(visit(fromTerm.getLeftExpression(), arg));
  
          // Visits join/unnest/nest clauses.
          for (AbstractBinaryCorrelateClause correlateClause : fromTerm.getCorrelateClauses()) {
              correlateClause.accept(this, arg);
          }
          return null;
      }
  
      @Override
      public Expression visit(JoinClause joinClause, ILangExpression arg) throws CompilationException {
          joinClause.setRightExpression(visit(joinClause.getRightExpression(), arg));
          joinClause.setConditionExpression(visit(joinClause.getConditionExpression(), arg));
          return null;
      }
  
      @Override
      public Expression visit(NestClause nestClause, ILangExpression arg) throws CompilationException {
          nestClause.setRightExpression(visit(nestClause.getRightExpression(), arg));
          nestClause.setConditionExpression(visit(nestClause.getConditionExpression(), arg));
          return null;
      }
  
      @Override
      public Expression visit(UnnestClause unnestClause, ILangExpression arg) throws CompilationException {
          unnestClause.setRightExpression(visit(unnestClause.getRightExpression(), arg));
          return null;
      }
  
      @Override
      public Expression visit(Projection projection, ILangExpression arg) throws CompilationException {
          if (projection.hasExpression()) {
              projection.setExpression(visit(projection.getExpression(), arg));
          }
          return null;
      }
  
      @Override
      public Expression visit(SelectBlock selectBlock, ILangExpression arg) throws CompilationException {
          // Traverses the select block in the order of "from", "let/where"s, "group by", "let/having"s and "select".
          if (selectBlock.hasFromClause()) {
              selectBlock.getFromClause().accept(this, arg);
          }
          if (selectBlock.hasLetWhereClauses()) {
              for (AbstractClause clause : selectBlock.getLetWhereList()) {
                  clause.accept(this, arg);
              }
          }
          if (selectBlock.hasGroupbyClause()) {
              selectBlock.getGroupbyClause().accept(this, arg);
          }
          if (selectBlock.hasLetHavingClausesAfterGroupby()) {
              for (AbstractClause clause : selectBlock.getLetHavingListAfterGroupby()) {
                  clause.accept(this, arg);
              }
          }
          selectBlock.getSelectClause().accept(this, arg);
          return null;
      }
  
      @Override
      public Expression visit(SelectClause selectClause, ILangExpression arg) throws CompilationException {
          if (selectClause.selectElement()) {
              selectClause.getSelectElement().accept(this, selectClause);
          }
          if (selectClause.selectRegular()) {
              selectClause.getSelectRegular().accept(this, selectClause);
          }
          return null;
      }
  
      @Override
      public Expression visit(SelectElement selectElement, ILangExpression arg) throws CompilationException {
          selectElement.setExpression(visit(selectElement.getExpression(), selectElement));
          return null;
      }
  
      @Override
      public Expression visit(SelectRegular selectRegular, ILangExpression arg) throws CompilationException {
          for (Projection projection : selectRegular.getProjections()) {
              projection.accept(this, arg);
          }
          return null;
      }
  
      @Override
      public Expression visit(SelectSetOperation selectSetOperation, ILangExpression arg) throws CompilationException {
          selectSetOperation.getLeftInput().accept(this, arg);
          for (SetOperationRight right : selectSetOperation.getRightInputs()) {
              right.getSetOperationRightInput().accept(this, arg);
          }
          return null;
      }
  
      @Override
      public Expression visit(HavingClause havingClause, ILangExpression arg) throws CompilationException {
          havingClause.setFilterExpression(visit(havingClause.getFilterExpression(), havingClause));
          return null;
      }
  
      @Override
      public Expression visit(Query q, ILangExpression arg) throws CompilationException {
          q.setBody(visit(q.getBody(), q));
          return null;
      }
  
      @Override
      public Expression visit(FunctionDecl fd, ILangExpression arg) throws CompilationException {
          fd.setFuncBody(visit(fd.getFuncBody(), fd));
          return null;
      }
  
      @Override
      public Expression visit(WhereClause whereClause, ILangExpression arg) throws CompilationException {
          whereClause.setWhereExpr(visit(whereClause.getWhereExpr(), whereClause));
          return null;
      }
  
      @Override
      public Expression visit(OrderbyClause oc, ILangExpression arg) throws CompilationException {
          oc.setOrderbyList(visit(oc.getOrderbyList(), oc));
          return null;
      }
  
      @Override
      public Expression visit(GroupbyClause gc, ILangExpression arg) throws CompilationException {
          for (List<GbyVariableExpressionPair> gbyPairList : gc.getGbyPairList()) {
              for (GbyVariableExpressionPair gbyVarExpr : gbyPairList) {
                  gbyVarExpr.setExpr(visit(gbyVarExpr.getExpr(), gc));
              }
          }
          if (gc.hasDecorList()) {
              for (GbyVariableExpressionPair decVarExpr : gc.getDecorPairList()) {
                  decVarExpr.setExpr(visit(decVarExpr.getExpr(), gc));
              }
          }
          return null;
      }
  
      @Override
      public Expression visit(LimitClause limitClause, ILangExpression arg) throws CompilationException {
          if (limitClause.hasLimitExpr()) {
              limitClause.setLimitExpr(visit(limitClause.getLimitExpr(), limitClause));
          }
          if (limitClause.hasOffset()) {
              limitClause.setOffset(visit(limitClause.getOffset(), limitClause));
          }
          return null;
      }
  
      @Override
      public Expression visit(LetClause letClause, ILangExpression arg) throws CompilationException {
          letClause.setBindingExpr(visit(letClause.getBindingExpr(), letClause));
          return null;
      }
  
      @Override
      public Expression visit(SelectExpression selectExpression, ILangExpression arg) throws CompilationException {
          // visit let list
          if (selectExpression.hasLetClauses()) {
              for (LetClause letClause : selectExpression.getLetList()) {
                  letClause.accept(this, selectExpression);
              }
          }
  
          // visit the main select.
          selectExpression.getSelectSetOperation().accept(this, selectExpression);
  
          // visit order by
          if (selectExpression.hasOrderby()) {
              selectExpression.getOrderbyClause().accept(this, selectExpression);
          }
  
          // visit limit
          if (selectExpression.hasLimit()) {
              selectExpression.getLimitClause().accept(this, selectExpression);
          }
          return selectExpression;
      }
  
      @Override
      public Expression visit(LiteralExpr l, ILangExpression arg) throws CompilationException {
          return l;
      }
  
      @Override
      public Expression visit(ListConstructor lc, ILangExpression arg) throws CompilationException {
          lc.setExprList(visit(lc.getExprList(), arg));
          return lc;
      }
  
      @Override
      public Expression visit(RecordConstructor rc, ILangExpression arg) throws CompilationException {
          for (FieldBinding binding : rc.getFbList()) {
              binding.setLeftExpr(visit(binding.getLeftExpr(), arg));
              binding.setRightExpr(visit(binding.getRightExpr(), arg));
          }
          return rc;
      }
  
      @Override
      public Expression visit(OperatorExpr operatorExpr, ILangExpression arg) throws CompilationException {
          operatorExpr.setExprList(visit(operatorExpr.getExprList(), arg));
          return operatorExpr;
      }
  
      @Override
      public Expression visit(IfExpr ifExpr, ILangExpression arg) throws CompilationException {
          ifExpr.setCondExpr(visit(ifExpr.getCondExpr(), arg));
          ifExpr.setThenExpr(visit(ifExpr.getThenExpr(), arg));
          ifExpr.setElseExpr(visit(ifExpr.getElseExpr(), arg));
          return ifExpr;
      }
  
      @Override
      public Expression visit(QuantifiedExpression qe, ILangExpression arg) throws CompilationException {
          for (QuantifiedPair pair : qe.getQuantifiedList()) {
              pair.setExpr(visit(pair.getExpr(), qe));
          }
          qe.setSatisfiesExpr(visit(qe.getSatisfiesExpr(), qe));
          return qe;
      }
  
      @Override
      public Expression visit(CallExpr callExpr, ILangExpression arg) throws CompilationException {
          callExpr.setExprList(visit(callExpr.getExprList(), arg));
          if (callExpr.hasAggregateFilterExpr()) {
              callExpr.setAggregateFilterExpr(visit(callExpr.getAggregateFilterExpr(), arg));
          }
          return callExpr;
      }
  
      @Override
      public Expression visit(VariableExpr varExpr, ILangExpression arg) throws CompilationException {
          return varExpr;
      }
  
      @Override
      public Expression visit(UnaryExpr u, ILangExpression arg) throws CompilationException {
          u.setExpr(visit(u.getExpr(), arg));
          return u;
      }
  
      @Override
      public Expression visit(WindowExpression winExpr, ILangExpression arg) throws CompilationException {
          visitWindowExpressionExcludingExprListAndAggFilter(winExpr, arg);
          winExpr.setExprList(visit(winExpr.getExprList(), arg));
          if (winExpr.hasAggregateFilterExpr()) {
              winExpr.setAggregateFilterExpr(visit(winExpr.getAggregateFilterExpr(), arg));
          }
          return winExpr;
      }
  
      protected void visitWindowExpressionExcludingExprListAndAggFilter(WindowExpression winExpr, ILangExpression arg)
              throws CompilationException {
          if (winExpr.hasPartitionList()) {
              winExpr.setPartitionList(visit(winExpr.getPartitionList(), arg));
          }
          if (winExpr.hasOrderByList()) {
              winExpr.setOrderbyList(visit(winExpr.getOrderbyList(), arg));
          }
          if (winExpr.hasFrameStartExpr()) {
              winExpr.setFrameStartExpr(visit(winExpr.getFrameStartExpr(), arg));
          }
          if (winExpr.hasFrameEndExpr()) {
              winExpr.setFrameEndExpr(visit(winExpr.getFrameEndExpr(), arg));
          }
          if (winExpr.hasWindowFieldList()) {
              for (Pair<Expression, Identifier> field : winExpr.getWindowFieldList()) {
                  field.first = visit(field.first, arg);
              }
          }
      }
  
      @Override
      public Expression visit(CopyToStatement stmtCopy, ILangExpression arg) throws CompilationException {
          stmtCopy.setBody(stmtCopy.getBody().accept(this, arg));
          stmtCopy.setPathExpressions(visit(stmtCopy.getPathExpressions(), arg));
          stmtCopy.setPartitionExpressions(visit(stmtCopy.getPartitionExpressions(), arg));
          stmtCopy.setOrderByList(visit(stmtCopy.getOrderByList(), arg));
          stmtCopy.setKeyExpressions(visit(stmtCopy.getKeyExpressions(), arg));
          return null;
      }
  
      @Override
      public Expression visit(FieldAccessor fa, ILangExpression arg) throws CompilationException {
          fa.setExpr(visit(fa.getExpr(), arg));
          return fa;
      }
  
      @Override
      public Expression visit(IndexAccessor ia, ILangExpression arg) throws CompilationException {
          ia.setExpr(visit(ia.getExpr(), arg));
          if (ia.getIndexExpr() != null) {
              ia.setIndexExpr(visit(ia.getIndexExpr(), arg));
          }
          return ia;
      }
  
      @Override
      public Expression visit(ListSliceExpression expression, ILangExpression arg) throws CompilationException {
          expression.setExpr(visit(expression.getExpr(), arg));
          expression.setStartIndexExpression(visit(expression.getStartIndexExpression(), arg));
  
          // End index expression can be null (optional)
          if (expression.hasEndExpression()) {
              expression.setEndIndexExpression(visit(expression.getEndIndexExpression(), arg));
          }
          return expression;
      }
  
      @Override
      public Expression visit(IVisitorExtension ve, ILangExpression arg) throws CompilationException {
          return ve.simpleExpressionDispatch(this, arg);
      }
  
      @Override
      public Expression visit(CaseExpression caseExpr, ILangExpression arg) throws CompilationException {
          caseExpr.setConditionExpr(visit(caseExpr.getConditionExpr(), arg));
          caseExpr.setWhenExprs(visit(caseExpr.getWhenExprs(), arg));
          caseExpr.setThenExprs(visit(caseExpr.getThenExprs(), arg));
          caseExpr.setElseExpr(visit(caseExpr.getElseExpr(), arg));
          return caseExpr;
      }
  
      @Override
      public Expression visit(InsertStatement insertStatement, ILangExpression arg) throws CompilationException {
          Expression returnExpr = insertStatement.getReturnExpression();
          if (returnExpr != null) {
              insertStatement.setReturnExpression(visit(returnExpr, arg));
          }
          Query bodyQuery = insertStatement.getQuery();
          bodyQuery.accept(this, arg);
          return null;
      }
  
      public Expression visit(Expression expr, ILangExpression arg) throws CompilationException {
          return postVisit(preVisit(expr).accept(this, arg));
      }
  
      protected Expression preVisit(Expression expr) throws CompilationException {
          return expr;
      }
  
      protected Expression postVisit(Expression expr) throws CompilationException {
          return expr;
      }
  
      protected List<Expression> visit(List<Expression> exprs, ILangExpression arg) throws CompilationException {
          List<Expression> newExprList = new ArrayList<>(exprs.size());
          for (Expression expr : exprs) {
              newExprList.add(visit(expr, arg));
          }
          return newExprList;
      }
  }
  