All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.sirix.index.art.NavigableSubMap Maven / Gradle / Ivy

package io.sirix.index.art;

import java.util.*;
import java.util.function.Consumer;

// A NavigableMap that adds range checking (if passed in key is within lower and upper bound)
// for all the map methods and then relays the call
// into the backing map
abstract class NavigableSubMap extends AbstractMap
		implements NavigableMap {

	final AdaptiveRadixTree tree;

	/**
	 * Endpoints are represented as triples (fromStart, lo,
	 * loInclusive) and (toEnd, hi, hiInclusive). If fromStart is
	 * true, then the low (absolute) bound is the start of the
	 * backing map, and the other values are ignored. Otherwise,
	 * if loInclusive is true, lo is the inclusive bound, else lo
	 * is the exclusive bound. Similarly for the upper bound.
	 */

	final K lo, hi;
	final byte[] loBytes, hiBytes;
	final boolean fromStart, toEnd;
	final boolean loInclusive, hiInclusive;

	NavigableSubMap(AdaptiveRadixTree m,
			boolean fromStart, K lo, boolean loInclusive,
			boolean toEnd, K hi, boolean hiInclusive) {
		// equivalent to type check in TreeMap
		this.loBytes = fromStart ? null : m.binaryComparable().get(lo);
		this.hiBytes = toEnd ? null : m.binaryComparable().get(hi);
		if (!fromStart && !toEnd) {
			if (m.compare(loBytes, 0, loBytes.length, hiBytes, 0, hiBytes.length) > 0)
				throw new IllegalArgumentException("fromKey > toKey");
		}
		this.tree = m;
		this.fromStart = fromStart;
		this.lo = lo;
		this.loInclusive = loInclusive;
		this.toEnd = toEnd;
		this.hi = hi;
		this.hiInclusive = hiInclusive;
	}

	// internal utilities

	final boolean tooLow(K key) {
		if (!fromStart) {
			int c = tree.compare(key, loBytes);
			// if c == 0 and if lower bound is exclusive
			// then this key is too low
			// else it is not, since it is as low as our lower bound
			if (c < 0 || (c == 0 && !loInclusive))
				return true;
		}
		// we don't have a lower bound
		return false;
	}

	final boolean tooHigh(K key) {
		if (!toEnd) {
			int c = tree.compare(key, hiBytes);
			// if c == 0 and if upper bound is exclusive
			// then this key is too higher
			// else it is not, since it is as greater as our upper bound
			if (c > 0 || (c == 0 && !hiInclusive))
				return true;
		}
		// we don't have an upper bound
		return false;
	}

	final boolean inRange(K key) {
		return !tooLow(key) && !tooHigh(key);
	}

	final boolean inClosedRange(K key) {
		// if we don't have any upper nor lower bounds, then all keys are always in range.
		// if we have a lower bound, then this key ought to be higher than our lower bound (closed, hence including).
		// if we have an upper bound, then this key ought to be lower than our upper bound (closed, hence including).
		return (fromStart || tree.compare(key, loBytes) >= 0)
				&& (toEnd || tree.compare(key, hiBytes) <= 0);
	}

	final boolean inRange(K key, boolean inclusive) {
		return inclusive ? inRange(key) : inClosedRange(key);
	}


	/*
	 * Absolute versions of relation operations.
	 * Subclasses map to these using like-named "sub"
	 * versions that invert senses for descending maps
	 */

	final LeafNode absLowest() {
		LeafNode e =
				(fromStart ? tree.getFirstEntry() :
						(loInclusive ? tree.getCeilingEntry(loBytes) :
								tree.getHigherEntry(loBytes)));
		return (e == null || tooHigh(e.getKey())) ? null : e;
	}

	final LeafNode absHighest() {
		LeafNode e =
				(toEnd ? tree.getLastEntry() :
						(hiInclusive ? tree.getFloorEntry(hiBytes) :
								tree.getLowerEntry(hiBytes)));
		return (e == null || tooLow(e.getKey())) ? null : e;
	}

	final LeafNode absCeiling(K key) {
		if (tooLow(key))
			return absLowest();
		LeafNode e = tree.getCeilingEntry(key);
		return (e == null || tooHigh(e.getKey())) ? null : e;
	}

	final LeafNode absHigher(K key) {
		if (tooLow(key))
			return absLowest();
		LeafNode e = tree.getHigherEntry(key);
		return (e == null || tooHigh(e.getKey())) ? null : e;
	}

	final LeafNode absFloor(K key) {
		if (tooHigh(key))
			return absHighest();
		LeafNode e = tree.getFloorEntry(key);
		return (e == null || tooLow(e.getKey())) ? null : e;
	}

	final LeafNode absLower(K key) {
		if (tooHigh(key))
			return absHighest();
		LeafNode e = tree.getLowerEntry(key);
		return (e == null || tooLow(e.getKey())) ? null : e;
	}

	/** Returns the absolute high fence for ascending traversal */
	final LeafNode absHighFence() {
		return (toEnd ? null : (hiInclusive ?
				tree.getHigherEntry(hiBytes) :
				tree.getCeilingEntry(hiBytes))); // then hi itself (but we want the entry, hence traversal is required)
	}

	/** Return the absolute low fence for descending traversal  */
	final LeafNode absLowFence() {
		return (fromStart ? null : (loInclusive ?
				tree.getLowerEntry(loBytes) :
				tree.getFloorEntry(loBytes))); // then lo itself (but we want the entry, hence traversal is required)
	}

	// Abstract methods defined in ascending vs descending classes
	// These relay to the appropriate absolute versions

	abstract LeafNode subLowest();

	abstract LeafNode subHighest();

	abstract LeafNode subCeiling(K key);

	abstract LeafNode subHigher(K key);

	abstract LeafNode subFloor(K key);

	abstract LeafNode subLower(K key);


	/* Returns ascending iterator from the perspective of this submap */

	abstract Iterator keyIterator();

	abstract Spliterator keySpliterator();


	/* Returns descending iterator from the perspective of this submap*/

	abstract Iterator descendingKeyIterator();

	// public methods
	@Override
	public boolean isEmpty() {
		return (fromStart && toEnd) ? tree.isEmpty() : entrySet().isEmpty();
	}

	@Override
	public int size() {
		return (fromStart && toEnd) ? tree.size() : entrySet().size();
	}

	@Override
	public final boolean containsKey(Object key) {
		return inRange((K) key) && tree.containsKey(key);
	}

	@Override
	public final V put(K key, V value) {
		if (!inRange(key))
			throw new IllegalArgumentException("key out of range");
		return tree.put(key, value);
	}

	@Override
	public final V get(Object key) {
		return !inRange((K) key) ? null : tree.get(key);
	}

	@Override
	public final V remove(Object key) {
		return !inRange((K) key) ? null : tree.remove(key);
	}

	@Override
	public final Entry ceilingEntry(K key) {
		return AdaptiveRadixTree.exportEntry(subCeiling(key));
	}

	@Override
	public final K ceilingKey(K key) {
		return AdaptiveRadixTree.keyOrNull(subCeiling(key));
	}

	@Override
	public final Entry higherEntry(K key) {
		return AdaptiveRadixTree.exportEntry(subHigher(key));
	}

	@Override
	public final K higherKey(K key) {
		return AdaptiveRadixTree.keyOrNull(subHigher(key));
	}

	@Override
	public final Entry floorEntry(K key) {
		return AdaptiveRadixTree.exportEntry(subFloor(key));
	}

	@Override
	public final K floorKey(K key) {
		return AdaptiveRadixTree.keyOrNull(subFloor(key));
	}

	@Override
	public final Entry lowerEntry(K key) {
		return AdaptiveRadixTree.exportEntry(subLower(key));
	}

	@Override
	public final K lowerKey(K key) {
		return AdaptiveRadixTree.keyOrNull(subLower(key));
	}

	@Override
	public final K firstKey() {
		return AdaptiveRadixTree.key(subLowest());
	}

	@Override
	public final K lastKey() {
		return AdaptiveRadixTree.key(subHighest());
	}

	@Override
	public final Entry firstEntry() {
		return AdaptiveRadixTree.exportEntry(subLowest());
	}

	@Override
	public final Entry lastEntry() {
		return AdaptiveRadixTree.exportEntry(subHighest());
	}

	@Override
	public final Entry pollFirstEntry() {
		LeafNode e = subLowest();
		Entry result = AdaptiveRadixTree.exportEntry(e);
		if (e != null)
			tree.deleteEntry(e);
		return result;
	}

	@Override
	public final Entry pollLastEntry() {
		LeafNode e = subHighest();
		Entry result = AdaptiveRadixTree.exportEntry(e);
		if (e != null)
			tree.deleteEntry(e);
		return result;
	}

	// Views
	transient NavigableMap descendingMapView;
	transient EntrySetView entrySetView;
	transient KeySet navigableKeySetView;

	@Override
	public final NavigableSet navigableKeySet() {
		KeySet nksv = navigableKeySetView;
		return (nksv != null) ? nksv :
				(navigableKeySetView = new KeySet<>(this));
	}

	@Override
	public final Set keySet() {
		return navigableKeySet();
	}

	@Override
	public NavigableSet descendingKeySet() {
		return descendingMap().navigableKeySet();
	}

	@Override
	public final SortedMap subMap(K fromKey, K toKey) {
		return subMap(fromKey, true, toKey, false);
	}

	@Override
	public final SortedMap headMap(K toKey) {
		return headMap(toKey, false);
	}

	@Override
	public final SortedMap tailMap(K fromKey) {
		return tailMap(fromKey, true);
	}

	// View classes

	// entry set views for submaps
	abstract class EntrySetView extends AbstractSet> {
		private transient int size = -1, sizeModCount;

		// if the submap does not define any upper and lower bounds
		// i.e. it is the same view as the original map (very unlikely)
		// then no need to explicitly calculate the size.
		@Override
		public int size() {
			if (fromStart && toEnd)
				return tree.size();
			// if size == -1, it is the first time we're calculating the size
			// if sizeModCount != m.getModCount(), the map has had modification operations
			// so it's size must've changed, recalculate.
			if (size == -1 || sizeModCount != tree.getModCount()) {
				sizeModCount = tree.getModCount();
				size = 0;
				Iterator i = iterator();
				while (i.hasNext()) {
					size++;
					i.next();
				}
			}
			return size;
		}

		@Override
		public boolean isEmpty() {
			LeafNode n = absLowest();
			return n == null || tooHigh(n.getKey());
		}

		// efficient impl of contains than the default in AbstractSet
		@Override
		public boolean contains(Object o) {
			if (!(o instanceof Map.Entry))
				return false;
			Entry entry = (Entry) o;
			Object key = entry.getKey();
			if (!inRange((K) key))
				return false;
			LeafNode node = tree.getEntry(key);
			return node != null &&
					AdaptiveRadixTree.valEquals(node.getValue(), entry.getValue());
		}

		// efficient impl of remove than the default in AbstractSet
		@Override
		public boolean remove(Object o) {
			if (!(o instanceof Map.Entry))
				return false;
			Entry entry = (Entry) o;
			Object key = entry.getKey();
			if (!inRange((K) key))
				return false;
			LeafNode node = tree.getEntry(key);
			if (node != null && AdaptiveRadixTree.valEquals(node.getValue(),
			                                                entry.getValue())) {
				tree.deleteEntry(node);
				return true;
			}
			return false;
		}
	}


	/* Dummy value serving as unmatchable fence key for unbounded SubMapIterators */
	private static final Object UNBOUNDED = new Object();

	/*
	 *  Iterators for SubMaps
	 *  that understand the submap's upper and lower bound while iterating.
	 *  Fence is one of the bounds depending on the kind of iterator (ascending, descending)
	 *  and first becomes the other one to start from.
	 */
	abstract class SubMapIterator implements Iterator {
		LeafNode lastReturned;
		LeafNode next;
		final Object fenceKey;
		int expectedModCount;

		SubMapIterator(LeafNode first,
				LeafNode fence) {
			expectedModCount = tree.getModCount();
			lastReturned = null;
			next = first;
			fenceKey = fence == null ? UNBOUNDED : fence.getKey();
		}

		@Override
		public final boolean hasNext() {
			return next != null && next.getKey() != fenceKey;
		}

		final LeafNode nextEntry() {
			LeafNode e = next;
			if (e == null || e.getKey() == fenceKey)
				throw new NoSuchElementException();
			if (tree.getModCount() != expectedModCount)
				throw new ConcurrentModificationException();
			next = AdaptiveRadixTree.successor(e);
			lastReturned = e;
			return e;
		}

		final LeafNode prevEntry() {
			LeafNode e = next;
			if (e == null || e.getKey() == fenceKey)
				throw new NoSuchElementException();
			if (tree.getModCount() != expectedModCount)
				throw new ConcurrentModificationException();
			next = AdaptiveRadixTree.predecessor(e);
			lastReturned = e;
			return e;
		}

		@Override
		public void remove() {
			if (lastReturned == null)
				throw new IllegalStateException();
			if (tree.getModCount() != expectedModCount)
				throw new ConcurrentModificationException();
			// deleted entries are replaced by their successors
			//	if (lastReturned.left != null && lastReturned.right != null)
			//		next = lastReturned;
			tree.deleteEntry(lastReturned);
			lastReturned = null;
			expectedModCount = tree.getModCount();
		}
	}

	final class SubMapEntryIterator extends SubMapIterator> {
		SubMapEntryIterator(LeafNode first,
				LeafNode fence) {
			super(first, fence);
		}

		@Override
		public Entry next() {
			return nextEntry();
		}
	}

	final class DescendingSubMapEntryIterator extends SubMapIterator> {
		DescendingSubMapEntryIterator(LeafNode last,
				LeafNode fence) {
			super(last, fence);
		}

		@Override
		public Entry next() {
			return prevEntry();
		}
	}

	// Implement minimal Spliterator as KeySpliterator backup
	final class SubMapKeyIterator extends SubMapIterator
			implements Spliterator {
		SubMapKeyIterator(LeafNode first,
				LeafNode fence) {
			super(first, fence);
		}

		@Override
		public K next() {
			return nextEntry().getKey();
		}

		@Override
		public Spliterator trySplit() {
			return null;
		}

		@Override
		public void forEachRemaining(Consumer action) {
			while (hasNext())
				action.accept(next());
		}

		@Override
		public boolean tryAdvance(Consumer action) {
			if (hasNext()) {
				action.accept(next());
				return true;
			}
			return false;
		}

		// estimating size of submap would be expensive
		// since we'd have to traverse from lower bound to upper bound
		// for this submap
		@Override
		public long estimateSize() {
			return Long.MAX_VALUE;
		}

		@Override
		public int characteristics() {
			return Spliterator.DISTINCT | Spliterator.ORDERED |
					Spliterator.SORTED;
		}

		@Override
		public final Comparator getComparator() {
			return NavigableSubMap.this.comparator();
		}
	}

	final class DescendingSubMapKeyIterator extends SubMapIterator
			implements Spliterator {
		DescendingSubMapKeyIterator(LeafNode last,
				LeafNode fence) {
			super(last, fence);
		}

		@Override
		public K next() {
			return prevEntry().getKey();
		}

		@Override
		public Spliterator trySplit() {
			return null;
		}

		@Override
		public void forEachRemaining(Consumer action) {
			while (hasNext())
				action.accept(next());
		}

		@Override
		public boolean tryAdvance(Consumer action) {
			if (hasNext()) {
				action.accept(next());
				return true;
			}
			return false;
		}

		@Override
		public long estimateSize() {
			return Long.MAX_VALUE;
		}

		@Override
		public int characteristics() {
			return Spliterator.DISTINCT | Spliterator.ORDERED;
		}
	}
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy