blob: caaaddde313d0d6f940a78e40130a19e1b26e1ed [file] [log] [blame]
/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde 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.eigenbase.sql;
import java.util.*;
import org.eigenbase.sql.fun.*;
import org.eigenbase.sql.parser.*;
import org.eigenbase.sql.util.*;
import org.eigenbase.sql.validate.*;
/**
* A <code>SqlNodeList</code> is a list of {@link SqlNode}s. It is also a {@link
* SqlNode}, so may appear in a parse tree.
*/
public class SqlNodeList
extends SqlNode
implements Iterable<SqlNode>
{
//~ Static fields/initializers ---------------------------------------------
/**
* An immutable, empty SqlNodeList.
*/
public static final SqlNodeList Empty =
new SqlNodeList(SqlParserPos.ZERO) {
public void add(SqlNode node)
{
throw new UnsupportedOperationException();
}
};
//~ Instance fields --------------------------------------------------------
private final List<SqlNode> list;
//~ Constructors -----------------------------------------------------------
/**
* Creates an empty <code>SqlNodeList</code>.
*/
public SqlNodeList(SqlParserPos pos)
{
super(pos);
list = new ArrayList<SqlNode>();
}
/**
* 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 collection,
SqlParserPos pos)
{
super(pos);
list = new ArrayList<SqlNode>(collection);
}
//~ Methods ----------------------------------------------------------------
// implement Iterable<SqlNode>
public Iterator<SqlNode> iterator()
{
return list.iterator();
}
public List<SqlNode> getList()
{
return list;
}
public void add(SqlNode node)
{
list.add(node);
}
public SqlNode clone(SqlParserPos pos)
{
return new SqlNodeList(
list,
pos);
}
public SqlNode get(int n)
{
return list.get(n);
}
public SqlNode set(int n, SqlNode node)
{
return list.set(n, node);
}
public int size()
{
return list.size();
}
public void unparse(
SqlWriter writer,
int leftPrec,
int rightPrec)
{
final SqlWriter.Frame frame =
((leftPrec > 0) || (rightPrec > 0)) ? writer.startList("(", ")")
: writer.startList("", "");
commaList(writer);
writer.endList(frame);
}
void commaList(SqlWriter writer)
{
// The precedence of the comma operator if low but not zero. For
// instance, this ensures parentheses in
// select x, (select * from foo order by z), y from t
for (int i = 0; i < list.size(); i++) {
SqlNode node = list.get(i);
writer.sep(",");
node.unparse(writer, 2, 3);
}
}
void andOrList(SqlWriter writer, SqlKind sepKind)
{
SqlBinaryOperator sepOp =
(sepKind == SqlKind.AND) ? SqlStdOperatorTable.andOperator
: SqlStdOperatorTable.orOperator;
for (int i = 0; i < list.size(); i++) {
SqlNode node = list.get(i);
writer.sep(sepKind.name(), false);
// The precedence pulling on the LHS of a node is the
// right-precedence of the separator operator, except at the start
// of the list; similarly for the RHS of a node. If the operator
// has left precedence 4 and right precedence 5, the precedences
// in a 3-node list will look as follows:
// 0 <- node1 -> 4 5 <- node2 -> 4 5 <- node3 -> 0
int lprec = (i == 0) ? 0 : sepOp.getRightPrec();
int rprec = (i == (list.size() - 1)) ? 0 : sepOp.getLeftPrec();
node.unparse(writer, lprec, rprec);
}
}
void _andOrList(SqlWriter writer, SqlKind sepKind)
{
SqlBinaryOperator sepOp =
(sepKind == SqlKind.AND) ? SqlStdOperatorTable.andOperator
: SqlStdOperatorTable.orOperator;
for (int i = 0; i < list.size(); i++) {
SqlNode node = list.get(i);
writer.sep(sepKind.name(), false);
int lprec = (i == 0) ? 0 : sepOp.getRightPrec();
int rprec = (i == (list.size() - 1)) ? 0 : sepOp.getLeftPrec();
node.unparse(writer, lprec, rprec);
}
}
public void validate(SqlValidator validator, SqlValidatorScope scope)
{
for (SqlNode child : list) {
child.validate(validator, scope);
}
}
public <R> R accept(SqlVisitor<R> visitor)
{
return visitor.visit(this);
}
public boolean equalsDeep(SqlNode node, boolean fail)
{
if (!(node instanceof SqlNodeList)) {
assert !fail : this + "!=" + node;
return false;
}
SqlNodeList that = (SqlNodeList) node;
if (this.size() != that.size()) {
assert !fail : this + "!=" + node;
return false;
}
for (int i = 0; i < list.size(); i++) {
SqlNode thisChild = list.get(i);
final SqlNode thatChild = that.list.get(i);
if (!thisChild.equalsDeep(thatChild, fail)) {
return false;
}
}
return true;
}
public SqlNode [] toArray()
{
return list.toArray(new SqlNode[list.size()]);
}
public static boolean isEmptyList(final SqlNode node)
{
if (node instanceof SqlNodeList) {
if (0 == ((SqlNodeList) node).size()) {
return true;
}
}
return false;
}
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) {
node.validateExpr(validator, scope);
}
}
}
// End SqlNodeList.java