blob: 8b8f45235908d575457e1ec253506a2fe286293c [file] [log] [blame]
/*
* 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.cql3.functions.types;
import java.util.Arrays;
import java.util.List;
import com.google.common.collect.ImmutableList;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.cql3.functions.types.exceptions.InvalidTypeException;
/**
* 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 final 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 {@code
* 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.
*/
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 {@code
* 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;
}
@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();
}
}