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.sql;
  
  import org.apache.calcite.sql.type.OperandHandlers;
  import org.apache.calcite.sql.type.SqlOperandHandler;
  import org.apache.calcite.sql.type.SqlOperandTypeChecker;
  import org.apache.calcite.sql.type.SqlOperandTypeInference;
  import org.apache.calcite.sql.type.SqlReturnTypeInference;
  import org.apache.calcite.sql.validate.SqlMonotonicity;
  import org.apache.calcite.sql.validate.SqlValidator;
  import org.apache.calcite.sql.validate.SqlValidatorScope;
  
  import org.checkerframework.checker.nullness.qual.Nullable;
  
  import java.util.function.Function;
  
  import static java.util.Objects.requireNonNull;
  
  /**
   * Concrete implementation of {@link SqlFunction}.
   *
   * <p>The class is final, and instances are immutable.
   *
   * <p>Instances are created only by {@link SqlBasicFunction#create} and are
   * "modified" by "wither" methods such as {@link #withName} to create a new
   * instance with one property changed. Since the class is final, you can modify
   * behavior only by providing strategy objects, not by overriding methods in a
   * subclass.
   */
  public final class SqlBasicFunction extends SqlFunction {
    private final SqlSyntax syntax;
    private final boolean deterministic;
    private final SqlOperandHandler operandHandler;
    private final int callValidator;
    private final Function<SqlOperatorBinding, SqlMonotonicity> monotonicityInference;
    private final boolean dynamic;
  
    //~ Constructors -----------------------------------------------------------
  
    /**
     * Creates a new SqlFunction for a call to a built-in function.
     *
     * @param name Name of built-in function
     * @param kind Kind of operator implemented by function
     * @param syntax Syntax
     * @param deterministic Whether the function is deterministic
     * @param returnTypeInference Strategy to use for return type inference
     * @param operandTypeInference Strategy to use for parameter type inference
     * @param operandHandler Strategy to use for handling operands
     * @param operandTypeChecker Strategy to use for parameter type checking
     * @param callValidator Strategy to validate calls
     * @param category Categorization for function
     * @param monotonicityInference Strategy to infer monotonicity of a call
     */
    private SqlBasicFunction(String name, SqlKind kind, SqlSyntax syntax,
        boolean deterministic, SqlReturnTypeInference returnTypeInference,
        @Nullable SqlOperandTypeInference operandTypeInference,
        SqlOperandHandler operandHandler,
        SqlOperandTypeChecker operandTypeChecker,
        Integer callValidator,
        SqlFunctionCategory category,
        Function<SqlOperatorBinding, SqlMonotonicity> monotonicityInference,
        boolean dynamic) {
      super(name, kind,
          requireNonNull(returnTypeInference, "returnTypeInference"),
          operandTypeInference,
          requireNonNull(operandTypeChecker, "operandTypeChecker"), category);
      this.syntax = requireNonNull(syntax, "syntax");
      this.deterministic = deterministic;
      this.operandHandler = requireNonNull(operandHandler, "operandHandler");
      this.callValidator = requireNonNull(callValidator, "callValidator");
      this.monotonicityInference =
          requireNonNull(monotonicityInference, "monotonicityInference");
      this.dynamic = dynamic;
    }
  
    /**
     * Creates a {@code SqlBasicFunction}.
     *
     * @param name function name
     * @param kind function kind
     * @param returnTypeInference Strategy to use for return type inference
     * @param operandTypeChecker Strategy to use for parameter type checking
     * @return a {@code SqlBasicFunction}
     */
    public static SqlBasicFunction create(String name, SqlKind kind,
        SqlReturnTypeInference returnTypeInference,
        SqlOperandTypeChecker operandTypeChecker) {
      return new SqlBasicFunction(name, kind,
          SqlSyntax.FUNCTION, true, returnTypeInference, null,
          OperandHandlers.DEFAULT, operandTypeChecker, 0,
          SqlFunctionCategory.SYSTEM, call -> SqlMonotonicity.NOT_MONOTONIC, false);
    }
  
    /** Creates a {@code SqlBasicFunction} whose name is the same as its kind
     * and whose category {@link SqlFunctionCategory#SYSTEM}. */
    public static SqlBasicFunction create(SqlKind kind,
        SqlReturnTypeInference returnTypeInference,
        SqlOperandTypeChecker operandTypeChecker) {
      return new SqlBasicFunction(kind.name(), kind,
          SqlSyntax.FUNCTION, true, returnTypeInference, null,
          OperandHandlers.DEFAULT, operandTypeChecker, 0,
          SqlFunctionCategory.SYSTEM, call -> SqlMonotonicity.NOT_MONOTONIC, false);
    }
  
    /** Creates a {@code SqlBasicFunction}
     * with kind {@link SqlKind#OTHER_FUNCTION}
     * and category {@link SqlFunctionCategory#NUMERIC}. */
    public static SqlBasicFunction create(String name,
        SqlReturnTypeInference returnTypeInference,
        SqlOperandTypeChecker operandTypeChecker) {
      return new SqlBasicFunction(name, SqlKind.OTHER_FUNCTION,
          SqlSyntax.FUNCTION, true, returnTypeInference, null,
          OperandHandlers.DEFAULT, operandTypeChecker, 0,
          SqlFunctionCategory.NUMERIC, call -> SqlMonotonicity.NOT_MONOTONIC, false);
    }
  
    /** Creates a {@code SqlBasicFunction}
     * with kind {@link SqlKind#OTHER_FUNCTION}. */
    public static SqlBasicFunction create(String name,
        SqlReturnTypeInference returnTypeInference,
        SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory category) {
      return new SqlBasicFunction(name, SqlKind.OTHER_FUNCTION,
          SqlSyntax.FUNCTION, true, returnTypeInference, null,
          OperandHandlers.DEFAULT, operandTypeChecker, 0,
          category, call -> SqlMonotonicity.NOT_MONOTONIC, false);
    }
  
    //~ Methods ----------------------------------------------------------------
  
    @Override public SqlReturnTypeInference getReturnTypeInference() {
      return requireNonNull(super.getReturnTypeInference(), "returnTypeInference");
    }
  
    @Override public SqlOperandTypeChecker getOperandTypeChecker() {
      return requireNonNull(super.getOperandTypeChecker(), "operandTypeChecker");
    }
  
    @Override public SqlSyntax getSyntax() {
      return syntax;
    }
  
    @Override public boolean isDeterministic() {
      return deterministic;
    }
  
    @Override public @Nullable SqlOperator reverse() {
      return isSymmetrical() ? this : null;
    }
  
    @Override public SqlMonotonicity getMonotonicity(SqlOperatorBinding call) {
      return monotonicityInference.apply(call);
    }
  
    @Override public SqlNode rewriteCall(SqlValidator validator, SqlCall call) {
      return operandHandler.rewriteCall(validator, call);
    }
  
    @Override public void validateCall(SqlCall call, SqlValidator validator,
        SqlValidatorScope scope, SqlValidatorScope operandScope) {
      super.validateCall(call, validator, scope, operandScope);
    }
  
    @Override public boolean isDynamicFunction() {
      return dynamic;
    }
  
    /** Returns a copy of this function with a given name. */
    public SqlBasicFunction withName(String name) {
      return new SqlBasicFunction(name, kind, syntax, deterministic,
          getReturnTypeInference(), getOperandTypeInference(), operandHandler,
          getOperandTypeChecker(), callValidator,
          getFunctionType(), monotonicityInference, dynamic);
    }
  
    /** Returns a copy of this function with a given kind. */
    public SqlBasicFunction withKind(SqlKind kind) {
      return new SqlBasicFunction(getName(), kind, syntax, deterministic,
          getReturnTypeInference(), getOperandTypeInference(), operandHandler,
          getOperandTypeChecker(), callValidator,
          getFunctionType(), monotonicityInference, dynamic);
    }
  
    /** Returns a copy of this function with a given category. */
    public SqlBasicFunction withFunctionType(SqlFunctionCategory category) {
      return new SqlBasicFunction(getName(), kind, syntax, deterministic,
          getReturnTypeInference(), getOperandTypeInference(), operandHandler,
          getOperandTypeChecker(), callValidator, category, monotonicityInference, dynamic);
    }
  
    /** Returns a copy of this function with a given syntax. */
    public SqlBasicFunction withSyntax(SqlSyntax syntax) {
      return new SqlBasicFunction(getName(), kind, syntax, deterministic,
          getReturnTypeInference(), getOperandTypeInference(), operandHandler,
          getOperandTypeChecker(), callValidator,
          getFunctionType(), monotonicityInference, dynamic);
    }
  
    /** Returns a copy of this function with a given strategy for inferring
     * returned type. */
    public SqlBasicFunction withReturnTypeInference(
        SqlReturnTypeInference returnTypeInference) {
      return new SqlBasicFunction(getName(), kind, syntax, deterministic,
          returnTypeInference, getOperandTypeInference(), operandHandler,
          getOperandTypeChecker(), callValidator,
          getFunctionType(), monotonicityInference, dynamic);
    }
  
    /** Returns a copy of this function with a given strategy for inferring
     * the types of its operands. */
    public SqlBasicFunction withOperandTypeInference(
        SqlOperandTypeInference operandTypeInference) {
      return new SqlBasicFunction(getName(), kind, syntax, deterministic,
          getReturnTypeInference(), operandTypeInference, operandHandler,
          getOperandTypeChecker(), callValidator,
          getFunctionType(), monotonicityInference, dynamic);
    }
  
    /** Returns a copy of this function with a given strategy for handling
     * operands. */
    public SqlBasicFunction withOperandHandler(SqlOperandHandler operandHandler) {
      return new SqlBasicFunction(getName(), kind, syntax, deterministic,
          getReturnTypeInference(), getOperandTypeInference(), operandHandler,
          getOperandTypeChecker(), callValidator,
          getFunctionType(), monotonicityInference, dynamic);
    }
    /** Returns a copy of this function with a given determinism. */
    public SqlBasicFunction withDeterministic(boolean deterministic) {
      return new SqlBasicFunction(getName(), kind, syntax, deterministic,
          getReturnTypeInference(), getOperandTypeInference(), operandHandler,
          getOperandTypeChecker(), callValidator,
          getFunctionType(), monotonicityInference, dynamic);
    }
  
    /** Returns a copy of this function with a given strategy for inferring
     * whether a call is monotonic. */
    public SqlBasicFunction withMonotonicityInference(
        Function<SqlOperatorBinding, SqlMonotonicity> monotonicityInference) {
      return new SqlBasicFunction(getName(), kind, syntax, deterministic,
          getReturnTypeInference(), getOperandTypeInference(), operandHandler,
          getOperandTypeChecker(), callValidator,
          getFunctionType(), monotonicityInference, dynamic);
    }
  
    public SqlBasicFunction withValidation(int callValidator) {
      return new SqlBasicFunction(getName(), kind, syntax, deterministic,
          getReturnTypeInference(), getOperandTypeInference(), operandHandler,
          getOperandTypeChecker(), callValidator,
          getFunctionType(), monotonicityInference, dynamic);
    }
  
    public SqlBasicFunction withDynamic(boolean dynamic) {
      return new SqlBasicFunction(getName(), kind, syntax, deterministic,
          getReturnTypeInference(), getOperandTypeInference(), operandHandler,
          getOperandTypeChecker(), callValidator,
          getFunctionType(), monotonicityInference, dynamic);
    }
  
    public SqlBasicFunction withOperandTypeChecker(SqlOperandTypeChecker operandTypeChecker) {
      return new SqlBasicFunction(getName(), kind, syntax, deterministic,
          getReturnTypeInference(), getOperandTypeInference(), operandHandler,
          operandTypeChecker, callValidator,
          getFunctionType(), monotonicityInference, dynamic);
    }
  }
  