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.parser.SqlParserPos;
  import org.apache.calcite.sql.util.SqlVisitor;
  import org.apache.calcite.sql.validate.SqlValidator;
  import org.apache.calcite.sql.validate.SqlValidatorScope;
  import org.apache.calcite.util.Litmus;
  
  import com.google.common.collect.ImmutableList;
  
  import org.checkerframework.checker.nullness.qual.Nullable;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Iterator;
  import java.util.List;
  import java.util.ListIterator;
  import java.util.RandomAccess;
  import java.util.function.Consumer;
  
  import static org.apache.calcite.linq4j.Nullness.castNonNull;
  
  import static java.util.Objects.requireNonNull;
  
  /**
   * A <code>SqlNodeList</code> is a list of {@link SqlNode}s. It is also a
   * {@link SqlNode}, so may appear in a parse tree.
   *
   * @see SqlNode#toList()
   */
  public class SqlNodeList extends SqlNode implements List<SqlNode>, RandomAccess {
    //~ Static fields/initializers ---------------------------------------------
  
    /**
     * An immutable, empty SqlNodeList.
     */
    public static final SqlNodeList EMPTY =
        new SqlNodeList(ImmutableList.of(), SqlParserPos.ZERO);
  
    /**
     * A SqlNodeList that has a single element that is an empty list.
     */
    public static final SqlNodeList SINGLETON_EMPTY =
        new SqlNodeList(ImmutableList.of(EMPTY), SqlParserPos.ZERO);
  
    /**
     * A SqlNodeList that has a single element that is a star identifier.
     */
    public static final SqlNodeList SINGLETON_STAR =
        new SqlNodeList(ImmutableList.of(SqlIdentifier.STAR), SqlParserPos.ZERO);
  
    //~ Instance fields --------------------------------------------------------
  
    // Sometimes null values are present in the list, however, it is assumed that callers would
    // perform all the required null-checks.
    private final List<@Nullable SqlNode> list;
  
    //~ Constructors -----------------------------------------------------------
  
    /** Creates a SqlNodeList with a given backing list.
     *
     * <p>Because SqlNodeList implements {@link RandomAccess}, the backing list
     * should allow O(1) access to elements. */
    private SqlNodeList(SqlParserPos pos, List<@Nullable SqlNode> list) {
      super(pos);
      this.list = requireNonNull(list, "list");
    }
  
    /**
     * Creates a SqlNodeList that is initially empty.
     */
    public SqlNodeList(SqlParserPos pos) {
      this(pos, new ArrayList<>());
    }
  
    /**
     * Creates a <code>SqlNodeList</code> containing the nodes in <code>
     * list</code>. The list is copied, but the nodes in it are not.
     */
    public SqlNodeList(
        Collection<? extends @Nullable SqlNode> collection,
        SqlParserPos pos) {
      this(pos, new ArrayList<@Nullable SqlNode>(collection));
    }
  
    /**
     * Creates a SqlNodeList with a given backing list.
     * Does not copy the list.
     */
    public static SqlNodeList of(SqlParserPos pos, List<@Nullable SqlNode> list) {
      return new SqlNodeList(pos, list);
    }
  
    //~ Methods ----------------------------------------------------------------
  
    // List, Collection and Iterable methods
  
  
    @Override public int hashCode() {
      return list.hashCode();
    }
  
    @Override public boolean equals(@Nullable Object o) {
      return this == o
          || o instanceof SqlNodeList && list.equals(((SqlNodeList) o).list)
          || o instanceof List && list.equals(o);
    }
  
    @Override public boolean isEmpty() {
      return list.size() == 0;
    }
  
    @Override public int size() {
      return list.size();
    }
  
    @Override public void forEach(Consumer<? super SqlNode> action) {
      //noinspection RedundantCast
      ((List<SqlNode>) list).forEach(action);
    }
  
    @SuppressWarnings("return.type.incompatible")
    @Override public Iterator</*Nullable*/ SqlNode> iterator() {
      return list.iterator();
    }
  
    @SuppressWarnings("return.type.incompatible")
    @Override public ListIterator</*Nullable*/ SqlNode> listIterator() {
      return list.listIterator();
    }
  
    @SuppressWarnings("return.type.incompatible")
    @Override public ListIterator</*Nullable*/ SqlNode> listIterator(int index) {
      return list.listIterator(index);
    }
  
    @SuppressWarnings("return.type.incompatible")
    @Override public List</*Nullable*/ SqlNode> subList(int fromIndex, int toIndex) {
      return list.subList(fromIndex, toIndex);
    }
  
    @SuppressWarnings("return.type.incompatible")
    @Override public /*Nullable*/ SqlNode get(int n) {
      return list.get(n);
    }
  
    @Override public SqlNode set(int n, @Nullable SqlNode node) {
      return castNonNull(list.set(n, node));
    }
  
    @Override public boolean contains(@Nullable Object o) {
      return list.contains(o);
    }
  
    @Override public boolean containsAll(Collection<?> c) {
      return list.containsAll(c);
    }
  
    @Override public int indexOf(@Nullable Object o) {
      return list.indexOf(o);
    }
  
    @Override public int lastIndexOf(@Nullable Object o) {
      return list.lastIndexOf(o);
    }
  
    @SuppressWarnings("return.type.incompatible")
    @Override public Object[] toArray() {
      // Per JDK specification, must return an Object[] not SqlNode[]; see e.g.
      // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652
      return list.toArray();
    }
  
    @SuppressWarnings("return.type.incompatible")
    @Override public <T> @Nullable T[] toArray(T @Nullable [] a) {
      return list.toArray(a);
    }
  
    @Override public boolean add(@Nullable SqlNode node) {
      return list.add(node);
    }
  
    @Override public void add(int index, @Nullable SqlNode element) {
      list.add(index, element);
    }
  
    @Override public boolean addAll(Collection<? extends @Nullable SqlNode> c) {
      return list.addAll(c);
    }
  
    @Override public boolean addAll(int index, Collection<? extends @Nullable SqlNode> c) {
      return list.addAll(index, c);
    }
  
    @Override public void clear() {
      list.clear();
    }
  
    @Override public boolean remove(@Nullable Object o) {
      return list.remove(o);
    }
  
    @Override public SqlNode remove(int index) {
      return castNonNull(list.remove(index));
    }
  
    @Override public boolean removeAll(Collection<?> c) {
      return list.removeAll(c);
    }
  
    @Override public boolean retainAll(Collection<?> c) {
      return list.retainAll(c);
    }
  
    // SqlNodeList-specific methods
  
    public List<@Nullable SqlNode> getList() {
      return list;
    }
  
    @Override public SqlNodeList clone(SqlParserPos pos) {
      return new SqlNodeList(list, pos);
    }
  
    @Override public void unparse(
        SqlWriter writer,
        int leftPrec,
        int rightPrec) {
      final SqlWriter.FrameTypeEnum frameType =
          (leftPrec > 0 || rightPrec > 0)
              ? SqlWriter.FrameTypeEnum.PARENTHESES
              : SqlWriter.FrameTypeEnum.SIMPLE;
      writer.list(frameType, SqlWriter.COMMA, this);
    }
  
    @Deprecated // to be removed before 2.0
    void commaList(SqlWriter writer) {
      unparse(writer, 0, 0);
    }
  
    @Deprecated // to be removed before 2.0
    void andOrList(SqlWriter writer, SqlBinaryOperator sepOp) {
      writer.list(SqlWriter.FrameTypeEnum.WHERE_LIST, sepOp, this);
    }
  
    @Override public void validate(SqlValidator validator, SqlValidatorScope scope) {
      for (SqlNode child : list) {
        if (child == null) {
          continue;
        }
        child.validate(validator, scope);
      }
    }
  
    @Override public <R> R accept(SqlVisitor<R> visitor) {
      return visitor.visit(this);
    }
  
    @Override public boolean equalsDeep(@Nullable SqlNode node, Litmus litmus) {
      if (!(node instanceof SqlNodeList)) {
        return litmus.fail("{} != {}", this, node);
      }
      SqlNodeList that = (SqlNodeList) node;
      return SqlNode.equalDeep(list, that.list,
          litmus.withMessageArgs("{} != {}", this, node));
    }
  
    public static boolean isEmptyList(final SqlNode node) {
      return node instanceof SqlNodeList
          && ((SqlNodeList) node).isEmpty();
    }
  
    public static SqlNodeList of(SqlNode node1) {
      final List<@Nullable SqlNode> list = new ArrayList<>(1);
      list.add(node1);
      return new SqlNodeList(SqlParserPos.ZERO, list);
    }
  
    public static SqlNodeList of(SqlNode node1, SqlNode node2) {
      final List<@Nullable SqlNode> list = new ArrayList<>(2);
      list.add(node1);
      list.add(node2);
      return new SqlNodeList(SqlParserPos.ZERO, list);
    }
  
    public static SqlNodeList of(SqlNode node1, SqlNode node2, @Nullable SqlNode... nodes) {
      final List<@Nullable SqlNode> list = new ArrayList<>(nodes.length + 2);
      list.add(node1);
      list.add(node2);
      Collections.addAll(list, nodes);
      return new SqlNodeList(SqlParserPos.ZERO, list);
    }
  
    @Override public void validateExpr(SqlValidator validator, SqlValidatorScope scope) {
      // While a SqlNodeList is not always a valid expression, this
      // implementation makes that assumption. It just validates the members
      // of the list.
      //
      // One example where this is valid is the IN operator. The expression
      //
      //    empno IN (10, 20)
      //
      // results in a call with operands
      //
      //    {  SqlIdentifier({"empno"}),
      //       SqlNodeList(SqlLiteral(10), SqlLiteral(20))  }
  
      for (SqlNode node : list) {
        if (node == null) {
          continue;
        }
        node.validateExpr(validator, scope);
      }
    }
  }
  