/*
* 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.db.composites;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Comparator;
import org.apache.cassandra.db.DeletionInfo;
import org.apache.cassandra.db.RangeTombstone;
import org.apache.cassandra.db.RowIndexEntry;
import org.apache.cassandra.db.TypeSizes;
import org.apache.cassandra.db.filter.ColumnSlice;
import org.apache.cassandra.db.filter.SliceQueryFilter;
import org.apache.cassandra.db.marshal.AbstractCompositeType;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.io.ISerializer;
import org.apache.cassandra.io.IVersionedSerializer;
import org.apache.cassandra.utils.ByteBufferUtil;
import static org.apache.cassandra.io.sstable.IndexHelper.IndexInfo;
public abstract class AbstractCType implements CType
{
private final Comparator<Composite> reverseComparator;
private final Comparator<IndexInfo> indexComparator;
private final Comparator<IndexInfo> indexReverseComparator;
private final Serializer serializer;
private final ISerializer<IndexInfo> indexSerializer;
private final IVersionedSerializer<ColumnSlice> sliceSerializer;
private final IVersionedSerializer<SliceQueryFilter> sliceQueryFilterSerializer;
private final DeletionInfo.Serializer deletionInfoSerializer;
private final RangeTombstone.Serializer rangeTombstoneSerializer;
private final RowIndexEntry.Serializer rowIndexEntrySerializer;
protected AbstractCType()
{
reverseComparator = new Comparator<Composite>()
{
public int compare(Composite c1, Composite c2)
{
return AbstractCType.this.compare(c2, c1);
}
};
indexComparator = new Comparator<IndexInfo>()
{
public int compare(IndexInfo o1, IndexInfo o2)
{
return AbstractCType.this.compare(o1.lastName, o2.lastName);
}
};
indexReverseComparator = new Comparator<IndexInfo>()
{
public int compare(IndexInfo o1, IndexInfo o2)
{
return AbstractCType.this.compare(o1.firstName, o2.firstName);
}
};
serializer = new Serializer(this);
indexSerializer = new IndexInfo.Serializer(this);
sliceSerializer = new ColumnSlice.Serializer(this);
sliceQueryFilterSerializer = new SliceQueryFilter.Serializer(this);
deletionInfoSerializer = new DeletionInfo.Serializer(this);
rangeTombstoneSerializer = new RangeTombstone.Serializer(this);
rowIndexEntrySerializer = new RowIndexEntry.Serializer(this);
}
public int compare(Composite c1, Composite c2)
{
if (c1 == null)
return c2 == null ? 0 : -1;
ByteBuffer previous = null;
int i;
int minSize = Math.min(c1.size(), c2.size());
for (i = 0; i < minSize; i++)
{
AbstractType<?> comparator = subtype(i);
ByteBuffer value1 = c1.get(i);
ByteBuffer value2 = c2.get(i);
int cmp = comparator.compareCollectionMembers(value1, value2, previous);
if (cmp != 0)
return cmp;
previous = value1;
}
if (c1.size() == c2.size())
{
if (c1.eoc() != c2.eoc())
{
switch (c1.eoc())
{
case START: return -1;
case END: return 1;
case NONE: return c2.eoc() == Composite.EOC.START ? 1 : -1;
}
}
return 0;
}
if (i == c1.size())
{
return c1.eoc() == Composite.EOC.END ? 1 : -1;
}
else
{
assert i == c2.size();
return c2.eoc() == Composite.EOC.END ? -1 : 1;
}
}
public void validate(Composite name)
{
ByteBuffer previous = null;
for (int i = 0; i < name.size(); i++)
{
AbstractType<?> comparator = subtype(i);
ByteBuffer value = name.get(i);
comparator.validateCollectionMember(value, previous);
previous = value;
}
}
public boolean isCompatibleWith(CType previous)
{
if (this == previous)
return true;
// Extending with new components is fine, shrinking is not
if (size() < previous.size())
return false;
for (int i = 0; i < previous.size(); i++)
{
AbstractType<?> tprev = previous.subtype(i);
AbstractType<?> tnew = subtype(i);
if (!tnew.isCompatibleWith(tprev))
return false;
}
return true;
}
public String getString(Composite c)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < c.size(); i++)
{
if (i > 0)
sb.append(":");
sb.append(AbstractCompositeType.escape(subtype(i).getString(c.get(i))));
}
switch (c.eoc())
{
case START:
sb.append(":_");
break;
case END:
sb.append(":!");
break;
}
return sb.toString();
}
public Composite make(Object... components)
{
if (components.length > size())
throw new IllegalArgumentException("Too many components, max is " + size());
CBuilder builder = builder();
for (int i = 0; i < components.length; i++)
{
Object obj = components[i];
if (obj instanceof ByteBuffer)
builder.add((ByteBuffer)obj);
else
builder.add(obj);
}
return builder.build();
}
public CType.Serializer serializer()
{
return serializer;
}
public Comparator<Composite> reverseComparator()
{
return reverseComparator;
}
public Comparator<IndexInfo> indexComparator()
{
return indexComparator;
}
public Comparator<IndexInfo> indexReverseComparator()
{
return indexReverseComparator;
}
public ISerializer<IndexInfo> indexSerializer()
{
return indexSerializer;
}
public IVersionedSerializer<ColumnSlice> sliceSerializer()
{
return sliceSerializer;
}
public IVersionedSerializer<SliceQueryFilter> sliceQueryFilterSerializer()
{
return sliceQueryFilterSerializer;
}
public DeletionInfo.Serializer deletionInfoSerializer()
{
return deletionInfoSerializer;
}
public RangeTombstone.Serializer rangeTombstoneSerializer()
{
return rangeTombstoneSerializer;
}
public RowIndexEntry.Serializer rowIndexEntrySerializer()
{
return rowIndexEntrySerializer;
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null)
return false;
if (!getClass().equals(o.getClass()))
return false;
CType c = (CType)o;
if (size() != c.size())
return false;
for (int i = 0; i < size(); i++)
{
if (!subtype(i).equals(c.subtype(i)))
return false;
}
return true;
}
@Override
public int hashCode()
{
int h = 31;
for (int i = 0; i < size(); i++)
h += subtype(i).hashCode();
return h + getClass().hashCode();
}
@Override
public String toString()
{
return asAbstractType().toString();
}
protected static ByteBuffer sliceBytes(ByteBuffer bb, int offs, int length)
{
ByteBuffer copy = bb.duplicate();
copy.position(offs);
copy.limit(offs + length);
return copy;
}
protected static void checkRemaining(ByteBuffer bb, int offs, int length)
{
if (offs + length > bb.limit())
throw new IllegalArgumentException("Not enough bytes");
}
private static class Serializer implements CType.Serializer
{
private final CType type;
public Serializer(CType type)
{
this.type = type;
}
public void serialize(Composite c, DataOutput out) throws IOException
{
ByteBufferUtil.writeWithShortLength(c.toByteBuffer(), out);
}
public Composite deserialize(DataInput in) throws IOException
{
return type.fromByteBuffer(ByteBufferUtil.readWithShortLength(in));
}
public long serializedSize(Composite c, TypeSizes type)
{
return type.sizeofWithShortLength(c.toByteBuffer());
}
public void skip(DataInput in) throws IOException
{
ByteBufferUtil.skipShortLength(in);
}
}
}