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.util;
  
  import org.apache.calcite.runtime.ConsList;
  import org.apache.calcite.sql.SqlFunction;
  import org.apache.calcite.sql.SqlFunctionCategory;
  import org.apache.calcite.sql.SqlIdentifier;
  import org.apache.calcite.sql.SqlOperator;
  import org.apache.calcite.sql.SqlOperatorTable;
  import org.apache.calcite.sql.SqlSyntax;
  import org.apache.calcite.sql.fun.LibraryOperator;
  import org.apache.calcite.sql.fun.SqlLibrary;
  import org.apache.calcite.sql.validate.SqlNameMatcher;
  import org.apache.calcite.util.Util;
  
  import com.google.common.collect.ImmutableList;
  
  import org.checkerframework.checker.nullness.qual.Nullable;
  
  import java.lang.reflect.Field;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  
  /**
   * ReflectiveSqlOperatorTable implements the {@link SqlOperatorTable} interface
   * by reflecting the public fields of a subclass.
   */
  public abstract class ReflectiveSqlOperatorTable
      extends SqlOperatorTables.IndexedSqlOperatorTable
      implements SqlOperatorTable {
    public static final String IS_NAME = "INFORMATION_SCHEMA";
  
    //~ Instance fields --------------------------------------------------------
  
    //~ Constructors -----------------------------------------------------------
  
    protected ReflectiveSqlOperatorTable() {
      // Initialize using an empty list of operators. After construction is
      // complete we will call init() and set the true operator list.
      super(ImmutableList.of());
    }
  
    //~ Methods ----------------------------------------------------------------
  
    /**
     * Performs post-constructor initialization of an operator table. It can't
     * be part of the constructor, because the subclass constructor needs to
     * complete first.
     */
    public final SqlOperatorTable init() {
      // Use reflection to register the expressions stored in public fields.
      final List<SqlOperator> list = new ArrayList<>();
      for (Field field : getClass().getFields()) {
        try {
          final Object o = field.get(this);
          if (o instanceof SqlOperator) {
            // Fields do not need the LibraryOperator tag, but if they have it,
            // we index them only if they contain STANDARD library.
            LibraryOperator libraryOperator =
                field.getAnnotation(LibraryOperator.class);
            if (libraryOperator != null) {
              if (Arrays.stream(libraryOperator.libraries())
                  .noneMatch(library -> library == SqlLibrary.STANDARD)) {
                continue;
              }
            }
  
            list.add((SqlOperator) o);
          }
        } catch (IllegalArgumentException | IllegalAccessException e) {
          throw Util.throwAsRuntime(Util.causeOrSelf(e));
        }
      }
      setOperators(buildIndex(list));
      return this;
    }
  
    @Override public void lookupOperatorOverloads(SqlIdentifier opName,
        @Nullable SqlFunctionCategory category, SqlSyntax syntax,
        List<SqlOperator> operatorList, SqlNameMatcher nameMatcher) {
      // NOTE jvs 3-Mar-2005:  ignore category until someone cares
  
      String simpleName;
      if (opName.names.size() > 1) {
        if (opName.names.get(opName.names.size() - 2).equals(IS_NAME)) {
          // per SQL99 Part 2 Section 10.4 Syntax Rule 7.b.ii.1
          simpleName = Util.last(opName.names);
        } else {
          return;
        }
      } else {
        simpleName = opName.getSimple();
      }
  
      lookUpOperators(simpleName, nameMatcher.isCaseSensitive(), op -> {
        if (op.getSyntax() == syntax) {
          operatorList.add(op);
        } else if (syntax == SqlSyntax.FUNCTION
            && op instanceof SqlFunction) {
          // this special case is needed for operators like CAST,
          // which are treated as functions but have special syntax
          operatorList.add(op);
        }
      });
  
      // REVIEW jvs 1-Jan-2005:  why is this extra lookup required?
      // Shouldn't it be covered by search above?
      switch (syntax) {
      case BINARY:
      case PREFIX:
      case POSTFIX:
        lookUpOperators(simpleName, nameMatcher.isCaseSensitive(), extra -> {
          // REVIEW: should only search operators added during this method?
          if (extra != null && !operatorList.contains(extra)) {
            operatorList.add(extra);
          }
        });
        break;
      default:
        break;
      }
    }
  
    /**
     * Registers a function or operator in the table.
     *
     * @deprecated This table is designed to be initialized from the fields of
     * a class, and adding operators is not efficient
     */
    @Deprecated
    public void register(SqlOperator op) {
      // Rebuild the immutable collections with their current contents plus one.
      setOperators(buildIndex(ConsList.of(op, getOperatorList())));
    }
  }
  