package org.apache.cassandra.db.marshal;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.google.common.collect.ImmutableList;
import org.apache.cassandra.cql3.ColumnIdentifier;
import org.apache.cassandra.cql3.Operator;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.io.util.DataOutputBufferFixed;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.utils.ByteBufferUtil;
public class CompositeType extends AbstractCompositeType
{
public static final int STATIC_MARKER = 0xFFFF;
public final List<AbstractType<?>> types;
private static final ConcurrentMap<List<AbstractType<?>>, CompositeType> instances = new ConcurrentHashMap<List<AbstractType<?>>, CompositeType>();
public static CompositeType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
{
return getInstance(parser.getTypeParameters());
}
public static CompositeType getInstance(AbstractType... types)
{
return getInstance(Arrays.<AbstractType<?>>asList(types));
}
protected boolean readIsStatic(ByteBuffer bb)
{
return readStatic(bb);
}
private static boolean readStatic(ByteBuffer bb)
{
if (bb.remaining() < 2)
return false;
int header = ByteBufferUtil.getShortLength(bb, bb.position());
if ((header & 0xFFFF) != STATIC_MARKER)
return false;
ByteBufferUtil.readShortLength(bb);
return true;
}
public static CompositeType getInstance(List<AbstractType<?>> types)
{
assert types != null && !types.isEmpty();
CompositeType ct = instances.get(types);
if (ct == null)
{
ct = new CompositeType(types);
CompositeType previous = instances.putIfAbsent(types, ct);
if (previous != null)
{
ct = previous;
}
}
return ct;
}
protected CompositeType(List<AbstractType<?>> types)
{
this.types = ImmutableList.copyOf(types);
}
protected AbstractType<?> getComparator(int i, ByteBuffer bb)
{
try
{
return types.get(i);
}
catch (IndexOutOfBoundsException e)
{
throw new RuntimeException("Cannot get comparator " + i + " in " + this + ". "
+ "This might due to a mismatch between the schema and the data read", e);
}
}
protected AbstractType<?> getComparator(int i, ByteBuffer bb1, ByteBuffer bb2)
{
return getComparator(i, bb1);
}
protected AbstractType<?> getAndAppendComparator(int i, ByteBuffer bb, StringBuilder sb)
{
return types.get(i);
}
protected ParsedComparator parseComparator(int i, String part)
{
return new StaticParsedComparator(types.get(i), part);
}
protected AbstractType<?> validateComparator(int i, ByteBuffer bb) throws MarshalException
{
if (i >= types.size())
throw new MarshalException("Too many bytes for comparator");
return types.get(i);
}
public ByteBuffer decompose(Object... objects)
{
assert objects.length == types.size();
ByteBuffer[] serialized = new ByteBuffer[objects.length];
for (int i = 0; i < objects.length; i++)
{
ByteBuffer buffer = ((AbstractType) types.get(i)).decompose(objects[i]);
serialized[i] = buffer;
}
return build(serialized);
}
@Override
public ByteBuffer[] split(ByteBuffer name)
{
ByteBuffer[] l = new ByteBuffer[types.size()];
ByteBuffer bb = name.duplicate();
readStatic(bb);
int i = 0;
while (bb.remaining() > 0)
{
l[i++] = ByteBufferUtil.readBytesWithShortLength(bb);
bb.get();
}
return i == l.length ? l : Arrays.copyOfRange(l, 0, i);
}
public static List<ByteBuffer> splitName(ByteBuffer name)
{
List<ByteBuffer> l = new ArrayList<>();
ByteBuffer bb = name.duplicate();
readStatic(bb);
while (bb.remaining() > 0)
{
l.add(ByteBufferUtil.readBytesWithShortLength(bb));
bb.get();
}
return l;
}
public static byte lastEOC(ByteBuffer name)
{
return name.get(name.limit() - 1);
}
public static ByteBuffer extractComponent(ByteBuffer bb, int idx)
{
bb = bb.duplicate();
readStatic(bb);
int i = 0;
while (bb.remaining() > 0)
{
ByteBuffer c = ByteBufferUtil.readBytesWithShortLength(bb);
if (i == idx)
return c;
bb.get();
++i;
}
return null;
}
public ByteBuffer extractLastComponent(ByteBuffer bb)
{
int idx = types.get(types.size() - 1) instanceof ColumnToCollectionType ? types.size() - 2 : types.size() - 1;
return extractComponent(bb, idx);
}
public static boolean isStaticName(ByteBuffer bb)
{
return bb.remaining() >= 2 && (ByteBufferUtil.getShortLength(bb, bb.position()) & 0xFFFF) == STATIC_MARKER;
}
@Override
public int componentsCount()
{
return types.size();
}
@Override
public List<AbstractType<?>> getComponents()
{
return types;
}
@Override
public boolean isCompatibleWith(AbstractType<?> previous)
{
if (this == previous)
return true;
if (!(previous instanceof CompositeType))
return false;
CompositeType cp = (CompositeType)previous;
if (types.size() < cp.types.size())
return false;
for (int i = 0; i < cp.types.size(); i++)
{
AbstractType tprev = cp.types.get(i);
AbstractType tnew = types.get(i);
if (!tnew.isCompatibleWith(tprev))
return false;
}
return true;
}
@Override
public boolean isValueCompatibleWithInternal(AbstractType<?> otherType)
{
if (this == otherType)
return true;
if (!(otherType instanceof CompositeType))
return false;
CompositeType cp = (CompositeType) otherType;
if (types.size() < cp.types.size())
return false;
for (int i = 0; i < cp.types.size(); i++)
{
AbstractType tprev = cp.types.get(i);
AbstractType tnew = types.get(i);
if (!tnew.isValueCompatibleWith(tprev))
return false;
}
return true;
}
private static class StaticParsedComparator implements ParsedComparator
{
final AbstractType<?> type;
final String part;
StaticParsedComparator(AbstractType<?> type, String part)
{
this.type = type;
this.part = part;
}
public AbstractType<?> getAbstractType()
{
return type;
}
public String getRemainingPart()
{
return part;
}
public int getComparatorSerializedSize()
{
return 0;
}
public void serializeComparator(ByteBuffer bb) {}
}
@Override
public String toString()
{
return getClass().getName() + TypeParser.stringifyTypeParameters(types);
}
public Builder builder()
{
return new Builder(this);
}
public Builder builder(boolean isStatic)
{
return new Builder(this, isStatic);
}
public static ByteBuffer build(ByteBuffer... buffers)
{
return build(false, buffers);
}
public static ByteBuffer build(boolean isStatic, ByteBuffer... buffers)
{
int totalLength = isStatic ? 2 : 0;
for (ByteBuffer bb : buffers)
totalLength += 2 + bb.remaining() + 1;
ByteBuffer out = ByteBuffer.allocate(totalLength);
if (isStatic)
out.putShort((short)STATIC_MARKER);
for (ByteBuffer bb : buffers)
{
ByteBufferUtil.writeShortLength(out, bb.remaining());
int toCopy = bb.remaining();
ByteBufferUtil.arrayCopy(bb, bb.position(), out, out.position(), toCopy);
out.position(out.position() + toCopy);
out.put((byte) 0);
}
out.flip();
return out;
}
public static class Builder
{
private final CompositeType composite;
private final List<ByteBuffer> components;
private final byte[] endOfComponents;
private int serializedSize;
private final boolean isStatic;
public Builder(CompositeType composite)
{
this(composite, false);
}
public Builder(CompositeType composite, boolean isStatic)
{
this(composite, new ArrayList<>(composite.types.size()), new byte[composite.types.size()], isStatic);
}
private Builder(CompositeType composite, List<ByteBuffer> components, byte[] endOfComponents, boolean isStatic)
{
assert endOfComponents.length == composite.types.size();
this.composite = composite;
this.components = components;
this.endOfComponents = endOfComponents;
this.isStatic = isStatic;
if (isStatic)
serializedSize = 2;
}
private Builder(Builder b)
{
this(b.composite, new ArrayList<>(b.components), Arrays.copyOf(b.endOfComponents, b.endOfComponents.length), b.isStatic);
this.serializedSize = b.serializedSize;
}
public Builder add(ByteBuffer bb)
{
if (components.size() >= composite.types.size())
throw new IllegalStateException("Composite column is already fully constructed");
components.add(bb);
serializedSize += 3 + bb.remaining();
return this;
}
public Builder add(ColumnIdentifier name)
{
return add(name.bytes);
}
public int componentCount()
{
return components.size();
}
public int remainingCount()
{
return composite.types.size() - components.size();
}
public ByteBuffer get(int i)
{
return components.get(i);
}
public ByteBuffer build()
{
try (DataOutputBuffer out = new DataOutputBufferFixed(serializedSize))
{
if (isStatic)
out.writeShort(STATIC_MARKER);
for (int i = 0; i < components.size(); i++)
{
ByteBufferUtil.writeWithShortLength(components.get(i), out);
out.write(endOfComponents[i]);
}
return ByteBuffer.wrap(out.getData(), 0, out.getLength());
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
public ByteBuffer buildAsEndOfRange()
{
if (components.isEmpty())
return ByteBufferUtil.EMPTY_BYTE_BUFFER;
ByteBuffer bb = build();
bb.put(bb.remaining() - 1, (byte)1);
return bb;
}
public ByteBuffer buildForRelation(Operator op)
{
int current = components.size() - 1;
switch (op)
{
case LT:
endOfComponents[current] = (byte) -1;
break;
case GT:
case LTE:
endOfComponents[current] = (byte) 1;
break;
default:
endOfComponents[current] = (byte) 0;
break;
}
return build();
}
public Builder copy()
{
return new Builder(this);
}
public ByteBuffer getComponent(int i)
{
if (i >= components.size())
throw new IllegalArgumentException();
return components.get(i);
}
}
}