ConditionToken.java

/*
 * 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.adapter.arrow;

import com.google.common.collect.ImmutableList;

import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.List;

import static java.util.Objects.requireNonNull;

/**
 * A structured representation of a single Arrow predicate condition.
 *
 * <p>A condition is either unary (e.g. {@code IS NULL}) or binary
 * (e.g. {@code =}, {@code <}). Unary conditions have a field name
 * and operator; binary conditions additionally have a literal value
 * and its type.
 *
 * @see ArrowTranslator
 */
class ConditionToken {
  final String fieldName;
  final Operator operator;
  final @Nullable String value;
  final @Nullable String valueType;

  private ConditionToken(String fieldName, Operator operator,
      @Nullable String value, @Nullable String valueType) {
    this.fieldName = requireNonNull(fieldName, "fieldName");
    this.operator = requireNonNull(operator, "operator");
    this.value = value;
    this.valueType = valueType;
  }

  /** Creates a binary condition token
   * (e.g. {@code intField equal 12 integer}). */
  static ConditionToken binary(String fieldName, Operator operator,
      String value, String valueType) {
    return new ConditionToken(fieldName, operator,
        requireNonNull(value, "value"),
        requireNonNull(valueType, "valueType"));
  }

  /** Creates a unary condition token
   * (e.g. {@code intField isnull}). */
  static ConditionToken unary(String fieldName, Operator operator) {
    return new ConditionToken(fieldName, operator, null, null);
  }

  /** Returns whether this is a binary condition. */
  boolean isBinary() {
    return value != null;
  }

  /** Converts this token to a string list for serialization
   * through code generation.
   *
   * <p>The result is either {@code [fieldName, operator]} for unary
   * conditions or {@code [fieldName, operator, value, valueType]} for
   * binary conditions. */
  List<String> toTokenList() {
    if (isBinary()) {
      return ImmutableList.of(fieldName, operator.token,
          requireNonNull(value, "value"),
          requireNonNull(valueType, "valueType"));
    }
    return ImmutableList.of(fieldName, operator.token);
  }

  /** Creates a {@code ConditionToken} from a serialized string list. */
  static ConditionToken fromTokenList(List<String> tokens) {
    final int size = tokens.size();
    if (size == 4) {
      return binary(tokens.get(0), Operator.of(tokens.get(1)),
          tokens.get(2), tokens.get(3));
    } else if (size == 2) {
      return unary(tokens.get(0), Operator.of(tokens.get(1)));
    }
    throw new IllegalArgumentException("Invalid condition tokens: " + tokens);
  }

  /** Operators supported by the Arrow adapter filter representation. */
  enum Operator {
    IS_NULL("isnull"),
    IS_NOT_NULL("isnotnull"),
    IS_TRUE("istrue"),
    IS_FALSE("isfalse"),
    IS_NOT_TRUE("isnottrue"),
    IS_NOT_FALSE("isnotfalse"),
    EQUAL("equal"),
    NOT_EQUAL("not_equal"),
    LESS_THAN("less_than"),
    LESS_THAN_OR_EQUAL("less_than_or_equal_to"),
    GREATER_THAN("greater_than"),
    GREATER_THAN_OR_EQUAL("greater_than_or_equal_to"),
    LIKE("like");

    final String token;

    Operator(String token) {
      this.token = token;
    }

    static Operator of(String token) {
      for (Operator operator : values()) {
        if (operator.token.equals(token)) {
          return operator;
        }
      }
      throw new UnsupportedOperationException(
          "Unsupported Arrow filter operator: " + token);
    }
  }
}