/*
 * Copyright 2008 Google Inc.
 *
 * Licensed 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 com.google.template.soy.exprtree;

import com.google.template.soy.basetree.AbstractNodeVisitor;
import com.google.template.soy.basetree.ParentNode;
import com.google.template.soy.exprtree.ExprNode.OperatorNode;
import com.google.template.soy.exprtree.ExprNode.ParentExprNode;
import com.google.template.soy.exprtree.ExprNode.PrimitiveNode;
import com.google.template.soy.exprtree.OperatorNodes.AndOpNode;
import com.google.template.soy.exprtree.OperatorNodes.ConditionalOpNode;
import com.google.template.soy.exprtree.OperatorNodes.DivideByOpNode;
import com.google.template.soy.exprtree.OperatorNodes.EqualOpNode;
import com.google.template.soy.exprtree.OperatorNodes.GreaterThanOpNode;
import com.google.template.soy.exprtree.OperatorNodes.GreaterThanOrEqualOpNode;
import com.google.template.soy.exprtree.OperatorNodes.LessThanOpNode;
import com.google.template.soy.exprtree.OperatorNodes.LessThanOrEqualOpNode;
import com.google.template.soy.exprtree.OperatorNodes.MinusOpNode;
import com.google.template.soy.exprtree.OperatorNodes.ModOpNode;
import com.google.template.soy.exprtree.OperatorNodes.NegativeOpNode;
import com.google.template.soy.exprtree.OperatorNodes.NotEqualOpNode;
import com.google.template.soy.exprtree.OperatorNodes.NotOpNode;
import com.google.template.soy.exprtree.OperatorNodes.NullCoalescingOpNode;
import com.google.template.soy.exprtree.OperatorNodes.OrOpNode;
import com.google.template.soy.exprtree.OperatorNodes.PlusOpNode;
import com.google.template.soy.exprtree.OperatorNodes.TimesOpNode;

/**
 * Abstract base class for all ExprNode visitors. A visitor is basically a function implemented for
 * some or all ExprNodes, where the implementation can be different for each specific node class.
 *
 * <p> Same as {@link AbstractReturningExprNodeVisitor} except that in this class, internal
 * {@code visit()} calls do not return a value.
 *
 * <p> Important: Do not use outside of Soy code (treat as superpackage-private).
 *
 * <p>
 * To create a visitor:
 * <ol>
 * <li> Subclass this class.
 * <li> Implement {@code visit*Node()} methods for some specific node types.
 * <li> Implement fallback methods for node types not specifically handled. The most general
 *      fallback method is {@link #visitExprNode visitExprNode()}, which is usually needed. Other
 *      fallback methods include {@code visitPrimitiveNode()} and {@code visitOperatorNode()}.
 * <li> Maybe implement a constructor, taking appropriate parameters for your visitor call.
 * <li> Maybe implement {@link #exec exec()} if this visitor needs to return a non-null final result
 *      and/or if this visitor has state that needs to be setup/reset before each unrelated use of
 *      {@code visit()}.
 * </ol>
 *
 * @param <R> The return type of this visitor.
 *
 * @see AbstractReturningExprNodeVisitor
 */
public abstract class AbstractExprNodeVisitor<R> extends AbstractNodeVisitor<ExprNode, R> {

  @Override protected final void visit(ExprNode node) {

    switch (node.getKind()) {

      case EXPR_ROOT_NODE: visitExprRootNode((ExprRootNode) node); break;

      case NULL_NODE: visitNullNode((NullNode) node); break;
      case BOOLEAN_NODE: visitBooleanNode((BooleanNode) node); break;
      case INTEGER_NODE: visitIntegerNode((IntegerNode) node); break;
      case FLOAT_NODE: visitFloatNode((FloatNode) node); break;
      case STRING_NODE: visitStringNode((StringNode) node); break;

      case LIST_LITERAL_NODE: visitListLiteralNode((ListLiteralNode) node); break;
      case MAP_LITERAL_NODE: visitMapLiteralNode((MapLiteralNode) node); break;

      case VAR_REF_NODE: visitVarRefNode((VarRefNode) node); break;
      case FIELD_ACCESS_NODE: visitFieldAccessNode((FieldAccessNode) node); break;
      case ITEM_ACCESS_NODE: visitItemAccessNode((ItemAccessNode) node); break;

      case GLOBAL_NODE: visitGlobalNode((GlobalNode) node); break;

      case NEGATIVE_OP_NODE: visitNegativeOpNode((NegativeOpNode) node); break;
      case NOT_OP_NODE: visitNotOpNode((NotOpNode) node); break;
      case TIMES_OP_NODE: visitTimesOpNode((TimesOpNode) node); break;
      case DIVIDE_BY_OP_NODE: visitDivideByOpNode((DivideByOpNode) node); break;
      case MOD_OP_NODE: visitModOpNode((ModOpNode) node); break;
      case PLUS_OP_NODE: visitPlusOpNode((PlusOpNode) node); break;
      case MINUS_OP_NODE: visitMinusOpNode((MinusOpNode) node); break;
      case LESS_THAN_OP_NODE: visitLessThanOpNode((LessThanOpNode) node); break;
      case GREATER_THAN_OP_NODE: visitGreaterThanOpNode((GreaterThanOpNode) node); break;
      case LESS_THAN_OR_EQUAL_OP_NODE:
        visitLessThanOrEqualOpNode((LessThanOrEqualOpNode) node); break;
      case GREATER_THAN_OR_EQUAL_OP_NODE:
        visitGreaterThanOrEqualOpNode((GreaterThanOrEqualOpNode) node); break;
      case EQUAL_OP_NODE: visitEqualOpNode((EqualOpNode) node); break;
      case NOT_EQUAL_OP_NODE: visitNotEqualOpNode((NotEqualOpNode) node); break;
      case AND_OP_NODE: visitAndOpNode((AndOpNode) node); break;
      case OR_OP_NODE: visitOrOpNode((OrOpNode) node); break;
      case NULL_COALESCING_OP_NODE: visitNullCoalescingOpNode((NullCoalescingOpNode) node); break;
      case CONDITIONAL_OP_NODE: visitConditionalOpNode((ConditionalOpNode) node); break;

      case FUNCTION_NODE: visitFunctionNode((FunctionNode) node); break;

      default: throw new UnsupportedOperationException();
    }
  }

  /**
   * Helper to visit all the children of a node, in order.
   * @param node The parent node whose children to visit.
   * @see #visitChildrenAllowingConcurrentModification
   */
  protected void visitChildren(ParentExprNode node) {
    visitChildren((ParentNode<ExprNode>) node);
  }


  /**
   * Helper to visit all the children of a node, in order.
   *
   * This method differs from {@code visitChildren} in that we are iterating through a copy of the
   * children. Thus, concurrent modification of the list of children is allowed.
   *
   * @param node The parent node whose children to visit.
   * @see #visitChildren
   */
  protected void visitChildrenAllowingConcurrentModification(ParentExprNode node) {
    visitChildrenAllowingConcurrentModification((ParentNode<ExprNode>) node);
  }


  // -----------------------------------------------------------------------------------------------
  // Implementations for misc nodes.


  protected void visitExprRootNode(ExprRootNode node) {
    visitExprNode(node);
  }


  // -----------------------------------------------------------------------------------------------
  // Implementations for primitive nodes.


  protected void visitNullNode(NullNode node) {
    visitPrimitiveNode(node);
  }

  protected void visitBooleanNode(BooleanNode node) {
    visitPrimitiveNode(node);
  }

  protected void visitIntegerNode(IntegerNode node) {
    visitPrimitiveNode(node);
  }

  protected void visitFloatNode(FloatNode node) {
    visitPrimitiveNode(node);
  }

  protected void visitStringNode(StringNode node) {
    visitPrimitiveNode(node);
  }

  protected void visitPrimitiveNode(PrimitiveNode node) {
    visitExprNode(node);
  }


  // -----------------------------------------------------------------------------------------------
  // Implementations for collection nodes.


  protected void visitListLiteralNode(ListLiteralNode node) {
    visitExprNode(node);
  }

  protected void visitMapLiteralNode(MapLiteralNode node) {
    visitExprNode(node);
  }


  // -----------------------------------------------------------------------------------------------
  // Implementations for reference nodes.


  protected void visitVarRefNode(VarRefNode node) {
    visitExprNode(node);
  }

  protected void visitDataAccessNode(DataAccessNode node) {
    visitExprNode(node);
  }

  protected void visitFieldAccessNode(FieldAccessNode node) {
    visitDataAccessNode(node);
  }

  protected void visitItemAccessNode(ItemAccessNode node) {
    visitDataAccessNode(node);
  }

  protected void visitGlobalNode(GlobalNode node) {
    visitExprNode(node);
  }


  // -----------------------------------------------------------------------------------------------
  // Implementations for operator nodes.


  protected void visitNegativeOpNode(NegativeOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitNotOpNode(NotOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitTimesOpNode(TimesOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitDivideByOpNode(DivideByOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitModOpNode(ModOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitPlusOpNode(PlusOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitMinusOpNode(MinusOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitLessThanOpNode(LessThanOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitGreaterThanOpNode(GreaterThanOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitLessThanOrEqualOpNode(LessThanOrEqualOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitGreaterThanOrEqualOpNode(GreaterThanOrEqualOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitEqualOpNode(EqualOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitNotEqualOpNode(NotEqualOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitAndOpNode(AndOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitOrOpNode(OrOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitNullCoalescingOpNode(NullCoalescingOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitConditionalOpNode(ConditionalOpNode node) {
    visitOperatorNode(node);
  }

  protected void visitOperatorNode(OperatorNode node) {
    visitExprNode(node);
  }


  // -----------------------------------------------------------------------------------------------
  // Implementations for function nodes.


  protected void visitFunctionNode(FunctionNode node) {
    visitExprNode(node);
  }


  // -----------------------------------------------------------------------------------------------
  // Fallback implementation.


  /**
   * @param node The node being visited.
   */
  protected void visitExprNode(ExprNode node) {
    throw new UnsupportedOperationException();
  }

}
