package org.apache.cassandra.db.marshal;
import java.nio.charset.CharacterCodingException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import org.apache.cassandra.cql3.Term;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.serializers.TypeSerializer;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.ByteBufferUtil;
public class DynamicCompositeType extends AbstractCompositeType
{
private static final Logger logger = LoggerFactory.getLogger(DynamicCompositeType.class);
private final Map<Byte, AbstractType<?>> aliases;
private static final Map<Map<Byte, AbstractType<?>>, DynamicCompositeType> instances = new HashMap<Map<Byte, AbstractType<?>>, DynamicCompositeType>();
public static synchronized DynamicCompositeType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
{
return getInstance(parser.getAliasParameters());
}
public static synchronized DynamicCompositeType getInstance(Map<Byte, AbstractType<?>> aliases)
{
DynamicCompositeType dct = instances.get(aliases);
if (dct == null)
{
dct = new DynamicCompositeType(aliases);
instances.put(aliases, dct);
}
return dct;
}
private DynamicCompositeType(Map<Byte, AbstractType<?>> aliases)
{
this.aliases = aliases;
}
protected boolean readIsStatic(ByteBuffer bb)
{
return false;
}
private AbstractType<?> getComparator(ByteBuffer bb)
{
try
{
int header = ByteBufferUtil.readShortLength(bb);
if ((header & 0x8000) == 0)
{
String name = ByteBufferUtil.string(ByteBufferUtil.readBytes(bb, header));
return TypeParser.parse(name);
}
else
{
return aliases.get((byte)(header & 0xFF));
}
}
catch (CharacterCodingException e)
{
throw new RuntimeException(e);
}
}
protected AbstractType<?> getComparator(int i, ByteBuffer bb)
{
return getComparator(bb);
}
protected AbstractType<?> getComparator(int i, ByteBuffer bb1, ByteBuffer bb2)
{
AbstractType<?> comp1 = getComparator(bb1);
AbstractType<?> comp2 = getComparator(bb2);
AbstractType<?> rawComp = comp1;
if ((comp1 instanceof ReversedType) && (comp2 instanceof ReversedType))
{
comp1 = ((ReversedType<?>) comp1).baseType;
comp2 = ((ReversedType<?>) comp2).baseType;
}
if (comp1 != comp2)
{
int cmp = comp1.getClass().getSimpleName().compareTo(comp2.getClass().getSimpleName());
if (cmp != 0)
return cmp < 0 ? FixedValueComparator.alwaysLesserThan : FixedValueComparator.alwaysGreaterThan;
cmp = comp1.getClass().getName().compareTo(comp2.getClass().getName());
if (cmp != 0)
return cmp < 0 ? FixedValueComparator.alwaysLesserThan : FixedValueComparator.alwaysGreaterThan;
}
return rawComp;
}
protected AbstractType<?> getAndAppendComparator(int i, ByteBuffer bb, StringBuilder sb)
{
try
{
int header = ByteBufferUtil.readShortLength(bb);
if ((header & 0x8000) == 0)
{
String name = ByteBufferUtil.string(ByteBufferUtil.readBytes(bb, header));
sb.append(name).append("@");
return TypeParser.parse(name);
}
else
{
sb.append((char)(header & 0xFF)).append("@");
return aliases.get((byte)(header & 0xFF));
}
}
catch (CharacterCodingException e)
{
throw new RuntimeException(e);
}
}
protected ParsedComparator parseComparator(int i, String part)
{
return new DynamicParsedComparator(part);
}
protected AbstractType<?> validateComparator(int i, ByteBuffer bb) throws MarshalException
{
AbstractType<?> comparator = null;
if (bb.remaining() < 2)
throw new MarshalException("Not enough bytes to header of the comparator part of component " + i);
int header = ByteBufferUtil.readShortLength(bb);
if ((header & 0x8000) == 0)
{
if (bb.remaining() < header)
throw new MarshalException("Not enough bytes to read comparator name of component " + i);
ByteBuffer value = ByteBufferUtil.readBytes(bb, header);
String valueStr = null;
try
{
valueStr = ByteBufferUtil.string(value);
comparator = TypeParser.parse(valueStr);
}
catch (CharacterCodingException ce)
{
logger.error("Failed with [{}] when decoding the byte buffer in ByteBufferUtil.string()",
ce.toString());
}
catch (Exception e)
{
logger.error("Failed to parse value string \"{}\" with exception: [{}]",
valueStr, e.toString());
}
}
else
{
comparator = aliases.get((byte)(header & 0xFF));
}
if (comparator == null)
throw new MarshalException("Cannot find comparator for component " + i);
else
return comparator;
}
public ByteBuffer decompose(Object... objects)
{
throw new UnsupportedOperationException();
}
@Override
public boolean isCompatibleWith(AbstractType<?> previous)
{
if (this == previous)
return true;
if (!(previous instanceof DynamicCompositeType))
return false;
DynamicCompositeType cp = (DynamicCompositeType)previous;
if (aliases.size() < cp.aliases.size())
return false;
for (Map.Entry<Byte, AbstractType<?>> entry : cp.aliases.entrySet())
{
AbstractType<?> tprev = entry.getValue();
AbstractType<?> tnew = aliases.get(entry.getKey());
if (tnew == null || tnew != tprev)
return false;
}
return true;
}
private class DynamicParsedComparator implements ParsedComparator
{
final AbstractType<?> type;
final boolean isAlias;
final String comparatorName;
final String remainingPart;
DynamicParsedComparator(String part)
{
String[] splits = part.split("@");
if (splits.length != 2)
throw new IllegalArgumentException("Invalid component representation: " + part);
comparatorName = splits[0];
remainingPart = splits[1];
try
{
AbstractType<?> t = null;
if (comparatorName.length() == 1)
{
t = aliases.get((byte)comparatorName.charAt(0));
}
isAlias = t != null;
if (!isAlias)
{
t = TypeParser.parse(comparatorName);
}
type = t;
}
catch (SyntaxException | ConfigurationException e)
{
throw new IllegalArgumentException(e);
}
}
public AbstractType<?> getAbstractType()
{
return type;
}
public String getRemainingPart()
{
return remainingPart;
}
public int getComparatorSerializedSize()
{
return isAlias ? 2 : 2 + ByteBufferUtil.bytes(comparatorName).remaining();
}
public void serializeComparator(ByteBuffer bb)
{
int header = 0;
if (isAlias)
header = 0x8000 | (((byte)comparatorName.charAt(0)) & 0xFF);
else
header = comparatorName.length();
ByteBufferUtil.writeShortLength(bb, header);
if (!isAlias)
bb.put(ByteBufferUtil.bytes(comparatorName));
}
}
@Override
public String toString()
{
return getClass().getName() + TypeParser.stringifyAliasesParameters(aliases);
}
private static class FixedValueComparator extends AbstractType<Void>
{
public static final FixedValueComparator alwaysLesserThan = new FixedValueComparator(-1);
public static final FixedValueComparator alwaysGreaterThan = new FixedValueComparator(1);
private final int cmp;
public FixedValueComparator(int cmp)
{
super(ComparisonType.CUSTOM);
this.cmp = cmp;
}
public int compareCustom(ByteBuffer v1, ByteBuffer v2)
{
return cmp;
}
@Override
public Void compose(ByteBuffer bytes)
{
throw new UnsupportedOperationException();
}
@Override
public ByteBuffer decompose(Void value)
{
throw new UnsupportedOperationException();
}
public String getString(ByteBuffer bytes)
{
throw new UnsupportedOperationException();
}
public ByteBuffer fromString(String str)
{
throw new UnsupportedOperationException();
}
@Override
public Term fromJSONObject(Object parsed)
{
throw new UnsupportedOperationException();
}
@Override
public String toJSONString(ByteBuffer buffer, ProtocolVersion protocolVersion)
{
throw new UnsupportedOperationException();
}
@Override
public void validate(ByteBuffer bytes)
{
throw new UnsupportedOperationException();
}
public TypeSerializer<Void> getSerializer()
{
throw new UnsupportedOperationException();
}
}
}