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.calcite.test.fuzzer;
  
  import org.apache.calcite.rel.type.RelDataType;
  import org.apache.calcite.rex.RexCall;
  import org.apache.calcite.rex.RexFieldAccess;
  import org.apache.calcite.rex.RexLiteral;
  import org.apache.calcite.rex.RexNode;
  import org.apache.calcite.rex.RexVisitorImpl;
  import org.apache.calcite.sql.SqlKind;
  import org.apache.calcite.sql.SqlOperator;
  import org.apache.calcite.sql.fun.SqlStdOperatorTable;
  import org.apache.calcite.sql.type.SqlTypeName;
  
  import com.google.common.collect.ImmutableMap;
  
  import java.util.List;
  import java.util.Map;
  
  /**
   * Converts {@link RexNode} into a string form usable for inclusion into
   * {@link RexProgramFuzzyTest}.
   * For instance, it converts {@code AND(=(?0.bool0, true), =(?0.bool1, true))} to
   * {@code isTrue(and(eq(vBool(0), trueLiteral), eq(vBool(1), trueLiteral)))}.
   */
  class RexToTestCodeShuttle extends RexVisitorImpl<String> {
    private static final Map<SqlOperator, String> OP_METHODS =
        ImmutableMap.<SqlOperator, String>builder()
            .put(SqlStdOperatorTable.AND, "and")
            .put(SqlStdOperatorTable.OR, "or")
            .put(SqlStdOperatorTable.CASE, "case_")
            .put(SqlStdOperatorTable.CAST, "abstractCast")
            .put(SqlStdOperatorTable.COALESCE, "coalesce")
            .put(SqlStdOperatorTable.IS_NULL, "isNull")
            .put(SqlStdOperatorTable.IS_NOT_NULL, "isNotNull")
            .put(SqlStdOperatorTable.IS_UNKNOWN, "isUnknown")
            .put(SqlStdOperatorTable.IS_TRUE, "isTrue")
            .put(SqlStdOperatorTable.IS_NOT_TRUE, "isNotTrue")
            .put(SqlStdOperatorTable.IS_FALSE, "isFalse")
            .put(SqlStdOperatorTable.IS_NOT_FALSE, "isNotFalse")
            .put(SqlStdOperatorTable.IS_DISTINCT_FROM, "isDistinctFrom")
            .put(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM, "isNotDistinctFrom")
            .put(SqlStdOperatorTable.NULLIF, "nullIf")
            .put(SqlStdOperatorTable.NOT, "not")
            .put(SqlStdOperatorTable.GREATER_THAN, "gt")
            .put(SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, "ge")
            .put(SqlStdOperatorTable.LESS_THAN, "lt")
            .put(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, "le")
            .put(SqlStdOperatorTable.EQUALS, "eq")
            .put(SqlStdOperatorTable.NOT_EQUALS, "ne")
            .put(SqlStdOperatorTable.PLUS, "plus")
            .put(SqlStdOperatorTable.UNARY_PLUS, "unaryPlus")
            .put(SqlStdOperatorTable.MINUS, "sub")
            .put(SqlStdOperatorTable.UNARY_MINUS, "unaryMinus")
            .put(SqlStdOperatorTable.MULTIPLY, "mul")
            .build();
  
  
    protected RexToTestCodeShuttle() {
      super(true);
    }
  
    @Override public String visitCall(RexCall call) {
      SqlOperator operator = call.getOperator();
      String method = OP_METHODS.get(operator);
  
      StringBuilder sb = new StringBuilder();
      if (method != null) {
        sb.append(method);
        sb.append('(');
      } else {
        sb.append("rexBuilder.makeCall(");
        sb.append("SqlStdOperatorTable.");
        sb.append(operator.getName().replace(' ', '_'));
        sb.append(", ");
      }
      List<RexNode> operands = call.getOperands();
      for (int i = 0; i < operands.size(); i++) {
        RexNode operand = operands.get(i);
        if (i > 0) {
          sb.append(", ");
        }
        sb.append(operand.accept(this));
      }
      if (operator.kind == SqlKind.CAST) {
        sb.append(", t");
        appendSqlType(sb, call.getType());
        sb.append('(');
        if (call.getType().isNullable()) {
          sb.append("true");
        }
        sb.append(')');
      }
      sb.append(')');
      return sb.toString();
    }
  
    @Override public String visitLiteral(RexLiteral literal) {
      RelDataType type = literal.getType();
  
      if (type.getSqlTypeName() == SqlTypeName.BOOLEAN) {
        if (literal.isNull()) {
          return "nullBool";
        }
        return literal.toString() + "Literal";
      }
      if (type.getSqlTypeName() == SqlTypeName.INTEGER) {
        if (literal.isNull()) {
          return "nullInt";
        }
        return "literal(" + literal.getValue() + ")";
      }
      if (type.getSqlTypeName() == SqlTypeName.VARCHAR) {
        if (literal.isNull()) {
          return "nullVarchar";
        }
      }
      return "/*" + literal.getTypeName().getName() + "*/" + literal.toString();
    }
  
    @Override public String visitFieldAccess(RexFieldAccess fieldAccess) {
      StringBuilder sb = new StringBuilder();
      sb.append("v");
      RelDataType type = fieldAccess.getType();
      appendSqlType(sb, type);
      if (!type.isNullable()) {
        sb.append("NotNull");
      }
      sb.append("(");
      sb.append(fieldAccess.getField().getIndex() % 10);
      sb.append(")");
      return sb.toString();
    }
  
    private void appendSqlType(StringBuilder sb, RelDataType type) {
      switch (type.getSqlTypeName()) {
      case BOOLEAN:
        sb.append("Bool");
        break;
      case INTEGER:
        sb.append("Int");
        break;
      case VARCHAR:
        sb.append("Varchar");
        break;
      }
    }
  }
  