// This software is released into the Public Domain. See copying.txt for details.
package org.openstreetmap.osmosis.core.filter.common;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Implements the IdTracker interface using a combination of BitSet and ListId trackers. It breaks
* the overall address space into segments and automatically switches between implementations for each
* chunk depending on which is more efficient.
*
* @author Brett Henderson
*/
public class DynamicIdTracker implements IdTracker {
/**
* Defines the number of ids managed by a single segment.
*/
/* package */ static final int SEGMENT_SIZE = 1024;
private List<DynamicIdTrackerSegment> segments;
/**
* Creates a new instance.
*/
public DynamicIdTracker() {
segments = new ArrayList<DynamicIdTrackerSegment>();
}
private int calculateOffset(long id) {
int offset;
// A long modulo an integer is an integer.
offset = (int) (id % SEGMENT_SIZE);
// If the number is negative, we need to shift the number relative to the base of the
// segment.
if (offset < 0) {
offset = SEGMENT_SIZE + offset;
}
return offset;
}
private DynamicIdTrackerSegment getSegment(long base, boolean createIfMissing) {
int intervalBegin;
int intervalEnd;
DynamicIdTrackerSegment segment;
segment = null;
// Perform a binary search splitting the list in half each time until
// the requested id is confirmed as existing or not.
intervalBegin = 0;
intervalEnd = segments.size();
for (boolean searchComplete = false; !searchComplete;) {
int intervalSize;
long currentBase;
DynamicIdTrackerSegment currentSegment;
// Calculate the interval size.
intervalSize = intervalEnd - intervalBegin;
// If no elements exist, we have no search to perform. If elements do exist then divide
// and conquer while the size is large, then commence linear search once the size is
// small.
if (intervalSize == 0) {
if (createIfMissing) {
segment = new DynamicIdTrackerSegment(base);
segments.add(intervalBegin, segment);
}
searchComplete = true;
} else if (intervalSize >= 2) {
int intervalMid;
// Split the interval in two.
intervalMid = intervalSize / 2 + intervalBegin;
// Check whether the midpoint id is above or below the id
// required.
currentSegment = segments.get(intervalMid);
currentBase = currentSegment.getBase();
if (currentBase == base) {
segment = currentSegment;
searchComplete = true;
} else if (currentBase < base) {
intervalBegin = intervalMid + 1;
} else {
intervalEnd = intervalMid;
}
} else {
// Iterate through the entire interval.
for (; intervalBegin < intervalEnd; intervalBegin++) {
// Check if the current offset contains the id required.
currentSegment = segments.get(intervalBegin);
currentBase = currentSegment.getBase();
if (currentBase == base) {
segment = currentSegment;
break;
} else if (currentBase > base) {
// The requested segment should exist prior to the current segment.
if (createIfMissing) {
segment = new DynamicIdTrackerSegment(base);
segments.add(intervalBegin, segment);
}
break;
}
}
// The requested base is at the end of this interval where intervalBegin is currently pointing.
if (segment == null && createIfMissing) {
segment = new DynamicIdTrackerSegment(base);
segments.add(intervalBegin, segment);
}
searchComplete = true;
}
}
return segment;
}
/**
* {@inheritDoc}
*/
@Override
public boolean get(long id) {
int offset;
long base;
DynamicIdTrackerSegment segment;
offset = calculateOffset(id);
base = id - offset;
segment = getSegment(base, false);
if (segment != null) {
return segment.get(offset);
} else {
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public void set(long id) {
int offset;
long base;
DynamicIdTrackerSegment segment;
offset = calculateOffset(id);
base = id - offset;
segment = getSegment(base, true);
segment.set(offset);
}
/**
* {@inheritDoc}
*/
@Override
public void setAll(IdTracker idTracker) {
for (Long id : idTracker) {
set(id);
}
}
/**
* {@inheritDoc}
*/
@Override
public Iterator<Long> iterator() {
return new SegmentIdIterator(segments.iterator());
}
private static class SegmentIdIterator implements Iterator<Long> {
private Iterator<DynamicIdTrackerSegment> segments;
private Iterator<Long> currentSegmentIds;
private long currentSegmentBase;
/**
* Creates a new instance.
*
* @param segments
* The segments to iterate over.
*/
public SegmentIdIterator(Iterator<DynamicIdTrackerSegment> segments) {
this.segments = segments;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasNext() {
for (;;) {
if (currentSegmentIds == null) {
if (segments.hasNext()) {
DynamicIdTrackerSegment segment = segments.next();
currentSegmentIds = segment.iterator();
currentSegmentBase = segment.getBase();
} else {
return false;
}
}
if (currentSegmentIds.hasNext()) {
return true;
} else {
currentSegmentIds = null;
}
}
}
/**
* {@inheritDoc}
*/
@Override
public Long next() {
if (hasNext()) {
return currentSegmentIds.next() + currentSegmentBase;
} else {
throw new NoSuchElementException();
}
}
/**
* {@inheritDoc}
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}