| /* |
| * 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.Objects; |
| import java.util.RandomAccess; |
| import java.util.function.Consumer; |
| |
| import static org.apache.calcite.linq4j.Nullness.castNonNull; |
| |
| /** |
| * 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 = Objects.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.isEmpty(); |
| } |
| |
| @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); |
| } |
| } |
| } |