/*
* 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.eigenbase.sql;
import java.util.*;
import org.eigenbase.sql.parser.*;
import org.eigenbase.sql.util.*;
import org.eigenbase.sql.validate.*;
import org.eigenbase.util.Util;
import com.google.common.collect.ImmutableList;
/**
* A <code>SqlIdentifier</code> is an identifier, possibly compound.
*/
public class SqlIdentifier extends SqlNode {
//~ Instance fields --------------------------------------------------------
/**
* Array of the components of this compound identifier.
*
* <p>It's convenient to have this member public, and it's convenient to
* have this member not-final, but it's a shame it's public and not-final.
* If you assign to this member, please use
* {@link #setNames(java.util.List, java.util.List)}.
* And yes, we'd like to make identifiers immutable one day.
*/
public ImmutableList<String> names;
/**
* This identifier's collation (if any).
*/
final SqlCollation collation;
/**
* A list of the positions of the components of compound identifiers.
*/
private ImmutableList<SqlParserPos> componentPositions;
//~ Constructors -----------------------------------------------------------
/**
* Creates a compound identifier, for example <code>foo.bar</code>.
*
* @param names Parts of the identifier, length ≥ 1
*/
public SqlIdentifier(
List<String> names,
SqlCollation collation,
SqlParserPos pos,
List<SqlParserPos> componentPositions) {
super(pos);
this.names = ImmutableList.copyOf(names);
this.collation = collation;
this.componentPositions = componentPositions == null ? null
: ImmutableList.copyOf(componentPositions);
for (String name : names) {
assert name != null;
}
}
public SqlIdentifier(List<String> names, SqlParserPos pos) {
this(names, null, pos, null);
}
/**
* Creates a simple identifier, for example <code>foo</code>, with a
* collation.
*/
public SqlIdentifier(
String name,
SqlCollation collation,
SqlParserPos pos) {
this(ImmutableList.of(name), collation, pos, null);
}
/**
* Creates a simple identifier, for example <code>foo</code>.
*/
public SqlIdentifier(
String name,
SqlParserPos pos) {
this(ImmutableList.of(name), null, pos, null);
}
//~ Methods ----------------------------------------------------------------
public SqlKind getKind() {
return SqlKind.IDENTIFIER;
}
public SqlNode clone(SqlParserPos pos) {
return new SqlIdentifier(names, collation, pos, componentPositions);
}
public String toString() {
return Util.sepList(names, ".");
}
/**
* Modifies the components of this identifier and their positions.
*
* @param names Names of components
* @param poses Positions of components
*/
public void setNames(List<String> names, List<SqlParserPos> poses) {
this.names = ImmutableList.copyOf(names);
this.componentPositions = poses == null ? null
: ImmutableList.copyOf(poses);
}
/**
* Returns the position of the <code>i</code>th component of a compound
* identifier, or the position of the whole identifier if that information
* is not present.
*
* @param i Ordinal of component.
* @return Position of i'th component
*/
public SqlParserPos getComponentParserPosition(int i) {
assert (i >= 0) && (i < names.size());
return (componentPositions == null) ? getParserPosition()
: componentPositions.get(i);
}
/**
* Copies names and components from another identifier. Does not modify the
* cross-component parser position.
*
* @param other identifier from which to copy
*/
public void assignNamesFrom(SqlIdentifier other) {
setNames(other.names, other.componentPositions);
}
/**
* Creates an identifier which contains only the <code>ordinal</code>th
* component of this compound identifier. It will have the correct {@link
* SqlParserPos}, provided that detailed position information is available.
*/
public SqlIdentifier getComponent(int ordinal) {
return new SqlIdentifier(names.get(ordinal),
getComponentParserPosition(ordinal));
}
public void unparse(
SqlWriter writer,
int leftPrec,
int rightPrec) {
final SqlWriter.Frame frame =
writer.startList(SqlWriter.FrameTypeEnum.IDENTIFIER);
for (String name : names) {
writer.sep(".");
if (name.equals("*")) {
writer.print(name);
} else {
writer.identifier(name);
}
}
if (null != collation) {
collation.unparse(writer, leftPrec, rightPrec);
}
writer.endList(frame);
}
public void validate(SqlValidator validator, SqlValidatorScope scope) {
validator.validateIdentifier(this, scope);
}
public void validateExpr(SqlValidator validator, SqlValidatorScope scope) {
// First check for builtin functions which don't have parentheses,
// like "LOCALTIME".
SqlCall call =
SqlUtil.makeCall(
validator.getOperatorTable(),
this);
if (call != null) {
validator.validateCall(call, scope);
return;
}
validator.validateIdentifier(this, scope);
}
public boolean equalsDeep(SqlNode node, boolean fail) {
if (!(node instanceof SqlIdentifier)) {
assert !fail : this + "!=" + node;
return false;
}
SqlIdentifier that = (SqlIdentifier) node;
if (this.names.size() != that.names.size()) {
assert !fail : this + "!=" + node;
return false;
}
for (int i = 0; i < names.size(); i++) {
if (!this.names.get(i).equals(that.names.get(i))) {
assert !fail : this + "!=" + node;
return false;
}
}
return true;
}
public <R> R accept(SqlVisitor<R> visitor) {
return visitor.visit(this);
}
public SqlCollation getCollation() {
return collation;
}
public String getSimple() {
assert names.size() == 1;
return names.get(0);
}
/**
* Returns whether this identifier is a star, such as "*" or "foo.bar.*".
*/
public boolean isStar() {
return Util.last(names).equals("*");
}
/**
* Returns whether this is a simple identifier. "FOO" is simple; "*",
* "FOO.*" and "FOO.BAR" are not.
*/
public boolean isSimple() {
return (names.size() == 1) && !names.get(0).equals("*");
}
public SqlMonotonicity getMonotonicity(SqlValidatorScope scope) {
// First check for builtin functions which don't have parentheses,
// like "LOCALTIME".
final SqlValidator validator = scope.getValidator();
SqlCall call =
SqlUtil.makeCall(
validator.getOperatorTable(),
this);
if (call != null) {
return call.getMonotonicity(scope);
}
final SqlIdentifier fqId = scope.fullyQualify(this);
final SqlValidatorNamespace ns =
SqlValidatorUtil.lookup(scope, Util.skipLast(fqId.names));
return ns.resolve().getMonotonicity(Util.last(fqId.names));
}
}
// End SqlIdentifier.java