/*
* 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.cassandra.cql;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.AsciiType;
import org.apache.cassandra.db.marshal.FloatType;
import org.apache.cassandra.db.marshal.IntegerType;
import org.apache.cassandra.db.marshal.LexicalUUIDType;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.serializers.MarshalException;
/** A term parsed from a CQL statement. */
public class Term
{
private final String text;
private final TermType type;
private Integer bindIndex = -1;
public Term(String text, TermType type)
{
this.text = text == null ? "" : text;
this.type = type;
}
/**
* Create new Term instance from a string, and an integer that corresponds
* with the token ID from CQLParser.
*
* @param text the text representation of the term.
* @param type the term's type as an integer token ID.
*/
public Term(String text, int type)
{
this(text == null ? "" : text, TermType.forInt(type));
}
public Term(long value, TermType type)
{
this(String.valueOf(value), type);
}
protected Term()
{
this("", TermType.STRING);
}
public Term(String text, int type, int index)
{
this(text, type);
this.bindIndex = index;
}
/**
* Returns the text parsed to create this term.
*
* @return the string term acquired from a CQL statement.
*/
public String getText()
{
return text;
}
/**
* Returns the typed value, serialized to a ByteBuffer according to a
* comparator/validator.
*
* @return a ByteBuffer of the value.
* @throws InvalidRequestException if unable to coerce the string to its type.
*/
public ByteBuffer getByteBuffer(AbstractType<?> validator, List<ByteBuffer> variables) throws InvalidRequestException
{
try
{
if (!isBindMarker()) return validator.fromStringCQL2(text);
// must be a marker term so check for a CqlBindValue stored in the term
if (bindIndex == null)
throw new AssertionError("a marker Term was encountered with no index value");
return variables.get(bindIndex);
}
catch (MarshalException e)
{
throw new InvalidRequestException(e.getMessage());
}
}
/**
* Returns the typed value, serialized to a ByteBuffer.
*
* @return a ByteBuffer of the value.
* @throws InvalidRequestException if unable to coerce the string to its type.
*/
public ByteBuffer getByteBuffer() throws InvalidRequestException
{
switch (type)
{
case STRING:
return AsciiType.instance.fromString(text);
case INTEGER:
return IntegerType.instance.fromString(text);
case UUID:
// we specifically want the Lexical class here, not "UUIDType," because we're supposed to have
// a uuid-shaped string here, and UUIDType also accepts integer or date strings (and turns them into version 1 uuids).
return LexicalUUIDType.instance.fromString(text);
case FLOAT:
return FloatType.instance.fromString(text);
}
// FIXME: handle scenario that should never happen
return null;
}
/**
* Obtain the term's type.
*
* @return the type
*/
public TermType getType()
{
return type;
}
public String toString()
{
return String.format("Term(%s, type=%s)", getText(), type);
}
public boolean isBindMarker()
{
return type==TermType.QMARK;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((text == null) ? 0 : text.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Term other = (Term) obj;
if (type==TermType.QMARK) return false; // markers are never equal
if (text == null)
{
if (other.text != null)
return false;
} else if (!text.equals(other.text))
return false;
if (type != other.type)
return false;
return true;
}
}
enum TermType
{
STRING, INTEGER, UUID, FLOAT, QMARK;
static TermType forInt(int type)
{
if ((type == CqlParser.STRING_LITERAL) || (type == CqlParser.IDENT))
return STRING;
else if (type == CqlParser.INTEGER)
return INTEGER;
else if (type == CqlParser.UUID)
return UUID;
else if (type == CqlParser.FLOAT)
return FLOAT;
else if (type == CqlParser.QMARK)
return QMARK;
// FIXME: handled scenario that should never occur.
return null;
}
}