/*
* XXL: The eXtensible and fleXible Library for data processing
*
* Copyright (C) 2000-2014 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department
* of Mathematics and Computer Science University of Marburg Germany
*
* This library is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library;
* If not, see <http://www.gnu.org/licenses/>.
*
* http://code.google.com/p/xxl/
*/
package xxl.core.indexStructures;
import static xxl.core.util.ConvertUtils.autoCast;
import static xxl.core.util.ConvertUtils.autoComparable;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.SortedSet;
import xxl.core.cursors.Cursor;
import xxl.core.relational.tuples.Tuple;
/**
* This class is a fixed non-editable portion of data of a {@link BPlusIndexedSet} defined by its
* lower and a upper bound and acts like a fixed range query. It is created by calling
* {@link BPlusIndexedSet#subSet(Object, Object)}, {@link BPlusIndexedSet#headSet(Object)} or
* {@link BPlusIndexedSet#tailSet(Object)}. Depending on set type a <code>BPlusIndexedSetView</code>
* contains the elements of the parent set which are greater or equal to the lower bound and (may be
* strictly) less than the upper bound. <br/>
* <b>Please note:</b> Because it is a fixed view not all methods of Java's {@link SortedSet}
* interface are supported. Although you can not edit the content of a view, it's content will be
* refreshed automatically if the source set changes. That means if a view <code>v</code> contains
* an item <code>e</code> and you remove <code>e</code> from the parent set, <code>e</code> will not
* be visible in <code>v</code> anymore.
*
* @author Marcus Pinnecke (pinnecke@mathematik.uni-marburg.de)
*
* @param <E> The data type which is managed by the parent set
*/
public final class BPlusIndexedSetView<E extends Comparable>
extends FixedQuery<E> implements Comparator<E> {
/*
* Actually an implementation of XXL Cursor to realize the "strictly less than" constrain of the
* Java SortedSet Interface for some kinds of sub set. That means e.g. SortedSet#subSet requires
* to skip the upper bound inside the view whereas SortedSet#tailSet includes the upper bound.
*
* This Cursor implementation just checks if the item after the next item to query is equal to the
* upper bound. If "excluding" the upper bound is enabled it will not return the upper bound when
* calling Cursor#hasNext/Cursor#next
*/
class RightOpenIntervalCursor<E extends Comparable> implements Cursor<E> {
private Cursor<E> m_Cursor;
private boolean m_ExcludeToElement;
private FixedQuery m_Parent;
private Comparable m_UpperBound;
public RightOpenIntervalCursor(Cursor cursor, Comparable upperBound,
FixedQuery parent) {
m_Cursor = cursor;
m_UpperBound =
autoComparable(autoCast(upperBound), parent.m_DataSource.mCreator);
m_Parent = parent;
m_ExcludeToElement = parent.m_ExcludeToElement;
}
@Override
public void close() {
m_Cursor.close();
}
@Override
public boolean hasNext() throws IllegalStateException {
if (m_ExcludeToElement)
return m_Cursor.hasNext()
&& autoComparable(autoCast(peek()), m_DataSource.mCreator)
.compareTo(
autoComparable(autoCast(m_UpperBound),
m_DataSource.mCreator)) < 0;
else
return m_Cursor.hasNext()
&& autoComparable(autoCast(peek()), m_DataSource.mCreator)
.compareTo(
autoComparable(autoCast(m_UpperBound),
m_DataSource.mCreator)) <= 0;
}
@Override
public E next() throws IllegalStateException, NoSuchElementException {
return (E) autoComparable(autoCast(m_Cursor.next()),
m_DataSource.mCreator);
}
@Override
public void open() {
m_Cursor.open();
}
@Override
public E peek() throws IllegalStateException, NoSuchElementException {
return (E) autoComparable(m_Cursor.peek(), m_Parent.m_DataSource.mCreator);
}
/**
* This method is not supported and will throw an <b>UnsupportedOperationException</b>
*/
@Override
public void remove() throws IllegalStateException,
UnsupportedOperationException {
throw new UnsupportedOperationException();
}
@Override
public void reset() throws UnsupportedOperationException {
m_Cursor.reset();
}
@Override
public boolean supportsPeek() {
return m_Cursor.supportsPeek();
}
/**
* This method is not supported and will throw an <b>UnsupportedOperationException</b>
*/
@Override
public boolean supportsRemove() {
return false;
}
@Override
public boolean supportsReset() {
return m_Cursor.supportsReset();
}
@Override
public boolean supportsUpdate() {
return false;
}
/**
* This method is not supported and will throw an <b>UnsupportedOperationException</b>
*/
@Override
public void update(E object) throws IllegalStateException,
UnsupportedOperationException {
throw new UnsupportedOperationException();
}
}
/*
* The reference of the parent set BPlusTree
*/
private BPlusTree m_BPlusTree;
/**
* Constructs a new instance of a fixed view of a BPlus indexed set.
*
* @param dataSource BPlusTree reference of the parent set
* @param minBound The views minimum bound
* @param maxBound The views maximum bound
* @param excludeToElement A flag which indicates to include or exclude the upper bound for
* queries
*/
public BPlusIndexedSetView(final BPlusIndexedSet dataSource,
Comparable minBound, Comparable maxBound, boolean excludeToElement) {
super(dataSource, minBound, maxBound, excludeToElement);
m_BPlusTree = (BPlusTree) dataSource.mTree;
}
/**
* This method is unsupported and will throw an <b>UnsupportedOperationException</b>.
*/
@Override
public boolean add(E e) {
throw new UnsupportedOperationException("Not available");
}
/**
* This method is unsupported and will throw an <b>UnsupportedOperationException</b>.
*/
@Override
public boolean addAll(Collection<? extends E> c) {
throw new UnsupportedOperationException("Not available");
}
/**
* This method is unsupported and will throw an <b>UnsupportedOperationException</b>.
*/
@Override
public void clear() {
throw new UnsupportedOperationException("Not available");
}
@Override
public Comparator<? super E> comparator() {
return this;
}
@Override
public int compare(E o1, E o2) {
return o1.compareTo(o2);
}
@Override
public boolean contains(Object o) {
if (!inbounds(o)) return false;
return m_DataSource.contains(o);
}
@Override
public boolean containsAll(Collection<?> c) {
for (Object item : c)
if (!contains(item)) return false;
return true;
}
private Comparable findElementBefore(Comparable e) {
Cursor cursor =
new RightOpenIntervalCursor<>(m_BPlusTree.rangeQuery(
autoComparable(autoCast(m_minBound), m_DataSource.mCreator),
autoComparable(autoCast(m_maxBound), m_DataSource.mCreator)),
m_maxBound, this);
Comparable last = null;
while (cursor.hasNext()) {
last = (Comparable) cursor.next();
}
return last;
}
@Override
public E first() {
return (E) new RightOpenIntervalCursor<>(m_BPlusTree.rangeQuery(
autoComparable(autoCast(m_minBound), m_DataSource.mCreator),
autoComparable(autoCast(m_maxBound), m_DataSource.mCreator)),
m_maxBound, this).next();
}
@Override
public SortedSet<E> headSet(E toElement) {
if (!inbounds(toElement)) throw new IndexOutOfBoundsException();
return new BPlusIndexedSetView<E>((BPlusIndexedSet) m_DataSource,
m_minBound, toElement, true);
}
/*
* Checks if the Object o is inside the views minimum and maximum bound
*/
private boolean inbounds(Object o) {
if ((autoComparable(o, m_DataSource.mCreator)).compareTo(m_minBound) < 0)
return false;
if ((autoComparable(o, m_DataSource.mCreator)).compareTo(m_maxBound) >= 0)
return false;
return true;
}
@Override
public boolean isEmpty() {
return size() <= 0;
}
@Override
public Iterator<E> iterator() {
return new RightOpenIntervalCursor<>(m_BPlusTree.rangeQuery(
autoComparable(autoCast(m_minBound), m_DataSource.mCreator),
autoComparable(autoCast(m_maxBound), m_DataSource.mCreator)),
m_maxBound, this);
}
@Override
public E last() {
RightOpenIntervalCursor roiCursor =
new RightOpenIntervalCursor<>(m_BPlusTree.rangeQuery(
autoComparable(autoCast(m_minBound), m_DataSource.mCreator),
autoComparable(autoCast(m_maxBound), m_DataSource.mCreator)),
m_maxBound, this);
return (E) findElementBefore(m_maxBound);
}
/**
* This method is unsupported and will throw an <b>UnsupportedOperationException</b>.
*/
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException("Not available");
}
/**
* This method is unsupported and will throw an <b>UnsupportedOperationException</b>.
*/
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException("Not available");
}
/**
* This method is unsupported and will throw an <b>UnsupportedOperationException</b>.
*/
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException("Not available");
}
@Override
public int size() {
RightOpenIntervalCursor roiCursor =
new RightOpenIntervalCursor<>(m_BPlusTree.rangeQuery(
autoComparable(autoCast(m_minBound), m_DataSource.mCreator),
autoComparable(autoCast(m_maxBound), m_DataSource.mCreator)),
m_maxBound, this);
int size = 0;
while (roiCursor.hasNext()) {
++size;
roiCursor.next();
}
return size;
}
@Override
public SortedSet<E> subSet(E fromElement, E toElement) {
if (!inbounds(fromElement) || !inbounds(toElement))
throw new IndexOutOfBoundsException();
return new BPlusIndexedSetView<E>((BPlusIndexedSet) m_DataSource,
fromElement, toElement, true);
}
@Override
public SortedSet<E> tailSet(E fromElement) {
if (!inbounds(fromElement)) throw new IndexOutOfBoundsException();
return new BPlusIndexedSetView<E>((BPlusIndexedSet) m_DataSource,
fromElement, m_maxBound, false);
}
@Override
public Object[] toArray() {
Object[] result = new Object[size()];
Cursor cursor =
new RightOpenIntervalCursor<>(m_BPlusTree.rangeQuery(
autoComparable(autoCast(m_minBound), m_DataSource.mCreator),
autoComparable(autoCast(m_maxBound), m_DataSource.mCreator)),
m_maxBound, this);
int index = 0;
while (cursor.hasNext()) {
Object item = m_BPlusTree.exactMatchQuery((Comparable) cursor.next());
if (item instanceof Tuple)
result[index] = ((Tuple) item).toArray();
else
result[index] = item;
index++;
}
return result;
}
@Override
public <T> T[] toArray(T[] a) {
Object[] result = new Object[size()];
Cursor cursor =
new RightOpenIntervalCursor<>(m_BPlusTree.rangeQuery(
autoComparable(autoCast(m_minBound), m_DataSource.mCreator),
autoComparable(autoCast(m_maxBound), m_DataSource.mCreator)),
m_maxBound, this);
int index = 0;
while (cursor.hasNext()) {
Object item = m_BPlusTree.exactMatchQuery((Comparable) cursor.next());
if (item instanceof Tuple)
result[index] = (T) ((Tuple) item).toArray();
else
result[index] = (T) item;
index++;
}
return (T[]) result;
}
}