/* * 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.datastax.driver.core.exceptions.InvalidTypeException; import com.google.common.collect.ImmutableList; import java.util.Arrays; import java.util.List; /** * A tuple type. * <p/> * A tuple type is a essentially a list of types. */ public class TupleType extends DataType { private final List<DataType> types; private final ProtocolVersion protocolVersion; private volatile CodecRegistry codecRegistry; TupleType(List<DataType> types, ProtocolVersion protocolVersion, CodecRegistry codecRegistry) { super(DataType.Name.TUPLE); this.types = ImmutableList.copyOf(types); this.protocolVersion = protocolVersion; this.codecRegistry = codecRegistry; } /** * Creates a "disconnected" tuple type (<b>you should prefer * {@link Metadata#newTupleType(DataType...) cluster.getMetadata().newTupleType(...)} * whenever possible</b>). * <p/> * This method is only exposed for situations where you don't have a {@code Cluster} * instance available. If you create a type with this method and use it with a * {@code Cluster} later, you won't be able to set tuple fields with custom codecs * registered against the cluster, or you might get errors if the protocol versions don't * match. * * @param protocolVersion the protocol version to use. * @param codecRegistry the codec registry to use. * @param types the types for the tuple type. * @return the newly created tuple type. */ public static TupleType of(ProtocolVersion protocolVersion, CodecRegistry codecRegistry, DataType... types) { return new TupleType(Arrays.asList(types), protocolVersion, codecRegistry); } /** * The (immutable) list of types composing this tuple type. * * @return the (immutable) list of types composing this tuple type. */ public List<DataType> getComponentTypes() { return types; } /** * Returns a new empty value for this tuple type. * * @return an empty (with all component to {@code null}) value for this * user type definition. */ public TupleValue newValue() { return new TupleValue(this); } /** * Returns a new value for this tuple type that uses the provided values * for the components. * <p/> * The numbers of values passed to this method must correspond to the * number of components in this tuple type. The {@code i}th parameter * value will then be assigned to the {@code i}th component of the resulting * tuple value. * * @param values the values to use for the component of the resulting * tuple. * @return a new tuple values based on the provided values. * @throws IllegalArgumentException if the number of {@code values} * provided does not correspond to the number of components in this tuple * type. * @throws InvalidTypeException if any of the provided value is not of * the correct type for the component. */ public TupleValue newValue(Object... values) { if (values.length != types.size()) throw new IllegalArgumentException(String.format("Invalid number of values. Expecting %d but got %d", types.size(), values.length)); TupleValue t = newValue(); for (int i = 0; i < values.length; i++) { DataType dataType = types.get(i); if (values[i] == null) t.setValue(i, null); else t.setValue(i, codecRegistry.codecFor(dataType, values[i]).serialize(values[i], protocolVersion)); } return t; } @Override public boolean isFrozen() { return true; } /** * Return the protocol version that has been used to deserialize * this tuple type, 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 tuple type belongs to, as reported by * {@link ProtocolOptions#getProtocolVersion()}. * * @return the protocol version that has been used to deserialize * this tuple type, or that will be used to serialize it. */ ProtocolVersion getProtocolVersion() { return protocolVersion; } CodecRegistry getCodecRegistry() { return codecRegistry; } void setCodecRegistry(CodecRegistry codecRegistry) { this.codecRegistry = codecRegistry; } @Override public int hashCode() { return Arrays.hashCode(new Object[]{name, types}); } @Override public boolean equals(Object o) { if (!(o instanceof TupleType)) return false; TupleType d = (TupleType) o; return name == d.name && types.equals(d.types); } /** * Return {@code true} if this tuple type contains the given tuple type, * and {@code false} otherwise. * <p/> * A tuple type is said to contain another one * if the latter has fewer components than the former, * but all of them are of the same type. * E.g. the type {@code tuple<int, text>} * is contained by the type {@code tuple<int, text, double>}. * <p/> * A contained type can be seen as a "partial" view * of a containing type, where the missing components * are supposed to be {@code null}. * * @param other the tuple type to compare against the current one * @return {@code true} if this tuple type contains the given tuple type, * and {@code false} otherwise. */ public boolean contains(TupleType other) { if (this.equals(other)) return true; if (other.types.size() > this.types.size()) return false; return types.subList(0, other.types.size()).equals(other.types); } @Override public String toString() { return "frozen<" + asFunctionParameterString() + ">"; } @Override public String asFunctionParameterString() { StringBuilder sb = new StringBuilder(); for (DataType type : types) { sb.append(sb.length() == 0 ? "tuple<" : ", "); sb.append(type.asFunctionParameterString()); } return sb.append(">").toString(); } }