/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.github.geophile.erdo.transaction;
import com.github.geophile.erdo.util.Interval;
import java.util.*;
public class TimestampSet implements Iterable<Interval>
{
// Object interface
public synchronized String toString()
{
flush();
StringBuilder buffer = new StringBuilder();
if (empty()) {
buffer.append("-");
} else {
boolean first = true;
for (Interval interval : intervals) {
if (first) {
first = false;
} else {
buffer.append(',');
}
buffer.append(interval.min());
if (interval.max() > interval.min()) {
buffer.append('-');
buffer.append(interval.max());
}
}
}
return buffer.toString();
}
// Iterable interface
public Iterator<Interval> iterator()
{
return intervals().iterator();
}
// TimestampSet interface
public static TimestampSet consolidate(List<TimestampSet> transactionTimestampsList)
{
List<Interval> intervals = new ArrayList<>();
for (TimestampSet transactionTimestamps : transactionTimestampsList) {
transactionTimestamps.flush();
intervals.addAll(transactionTimestamps.intervals);
}
Collections.sort(intervals, INTERVAL_COMPARATOR);
TimestampSet consolidated = new TimestampSet();
for (Interval interval : intervals) {
consolidated.append(interval.min(), interval.max());
}
return consolidated;
}
public TimestampSet()
{
}
public TimestampSet(long timestamp)
{
intervals.add(new Interval(timestamp));
}
public void append(Long timestamp)
{
append(timestamp, timestamp);
}
public synchronized void append(long minTimestamp, long maxTimestamp)
{
if (min == -1L && max == -1L) {
min = minTimestamp;
max = maxTimestamp;
} else if (minTimestamp == max + 1) {
max = maxTimestamp;
} else if (minTimestamp > max + 1) {
intervals.add(new Interval(min, max));
min = minTimestamp;
max = maxTimestamp;
} else if (minTimestamp <= max) {
assert false;
}
}
public TimestampSet union(TimestampSet that)
{
List<TimestampSet> transactionTimestampsList = new ArrayList<>();
transactionTimestampsList.add(this);
transactionTimestampsList.add(that);
return consolidate(transactionTimestampsList);
}
public TimestampSet minus(TimestampSet that)
{
TimestampSet minus = new TimestampSet();
this.flush();
that.flush();
Iterator<Interval> keepIterator = intervals.iterator();
Iterator<Interval> removeIterator = that.intervals.iterator();
Interval keep = keepIterator.hasNext() ? keepIterator.next() : null;
Interval remove = removeIterator.hasNext() ? removeIterator.next() : null;
while (keep != null && remove != null) {
if (keep.min() < remove.min()) {
if (keep.max() >= remove.min()) {
// keep: |-----------------|
// remove: |---...
if (keep.min() <= remove.min() - 1) {
minus.append(keep.min(), remove.min() - 1);
}
keep = new Interval(remove.min(), keep.max());
} else {
// keep: |-----|
// remove: |---...
minus.append(keep.min(), keep.max());
keep = keepIterator.hasNext() ? keepIterator.next() : null;
}
} else { // keep.min() >= remove.min()
if (keep.min() > remove.max()) {
// keep: |-----------------|
// remove: |---|
remove = removeIterator.hasNext() ? removeIterator.next() : null;
} else {
// keep: |-----------------|
// remove: |-------------...
if (remove.max() + 1 <= keep.max()) {
keep = new Interval(remove.max() + 1, keep.max());
} else {
keep = keepIterator.hasNext() ? keepIterator.next() : null;
}
}
}
}
while (keep != null) {
minus.append(keep.min(), keep.max());
keep = keepIterator.hasNext() ? keepIterator.next() : null;
}
return minus;
}
public synchronized boolean empty()
{
flush();
return intervals.isEmpty();
}
public synchronized long minTimestamp()
{
flush();
assert !intervals.isEmpty();
return intervals.get(0).min();
}
public synchronized long maxTimestamp()
{
flush();
assert !intervals.isEmpty();
return intervals.get(intervals.size() - 1).max();
}
public synchronized long maxDeletionTimestamp()
{
flush();
long maxDeletionTimestamp = -1L;
if (!intervals.isEmpty()) {
Interval firstInterval = intervals.get(0);
if (firstInterval.min() == 0) {
maxDeletionTimestamp = firstInterval.max();
}
}
return maxDeletionTimestamp;
}
public synchronized TimestampSet copy()
{
TimestampSet copy = new TimestampSet();
copy.min = min;
copy.max = max;
copy.intervals.addAll(intervals);
return copy;
}
// For use by this package
synchronized List<Interval> intervals()
{
flush();
return new ArrayList<>(intervals);
}
// For use by this class
private void flush()
{
if (min != -1L && max != -1L) {
intervals.add(new Interval(min, max));
min = -1L;
max = -1L;
}
}
// Class state
private static final Comparator<Interval> INTERVAL_COMPARATOR =
new Comparator<Interval>()
{
public int compare(Interval x, Interval y)
{
long xMin = x.min();
long xMax = x.max();
long yMin = y.min();
long yMax = y.max();
if (xMax < yMin) {
return -1;
} else if (xMin > yMax) {
return 1;
} else if (xMin <= yMin && yMax <= xMax ||
yMin <= xMin && xMax <= yMax) {
assert false;
return 0;
} else {
assert false;
return 0;
}
}
};
// Object state
private long min = -1L;
private long max = -1L;
private List<Interval> intervals = new ArrayList<>();
}