package org.apache.cassandra.db;
import java.util.Collections;
import java.util.Iterator;
import com.google.common.base.Objects;
import org.apache.cassandra.db.rows.*;
import org.apache.cassandra.db.rows.EncodingStats;
import org.apache.cassandra.utils.ObjectSizes;
import org.apache.cassandra.utils.memory.AbstractAllocator;
public class MutableDeletionInfo implements DeletionInfo
{
private static final long EMPTY_SIZE = ObjectSizes.measure(new MutableDeletionInfo(0, 0));
private DeletionTime partitionDeletion;
private RangeTombstoneList ranges;
public MutableDeletionInfo(long markedForDeleteAt, int localDeletionTime)
{
this(new DeletionTime(markedForDeleteAt, localDeletionTime == Integer.MIN_VALUE ? Integer.MAX_VALUE : localDeletionTime));
}
public MutableDeletionInfo(DeletionTime partitionDeletion)
{
this(partitionDeletion, null);
}
public MutableDeletionInfo(DeletionTime partitionDeletion, RangeTombstoneList ranges)
{
this.partitionDeletion = partitionDeletion;
this.ranges = ranges;
}
public static MutableDeletionInfo live()
{
return new MutableDeletionInfo(DeletionTime.LIVE);
}
public MutableDeletionInfo mutableCopy()
{
return new MutableDeletionInfo(partitionDeletion, ranges == null ? null : ranges.copy());
}
public MutableDeletionInfo copy(AbstractAllocator allocator)
{
RangeTombstoneList rangesCopy = null;
if (ranges != null)
rangesCopy = ranges.copy(allocator);
return new MutableDeletionInfo(partitionDeletion, rangesCopy);
}
public boolean isLive()
{
return partitionDeletion.isLive() && (ranges == null || ranges.isEmpty());
}
public void add(DeletionTime newInfo)
{
if (newInfo.supersedes(partitionDeletion))
partitionDeletion = newInfo;
}
public void add(RangeTombstone tombstone, ClusteringComparator comparator)
{
if (ranges == null)
ranges = new RangeTombstoneList(comparator, 1);
ranges.add(tombstone);
}
public DeletionInfo add(DeletionInfo newInfo)
{
add(newInfo.getPartitionDeletion());
assert newInfo instanceof MutableDeletionInfo;
RangeTombstoneList newRanges = ((MutableDeletionInfo)newInfo).ranges;
if (ranges == null)
ranges = newRanges == null ? null : newRanges.copy();
else if (newRanges != null)
ranges.addAll(newRanges);
return this;
}
public DeletionTime getPartitionDeletion()
{
return partitionDeletion;
}
public Iterator<RangeTombstone> rangeIterator(boolean reversed)
{
return ranges == null ? Collections.emptyIterator() : ranges.iterator(reversed);
}
public Iterator<RangeTombstone> rangeIterator(Slice slice, boolean reversed)
{
return ranges == null ? Collections.emptyIterator() : ranges.iterator(slice, reversed);
}
public RangeTombstone rangeCovering(Clustering name)
{
return ranges == null ? null : ranges.search(name);
}
public int dataSize()
{
int size = TypeSizes.sizeof(partitionDeletion.markedForDeleteAt());
return size + (ranges == null ? 0 : ranges.dataSize());
}
public boolean hasRanges()
{
return ranges != null && !ranges.isEmpty();
}
public int rangeCount()
{
return hasRanges() ? ranges.size() : 0;
}
public long maxTimestamp()
{
return ranges == null ? partitionDeletion.markedForDeleteAt() : Math.max(partitionDeletion.markedForDeleteAt(), ranges.maxMarkedAt());
}
public boolean mayModify(DeletionInfo delInfo)
{
return partitionDeletion.compareTo(delInfo.getPartitionDeletion()) > 0 || hasRanges();
}
@Override
public String toString()
{
if (ranges == null || ranges.isEmpty())
return String.format("{%s}", partitionDeletion);
else
return String.format("{%s, ranges=%s}", partitionDeletion, rangesAsString());
}
private String rangesAsString()
{
assert !ranges.isEmpty();
StringBuilder sb = new StringBuilder();
ClusteringComparator cc = ranges.comparator();
Iterator<RangeTombstone> iter = rangeIterator(false);
while (iter.hasNext())
{
RangeTombstone i = iter.next();
sb.append(i.deletedSlice().toString(cc));
sb.append('@');
sb.append(i.deletionTime());
}
return sb.toString();
}
public DeletionInfo updateAllTimestamp(long timestamp)
{
if (partitionDeletion.markedForDeleteAt() != Long.MIN_VALUE)
partitionDeletion = new DeletionTime(timestamp, partitionDeletion.localDeletionTime());
if (ranges != null)
ranges.updateAllTimestamp(timestamp);
return this;
}
@Override
public boolean equals(Object o)
{
if(!(o instanceof MutableDeletionInfo))
return false;
MutableDeletionInfo that = (MutableDeletionInfo)o;
return partitionDeletion.equals(that.partitionDeletion) && Objects.equal(ranges, that.ranges);
}
@Override
public final int hashCode()
{
return Objects.hashCode(partitionDeletion, ranges);
}
@Override
public long unsharedHeapSize()
{
return EMPTY_SIZE + partitionDeletion.unsharedHeapSize() + (ranges == null ? 0 : ranges.unsharedHeapSize());
}
public void collectStats(EncodingStats.Collector collector)
{
collector.update(partitionDeletion);
if (ranges != null)
ranges.collectStats(collector);
}
public static Builder builder(DeletionTime partitionLevelDeletion, ClusteringComparator comparator, boolean reversed)
{
return new Builder(partitionLevelDeletion, comparator, reversed);
}
public static class Builder
{
private final MutableDeletionInfo deletion;
private final ClusteringComparator comparator;
private final boolean reversed;
private RangeTombstoneMarker openMarker;
private Builder(DeletionTime partitionLevelDeletion, ClusteringComparator comparator, boolean reversed)
{
this.deletion = new MutableDeletionInfo(partitionLevelDeletion);
this.comparator = comparator;
this.reversed = reversed;
}
public void add(RangeTombstoneMarker marker)
{
if (marker.isClose(reversed))
{
DeletionTime openDeletion = openMarker.openDeletionTime(reversed);
assert marker.closeDeletionTime(reversed).equals(openDeletion);
ClusteringBound open = openMarker.openBound(reversed);
ClusteringBound close = marker.closeBound(reversed);
Slice slice = reversed ? Slice.make(close, open) : Slice.make(open, close);
deletion.add(new RangeTombstone(slice, openDeletion), comparator);
}
if (marker.isOpen(reversed))
{
openMarker = marker;
}
}
public MutableDeletionInfo build()
{
return deletion;
}
}
}