com.datastax.oss.driver.api.core.CqlIdentifier Maven / Gradle / Ivy
/*
* 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 com.datastax.oss.driver.api.core;
import com.datastax.oss.driver.internal.core.util.Strings;
import com.datastax.oss.driver.shaded.guava.common.base.Preconditions;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Locale;
import net.jcip.annotations.Immutable;
/**
* The identifier of CQL element (keyspace, table, column, etc).
*
* It has two representations:
*
*
* - the "CQL" form, which is how you would type the identifier in a CQL query. It is
* case-insensitive unless enclosed in double quotation marks; in addition, identifiers that
* contain special characters (anything other than alphanumeric and underscore), or match CQL
* keywords, must be double-quoted (with inner double quotes escaped as {@code ""}).
*
- the "internal" form, which is how the name is stored in Cassandra system tables. It is
* lower-case for case-sensitive identifiers, and in the exact case for case-sensitive
* identifiers.
*
*
* Examples:
*
*
* Create statement Case-sensitive? CQL id Internal id
* CREATE TABLE t(foo int PRIMARY KEY) No foo foo
* CREATE TABLE t(Foo int PRIMARY KEY) No foo foo
* CREATE TABLE t("Foo" int PRIMARY KEY) Yes "Foo" Foo
* CREATE TABLE t("foo bar" int PRIMARY KEY) Yes "foo bar" foo bar
* CREATE TABLE t("foo""bar" int PRIMARY KEY) Yes "foo""bar" foo"bar
* CREATE TABLE t("create" int PRIMARY KEY) Yes (reserved keyword) "create" create
*
*
* This class provides a common representation and avoids any ambiguity about which form the
* identifier is in. Driver clients will generally want to create instances from the CQL form with
* {@link #fromCql(String)}.
*
* There is no internal caching; if you reuse the same identifiers often, consider caching them
* in your application.
*/
@Immutable
public class CqlIdentifier implements Serializable {
private static final long serialVersionUID = 1;
// IMPLEMENTATION NOTES:
// This is used internally, and for all API methods where the overhead of requiring the client to
// create an instance is acceptable (metadata, statement.getKeyspace, etc.)
// One exception is named getters, where we keep raw strings with the 3.x rules.
/** Creates an identifier from its {@link CqlIdentifier CQL form}. */
@NonNull
public static CqlIdentifier fromCql(@NonNull String cql) {
Preconditions.checkNotNull(cql, "cql must not be null");
final String internal;
if (Strings.isDoubleQuoted(cql)) {
internal = Strings.unDoubleQuote(cql);
} else {
internal = cql.toLowerCase(Locale.ROOT);
Preconditions.checkArgument(
!Strings.needsDoubleQuotes(internal), "Invalid CQL form [%s]: needs double quotes", cql);
}
return fromInternal(internal);
}
/** Creates an identifier from its {@link CqlIdentifier internal form}. */
@NonNull
public static CqlIdentifier fromInternal(@NonNull String internal) {
Preconditions.checkNotNull(internal, "internal must not be null");
return new CqlIdentifier(internal);
}
/** @serial */
private final String internal;
private CqlIdentifier(String internal) {
this.internal = internal;
}
/**
* Returns the identifier in the "internal" format.
*
* @return the identifier in its exact case, unquoted.
*/
@NonNull
public String asInternal() {
return this.internal;
}
/**
* Returns the identifier in a format appropriate for concatenation in a CQL query.
*
* @param pretty if {@code true}, use the shortest possible representation: if the identifier is
* case-insensitive, an unquoted, lower-case string, otherwise the double-quoted form. If
* {@code false}, always use the double-quoted form (this is slightly more efficient since we
* don't need to inspect the string).
*/
@NonNull
public String asCql(boolean pretty) {
if (pretty) {
return Strings.needsDoubleQuotes(internal) ? Strings.doubleQuote(internal) : internal;
} else {
return Strings.doubleQuote(internal);
}
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
} else if (other instanceof CqlIdentifier) {
CqlIdentifier that = (CqlIdentifier) other;
return this.internal.equals(that.internal);
} else {
return false;
}
}
@Override
public int hashCode() {
return internal.hashCode();
}
@Override
public String toString() {
return internal;
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
Preconditions.checkNotNull(internal, "internal must not be null");
}
}