/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.drools.core.util.index;
import org.drools.core.reteoo.TupleMemory;
import org.drools.core.spi.Tuple;
import org.drools.core.util.AbstractHashTable;
import org.drools.core.util.Entry;
import org.drools.core.util.FastIterator;
import org.drools.core.util.Iterator;
import org.drools.core.util.RBTree;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.List;
public class LeftTupleIndexRangeRBTree implements Externalizable, TupleMemory {
private RBTree<Comparable<Comparable>, RBTree<Comparable<Comparable>, TupleList>> tree;
private AbstractHashTable.FieldIndex ascendingIndex;
private IndexUtil.ConstraintType ascendingConstraintType;
private AbstractHashTable.FieldIndex descendingIndex;
private IndexUtil.ConstraintType descendingConstraintType;
private transient TupleFastIterator tupleFastIterator;
private int size;
public LeftTupleIndexRangeRBTree() {
// constructor for serialisation
}
public LeftTupleIndexRangeRBTree( IndexUtil.ConstraintType ascendingConstraintType, AbstractHashTable.FieldIndex ascendingIndex,
IndexUtil.ConstraintType descendingConstraintType, AbstractHashTable.FieldIndex descendingIndex ) {
this.ascendingIndex = ascendingIndex;
this.ascendingConstraintType = ascendingConstraintType;
this.descendingIndex = descendingIndex;
this.descendingConstraintType = descendingConstraintType;
tree = new RBTree<Comparable<Comparable>, RBTree<Comparable<Comparable>, TupleList>>();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject( tree );
out.writeObject( ascendingIndex );
out.writeObject( ascendingConstraintType );
out.writeObject( descendingIndex );
out.writeObject( descendingConstraintType );
out.writeInt(size);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
tree = (RBTree<Comparable<Comparable>, RBTree<Comparable<Comparable>, TupleList>>) in.readObject();
ascendingIndex = (AbstractHashTable.FieldIndex) in.readObject();
ascendingConstraintType = (IndexUtil.ConstraintType) in.readObject();
descendingIndex = (AbstractHashTable.FieldIndex) in.readObject();
descendingConstraintType = (IndexUtil.ConstraintType) in.readObject();
size = in.readInt();
}
public void add(Tuple tuple) {
Comparable lowerBound = getLeftAscendingIndexedValue(tuple);
Comparable upperBound = getLeftDescendingIndexedValue(tuple);
RBTree<Comparable<Comparable>, TupleList> nestedTree = tree.lookup(lowerBound);
if (nestedTree == null) {
nestedTree = new RBTree<Comparable<Comparable>, TupleList>();
tree.insert(lowerBound, nestedTree);
}
TupleList list = nestedTree.lookup(upperBound);
if (list == null) {
list = new TupleList();
nestedTree.insert(upperBound, list);
}
list.add(tuple);
size++;
}
public void remove(Tuple tuple) {
tuple.getMemory().remove(tuple);
size--;
}
public void removeAdd(Tuple tuple) {
remove( tuple );
add( tuple );
}
public boolean isIndexed() {
return true;
}
public int size() {
return size;
}
public Entry[] toArray() {
FastIterator it = tree.fastIterator();
if (it == null) {
return new Entry[0];
}
List<Comparable> toBeRemoved = new ArrayList<Comparable>();
List<Comparable> nestedToBeRemoved = new ArrayList<Comparable>();
List<Tuple> result = new ArrayList<Tuple>();
RBTree.Node<Comparable<Comparable>, RBTree<Comparable<Comparable>, TupleList>> node = null;
RBTree.Node<Comparable<Comparable>, TupleList> nestedNode = null;
while ( (node = (RBTree.Node<Comparable<Comparable>, RBTree<Comparable<Comparable>, TupleList>>) it.next( node )) != null ) {
nestedToBeRemoved.clear();
RBTree<Comparable<Comparable>, TupleList> nestedTree = node.value;
FastIterator nestedIt = nestedTree.fastIterator();
while ( (nestedNode = (RBTree.Node<Comparable<Comparable>, TupleList>) nestedIt.next( nestedNode )) != null ) {
TupleList list = nestedNode.value;
int listSize = list.size();
if (listSize == 0) {
nestedToBeRemoved.add(nestedNode.key);
} else {
Tuple entry = list.getFirst();
while (entry != null) {
result.add(entry);
entry = (Tuple) entry.getNext();
}
}
}
for (Comparable key : nestedToBeRemoved) {
nestedTree.delete(key);
}
if (nestedTree.isEmpty()) {
toBeRemoved.add(node.key);
}
}
for (Comparable key : toBeRemoved) {
tree.delete(key);
}
return result.toArray(new Tuple[result.size()]);
}
public Tuple getFirst(Tuple tuple) {
Comparable key = getRightIndexedValue(tuple);
return getFirst(key);
}
public Iterator<Tuple> iterator() {
TupleList list = tree.first().value.first().value;
Tuple firstTuple = list != null ? list.getFirst() : null;
return new FastIterator.IteratorAdapter(fastIterator(), firstTuple);
}
public boolean contains(Tuple tuple) {
Comparable lowerBound = getLeftAscendingIndexedValue(tuple);
RBTree<Comparable<Comparable>, TupleList> nestedTree = tree.lookup(lowerBound);
return nestedTree == null ? false : nestedTree.lookup(getLeftDescendingIndexedValue(tuple)) != null;
}
public FastIterator fastIterator() {
if ( tupleFastIterator == null ) {
tupleFastIterator = new TupleFastIterator();
}
return tupleFastIterator;
}
public FastIterator fullFastIterator() {
if ( tupleFastIterator == null ) {
tupleFastIterator = new TupleFastIterator();
}
return tupleFastIterator;
}
public FastIterator fullFastIterator(Tuple tuple) {
FastIterator fastIterator = fullFastIterator();
fastIterator.next(getNext(tuple));
return fastIterator;
}
private Comparable getLeftAscendingIndexedValue(Tuple tuple) {
return (Comparable) ascendingIndex.getDeclaration().getExtractor().getValue( tuple.get( ascendingIndex.getDeclaration() ).getObject() );
}
private Comparable getLeftDescendingIndexedValue(Tuple tuple) {
return (Comparable) descendingIndex.getDeclaration().getExtractor().getValue( tuple.get( descendingIndex.getDeclaration() ).getObject() );
}
private Comparable getRightIndexedValue(Tuple tuple) {
return (Comparable) ascendingIndex.getExtractor().getValue( tuple.getFactHandle().getObject() );
}
private Tuple getFirst(Comparable key) {
RBTree.Node<Comparable<Comparable>, RBTree<Comparable<Comparable>, TupleList>> nestedNode = null;
RBTree.Node<Comparable<Comparable>, TupleList> firstNode;
boolean findNextNestedNode = true;
while (true) {
if (findNextNestedNode) {
switch (ascendingConstraintType) {
case GREATER_THAN:
nestedNode = tree.findNearestNode(key, false, RBTree.Boundary.UPPER);
break;
case GREATER_OR_EQUAL:
nestedNode = tree.findNearestNode(key, true, RBTree.Boundary.UPPER);
break;
default:
throw new UnsupportedOperationException("Cannot call remove constraint of type: " + ascendingConstraintType);
}
}
findNextNestedNode = true;
if (nestedNode == null) {
return null;
}
if (nestedNode.value.isEmpty()) {
tree.delete(nestedNode.key);
continue;
}
RBTree<Comparable<Comparable>, TupleList> nestedTree = nestedNode.value;
while (true) {
switch (descendingConstraintType) {
case LESS_THAN:
firstNode = nestedTree.findNearestNode(key, false, RBTree.Boundary.LOWER);
break;
case LESS_OR_EQUAL:
firstNode = nestedTree.findNearestNode(key, true, RBTree.Boundary.LOWER);
break;
default:
throw new UnsupportedOperationException("Cannot call remove constraint of type: " + descendingConstraintType);
}
if (firstNode != null && firstNode.value.size() == 0) {
nestedTree.delete(firstNode.key);
firstNode = null;
}
if (firstNode == null) {
nestedNode = tree.findNearestNode(nestedNode.key, false, RBTree.Boundary.UPPER);
if (nestedNode == null) {
return null;
}
findNextNestedNode = false;
break;
}
return firstNode.value.getFirst();
}
}
}
private Entry getNext(Tuple tuple) {
Tuple next = (Tuple) tuple.getNext();
if (next != null) {
return next;
}
Comparable ascendingKey = getLeftAscendingIndexedValue(tuple);
Comparable descendingKey = getLeftDescendingIndexedValue(tuple);
RBTree<Comparable<Comparable>, TupleList> nestedTree = tree.lookup(ascendingKey);
while (nestedTree != null) {
while (true) {
RBTree.Node<Comparable<Comparable>, TupleList> nextNode = nestedTree.findNearestNode(descendingKey, false, RBTree.Boundary.LOWER);
if (nextNode == null) {
break;
}
if (nextNode.value.size() == 0) {
nestedTree.delete(nextNode.key);
} else {
return nextNode.value.getFirst();
}
}
RBTree.Node<Comparable<Comparable>, RBTree<Comparable<Comparable>, TupleList>> nextNode = tree.findNearestNode(ascendingKey, false, RBTree.Boundary.UPPER);
nestedTree = nextNode == null ? null : nextNode.value;
}
return null;
}
public class TupleFastIterator implements FastIterator {
public Entry next(Entry object) {
return object == null ? null : getNext((Tuple) object);
}
public boolean isFullIterator() {
return false;
}
}
public void clear() {
tree = new RBTree<Comparable<Comparable>, RBTree<Comparable<Comparable>, TupleList>>();
}
public TupleMemory.IndexType getIndexType() {
return TupleMemory.IndexType.COMPARISON;
}
}