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.fun;
  
  import org.apache.calcite.rel.type.RelDataType;
  import org.apache.calcite.sql.SqlBinaryOperator;
  import org.apache.calcite.sql.SqlCall;
  import org.apache.calcite.sql.SqlCallBinding;
  import org.apache.calcite.sql.SqlKind;
  import org.apache.calcite.sql.SqlOperandCountRange;
  import org.apache.calcite.sql.SqlOperator;
  import org.apache.calcite.sql.SqlWriter;
  import org.apache.calcite.sql.type.InferTypes;
  import org.apache.calcite.sql.type.OperandTypes;
  import org.apache.calcite.sql.type.ReturnTypes;
  import org.apache.calcite.sql.type.SqlOperandCountRanges;
  import org.apache.calcite.sql.type.SqlTypeUtil;
  
  /**
   * An operator describing the <code>~</code> operator.
   *
   * <p> Syntax: <code>src-value [!] ~ [*] pattern-value</code>
   */
  public class SqlPosixRegexOperator extends SqlBinaryOperator {
    // ~ Instance fields --------------------------------------------------------
  
    private final boolean caseSensitive;
    private final boolean negated;
    private final String operatorString;
  
    // ~ Constructors -----------------------------------------------------------
  
    /**
     * Creates a SqlPosixRegexOperator.
     *
     * @param name    Operator name
     * @param kind    Kind
     * @param negated Whether this is '!~' or '!~*'
     */
    SqlPosixRegexOperator(
        String name,
        SqlKind kind,
        boolean caseSensitive,
        boolean negated) {
      super(
          name,
          kind,
          32,
          true,
          ReturnTypes.BOOLEAN_NULLABLE,
          InferTypes.FIRST_KNOWN,
          OperandTypes.STRING_SAME_SAME_SAME);
      this.caseSensitive = caseSensitive;
      this.negated = negated;
      final StringBuilder sb = new StringBuilder(3);
      if (this.negated) {
        sb.append("!");
      }
      sb.append("~");
      if (!this.caseSensitive) {
        sb.append("*");
      }
      this.operatorString = sb.toString();
    }
  
    // ~ Methods ----------------------------------------------------------------
  
    @Override public SqlOperator not() {
      return of(!negated, caseSensitive);
    }
  
    private static SqlOperator of(boolean negated, boolean ignoreCase) {
      if (ignoreCase) {
        return negated
            ? SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_SENSITIVE
            : SqlStdOperatorTable.POSIX_REGEX_CASE_SENSITIVE;
      } else {
        return negated
            ? SqlStdOperatorTable.NEGATED_POSIX_REGEX_CASE_INSENSITIVE
            : SqlStdOperatorTable.POSIX_REGEX_CASE_INSENSITIVE;
      }
    }
  
    @Override public SqlOperandCountRange getOperandCountRange() {
      return SqlOperandCountRanges.of(2);
    }
  
    @Override public boolean checkOperandTypes(
        SqlCallBinding callBinding,
        boolean throwOnFailure) {
      int operandCount = callBinding.getOperandCount();
      if (operandCount != 2) {
        throw new AssertionError(
            "Unexpected number of args to " + callBinding.getCall() + ": " + operandCount);
      }
  
      RelDataType op1Type = callBinding.getOperandType(0);
      RelDataType op2Type = callBinding.getOperandType(1);
  
      if (!SqlTypeUtil.isComparable(op1Type, op2Type)) {
        throw new AssertionError(
            "Incompatible first two operand types " + op1Type + " and " + op2Type);
      }
  
      return SqlTypeUtil.isCharTypeComparable(
          callBinding,
          callBinding.operands().subList(0, 2),
          throwOnFailure);
    }
  
    @Override public void unparse(
        SqlWriter writer,
        SqlCall call,
        int leftPrec,
        int rightPrec) {
      final SqlWriter.Frame frame = writer.startList("", "");
      call.operand(0).unparse(writer, getLeftPrec(), getRightPrec());
  
      writer.print(this.operatorString);
      writer.print(" ");
  
      call.operand(1).unparse(writer, getLeftPrec(), getRightPrec());
      writer.endList(frame);
    }
  
    /**
     * Returns whether this operator matches the case of its operands.
     *
     * @return whether this operator matches the case of its operands
     */
    public boolean isCaseSensitive() {
      return caseSensitive;
    }
  
    /**
     * Returns whether this is 'NOT' variant of an operator.
     *
     * @see #not()
     */
    public boolean isNegated() {
      return negated;
    }
  }
  