package org.apache.cassandra.db.rows;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.Objects;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.db.DeletionPurger;
import org.apache.cassandra.db.TypeSizes;
import org.apache.cassandra.db.context.CounterContext;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.CollectionType;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.memory.AbstractAllocator;
public abstract class AbstractCell extends Cell
{
protected AbstractCell(ColumnDefinition column)
{
super(column);
}
public boolean isCounterCell()
{
return !isTombstone() && column.isCounterColumn();
}
public boolean isLive(int nowInSec)
{
return localDeletionTime() == NO_DELETION_TIME || (ttl() != NO_TTL && nowInSec < localDeletionTime());
}
public boolean isTombstone()
{
return localDeletionTime() != NO_DELETION_TIME && ttl() == NO_TTL;
}
public boolean isExpiring()
{
return ttl() != NO_TTL;
}
public Cell markCounterLocalToBeCleared()
{
if (!isCounterCell())
return this;
ByteBuffer value = value();
ByteBuffer marked = CounterContext.instance().markLocalToBeCleared(value);
return marked == value ? this : new BufferCell(column, timestamp(), ttl(), localDeletionTime(), marked, path());
}
public Cell purge(DeletionPurger purger, int nowInSec)
{
if (!isLive(nowInSec))
{
if (purger.shouldPurge(timestamp(), localDeletionTime()))
return null;
if (isExpiring())
{
return BufferCell.tombstone(column, timestamp(), localDeletionTime() - ttl(), path()).purge(purger, nowInSec);
}
}
return this;
}
public Cell copy(AbstractAllocator allocator)
{
CellPath path = path();
return new BufferCell(column, timestamp(), ttl(), localDeletionTime(), allocator.clone(value()), path == null ? null : path.copy(allocator));
}
public Cell updateAllTimestamp(long newTimestamp)
{
return new BufferCell(column, isTombstone() ? newTimestamp - 1 : newTimestamp, ttl(), localDeletionTime(), value(), path());
}
public int dataSize()
{
CellPath path = path();
return TypeSizes.sizeof(timestamp())
+ TypeSizes.sizeof(ttl())
+ TypeSizes.sizeof(localDeletionTime())
+ value().remaining()
+ (path == null ? 0 : path.dataSize());
}
public void digest(MessageDigest digest)
{
if (isCounterCell())
{
CounterContext.instance().updateDigest(digest, value());
}
else
{
digest.update(value().duplicate());
}
FBUtilities.updateWithLong(digest, timestamp());
FBUtilities.updateWithInt(digest, ttl());
FBUtilities.updateWithBoolean(digest, isCounterCell());
if (path() != null)
path().digest(digest);
}
public void validate()
{
if (ttl() < 0)
throw new MarshalException("A TTL should not be negative");
if (localDeletionTime() < 0)
throw new MarshalException("A local deletion time should not be negative");
if (isExpiring() && localDeletionTime() == NO_DELETION_TIME)
throw new MarshalException("Shoud not have a TTL without an associated local deletion time");
column().validateCell(this);
}
public long maxTimestamp()
{
return timestamp();
}
@Override
public boolean equals(Object other)
{
if (this == other)
return true;
if(!(other instanceof Cell))
return false;
Cell that = (Cell)other;
return this.column().equals(that.column())
&& this.isCounterCell() == that.isCounterCell()
&& this.timestamp() == that.timestamp()
&& this.ttl() == that.ttl()
&& this.localDeletionTime() == that.localDeletionTime()
&& Objects.equals(this.value(), that.value())
&& Objects.equals(this.path(), that.path());
}
@Override
public int hashCode()
{
return Objects.hash(column(), isCounterCell(), timestamp(), ttl(), localDeletionTime(), value(), path());
}
@Override
public String toString()
{
if (isCounterCell())
return String.format("[%s=%d ts=%d]", column().name, CounterContext.instance().total(value()), timestamp());
AbstractType<?> type = column().type;
if (type instanceof CollectionType && type.isMultiCell())
{
CollectionType ct = (CollectionType)type;
return String.format("[%s[%s]=%s %s]",
column().name,
ct.nameComparator().getString(path().get(0)),
ct.valueComparator().getString(value()),
livenessInfoString());
}
if (isTombstone())
return String.format("[%s=<tombstone> %s]", column().name, livenessInfoString());
else
return String.format("[%s=%s %s]", column().name, type.getString(value()), livenessInfoString());
}
private String livenessInfoString()
{
if (isExpiring())
return String.format("ts=%d ttl=%d ldt=%d", timestamp(), ttl(), localDeletionTime());
else if (isTombstone())
return String.format("ts=%d ldt=%d", timestamp(), localDeletionTime());
else
return String.format("ts=%d", timestamp());
}
}