com.datastax.driver.core.UserType Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cassandra-driver-core Show documentation
Show all versions of cassandra-driver-core Show documentation
A driver for Apache Cassandra 1.2+ that works exclusively with the Cassandra Query Language version 3
(CQL3) and Cassandra's binary protocol.
/*
* Copyright (C) 2012-2015 DataStax Inc.
*
* Licensed 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.driver.core;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import java.util.*;
/**
* A User Defined Type (UDT).
*
* A UDT is a essentially a named collection of fields (with a name and a type).
*/
public class UserType extends DataType implements Iterable {
static final String TYPE_NAME = "type_name";
private static final String COLS_NAMES = "field_names";
static final String COLS_TYPES = "field_types";
private final String keyspace;
private final String typeName;
private final ProtocolVersion protocolVersion;
private volatile CodecRegistry codecRegistry;
// Note that we don't expose the order of fields, from an API perspective this is a map
// of String->Field, but internally we care about the order because the serialization format
// of UDT expects a particular order.
final Field[] byIdx;
// For a given name, we can only have one field with that name, so we don't need a int[] in
// practice. However, storing one element arrays save allocations in UDTValue.getAllIndexesOf
// implementation.
final Map byName;
UserType(String keyspace, String typeName, Collection fields, ProtocolVersion protocolVersion, CodecRegistry codecRegistry) {
super(DataType.Name.UDT);
this.keyspace = keyspace;
this.typeName = typeName;
this.protocolVersion = protocolVersion;
// codecRegistry can be null, if this object is being constructed from a response message
// see Responses.Result.Rows.Metadata.decode()
this.codecRegistry = codecRegistry;
this.byIdx = fields.toArray(new Field[fields.size()]);
ImmutableMap.Builder builder = new ImmutableMap.Builder();
for (int i = 0; i < byIdx.length; i++)
builder.put(byIdx[i].getName(), new int[]{i});
this.byName = builder.build();
}
static UserType build(KeyspaceMetadata ksm, Row row, VersionNumber version, Cluster cluster, Map userTypes) {
ProtocolVersion protocolVersion = cluster.getConfiguration().getProtocolOptions().getProtocolVersion();
CodecRegistry codecRegistry = cluster.getConfiguration().getCodecRegistry();
String keyspace = row.getString(KeyspaceMetadata.KS_NAME);
String name = row.getString(TYPE_NAME);
List fieldNames = row.getList(COLS_NAMES, String.class);
List fieldTypes = row.getList(COLS_TYPES, String.class);
List fields = new ArrayList(fieldNames.size());
for (int i = 0; i < fieldNames.size(); i++) {
DataType fieldType;
if (version.getMajor() >= 3.0) {
fieldType = DataTypeCqlNameParser.parse(fieldTypes.get(i), cluster, ksm.getName(), userTypes, ksm.userTypes, false, false);
} else {
fieldType = DataTypeClassNameParser.parseOne(fieldTypes.get(i), protocolVersion, codecRegistry);
}
fields.add(new Field(fieldNames.get(i), fieldType));
}
return new UserType(keyspace, name, fields, protocolVersion, codecRegistry);
}
/**
* Returns a new empty value for this user type definition.
*
* @return an empty value for this user type definition.
*/
public UDTValue newValue() {
return new UDTValue(this);
}
/**
* The name of the keyspace this UDT is part of.
*
* @return the name of the keyspace this UDT is part of.
*/
public String getKeyspace() {
return keyspace;
}
/**
* The name of this user type.
*
* @return the name of this user type.
*/
public String getTypeName() {
return typeName;
}
/**
* Returns the number of fields in this UDT.
*
* @return the number of fields in this UDT.
*/
public int size() {
return byIdx.length;
}
/**
* Returns whether this UDT contains a given field.
*
* @param name the name to check. Note that {@code name} obey the usual
* CQL identifier rules: it should be quoted if it denotes a case sensitive
* identifier (you can use {@link Metadata#quote} for the quoting).
* @return {@code true} if this UDT contains a field named {@code name},
* {@code false} otherwise.
*/
public boolean contains(String name) {
return byName.containsKey(Metadata.handleId(name));
}
/**
* Returns an iterator over the fields of this UDT.
*
* @return an iterator over the fields of this UDT.
*/
@Override
public Iterator iterator() {
return Iterators.forArray(byIdx);
}
/**
* Returns the names of the fields of this UDT.
*
* @return the names of the fields of this UDT as a collection.
*/
public Collection getFieldNames() {
return byName.keySet();
}
/**
* Returns the type of a given field.
*
* @param name the name of the field. Note that {@code name} obey the usual
* CQL identifier rules: it should be quoted if it denotes a case sensitive
* identifier (you can use {@link Metadata#quote} for the quoting).
* @return the type of field {@code name} if this UDT has a field of this
* name, {@code null} otherwise.
* @throws IllegalArgumentException if {@code name} is not a field of this
* UDT definition.
*/
public DataType getFieldType(String name) {
int[] idx = byName.get(Metadata.handleId(name));
if (idx == null)
throw new IllegalArgumentException(name + " is not a field defined in this definition");
return byIdx[idx[0]].getType();
}
@Override
public boolean isFrozen() {
return true;
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + keyspace.hashCode();
result = 31 * result + typeName.hashCode();
result = 31 * result + Arrays.hashCode(byIdx);
return result;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof UserType))
return false;
UserType other = (UserType) o;
// Note: we don't test byName because it's redundant with byIdx in practice,
// but also because the map holds 'int[]' which don't have proper equal.
return name.equals(other.name)
&& keyspace.equals(other.keyspace)
&& typeName.equals(other.typeName)
&& Arrays.equals(byIdx, other.byIdx);
}
/**
* Returns a CQL query representing this user type in human readable form.
*
* This method is equivalent to {@link #asCQLQuery} but the ouptut is
* formatted to be human readable (for some definition of human readable).
*
* @return the CQL query representing this user type.
*/
public String exportAsString() {
return asCQLQuery(true);
}
/**
* Returns a CQL query representing this user type.
*
* This method returns a single 'CREATE TYPE' query corresponding
* to this UDT definition.
*
* Note that the returned string is a single line; the returned query
* is not formatted in any way.
*
* @return the 'CREATE TYPE' query corresponding to this user type.
* @see #exportAsString
*/
public String asCQLQuery() {
return asCQLQuery(false);
}
/**
* Return the protocol version that has been used to deserialize
* this UDT, or that will be used to serialize it.
* In most cases this should be the version
* currently in use by the cluster instance
* that this UDT belongs to, as reported by
* {@link ProtocolOptions#getProtocolVersion()}.
*
* @return the protocol version that has been used to deserialize
* this UDT, or that will be used to serialize it.
*/
ProtocolVersion getProtocolVersion() {
return protocolVersion;
}
CodecRegistry getCodecRegistry() {
return codecRegistry;
}
void setCodecRegistry(CodecRegistry codecRegistry) {
this.codecRegistry = codecRegistry;
}
private String asCQLQuery(boolean formatted) {
StringBuilder sb = new StringBuilder();
sb.append("CREATE TYPE ").append(Metadata.escapeId(keyspace)).append('.').append(Metadata.escapeId(typeName)).append(" (");
TableMetadata.newLine(sb, formatted);
for (int i = 0; i < byIdx.length; i++) {
sb.append(TableMetadata.spaces(4, formatted)).append(byIdx[i]);
if (i < byIdx.length - 1)
sb.append(',');
TableMetadata.newLine(sb, formatted);
}
return sb.append(");").toString();
}
@Override
public String toString() {
return "frozen<" + Metadata.escapeId(getKeyspace()) + '.' + Metadata.escapeId(getTypeName()) + ">";
}
@Override
public String asFunctionParameterString() {
return Metadata.escapeId(getTypeName());
}
/**
* A UDT field.
*/
public static class Field {
private final String name;
private final DataType type;
Field(String name, DataType type) {
this.name = name;
this.type = type;
}
/**
* Returns the name of the field.
*
* @return the name of the field.
*/
public String getName() {
return name;
}
/**
* Returns the type of the field.
*
* @return the type of the field.
*/
public DataType getType() {
return type;
}
@Override
public final int hashCode() {
return Arrays.hashCode(new Object[]{name, type});
}
@Override
public final boolean equals(Object o) {
if (!(o instanceof Field))
return false;
Field other = (Field) o;
return name.equals(other.name)
&& type.equals(other.type);
}
@Override
public String toString() {
return Metadata.escapeId(name) + ' ' + type;
}
}
/**
* A "shallow" definition of a UDT that only contains the keyspace and type name, without any information
* about the type's structure.
*
* This is used for internal dependency analysis only, and never returned to the client.
*
* @since 3.0.0
*/
static class Shallow extends DataType {
final String keyspaceName;
final String typeName;
Shallow(String keyspaceName, String typeName) {
super(Name.UDT);
this.keyspaceName = keyspaceName;
this.typeName = typeName;
}
@Override
public boolean isFrozen() {
return false;
}
}
}