blob: 848e74a80e29608f7d7417b9f03863537300204e [file] [log] [blame]
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
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.apache.poi.hdf.model.util;
import java.util.*;
import org.apache.poi.hdf.model.hdftypes.PropertyNode;
/*
* A B-Tree like implementation of the java.util.Set inteface. This is a modifiable set
* and thus allows elements to be added and removed. An instance of java.util.Comparator
* must be provided at construction else all Objects added to the set must implement
* java.util.Comparable and must be comparable to one another. No duplicate elements
* will be allowed in any BTreeSet in accordance with the specifications of the Set interface.
* Any attempt to add a null element will result in an IllegalArgumentException being thrown.
* The java.util.Iterator returned by the iterator method guarantees the elements returned
* are in ascending order. The Iterator.remove() method is supported.
* Comment me
*
* @author Ryan Ackley
*
*/
@Deprecated
public final class BTreeSet extends AbstractSet
{
/*
* Instance Variables
*/
public BTreeNode root;
private Comparator comparator = null;
private int order;
int size = 0;
/*
* Constructors
* A no-arg constructor is supported in accordance with the specifications of the
* java.util.Collections interface. If the order for the B-Tree is not specified
* at construction it defaults to 32.
*/
public BTreeSet()
{
this(6); // Default order for a BTreeSet is 32
}
public BTreeSet(Collection c)
{
this(6); // Default order for a BTreeSet is 32
addAll(c);
}
public BTreeSet(int order)
{
this(order, null);
}
public BTreeSet(int order, Comparator comparator)
{
this.order = order;
this.comparator = comparator;
root = new BTreeNode(null);
}
/*
* Public Methods
*/
public boolean add(Object x) throws IllegalArgumentException
{
if (x == null) throw new IllegalArgumentException();
return root.insert(x, -1);
}
public boolean contains(Object x)
{
return root.includes(x);
}
public boolean remove(Object x)
{
if (x == null) return false;
return root.delete(x, -1);
}
public int size()
{
return size;
}
public void clear()
{
root = new BTreeNode(null);
size = 0;
}
public java.util.Iterator iterator()
{
return new Iterator();
}
public static ArrayList findProperties(int start, int end, BTreeSet.BTreeNode root)
{
ArrayList results = new ArrayList();
BTreeSet.Entry[] entries = root.entries;
for(int x = 0; x < entries.length; x++)
{
if(entries[x] != null)
{
BTreeSet.BTreeNode child = entries[x].child;
PropertyNode xNode = (PropertyNode)entries[x].element;
if(xNode != null)
{
int xStart = xNode.getStart();
int xEnd = xNode.getEnd();
if(xStart < end)
{
if(xStart >= start)
{
if(child != null)
{
ArrayList beforeItems = findProperties(start, end, child);
results.addAll(beforeItems);
}
results.add(xNode);
}
else if(start < xEnd)
{
results.add(xNode);
//break;
}
}
else
{
if(child != null)
{
ArrayList beforeItems = findProperties(start, end, child);
results.addAll(beforeItems);
}
break;
}
}
else if(child != null)
{
ArrayList afterItems = findProperties(start, end, child);
results.addAll(afterItems);
}
}
else
{
break;
}
}
return results;
}
/*
* Private methods
*/
int compare(Object x, Object y)
{
return (comparator == null ? ((Comparable)x).compareTo(y) : comparator.compare(x, y));
}
/*
* Inner Classes
*/
/*
* Guarantees that the Objects are returned in ascending order. Due to the volatile
* structure of a B-Tree (many splits, steals and merges can happen in a single call to remove)
* this Iterator does not attempt to track any concurrent changes that are happening to
* it's BTreeSet. Therefore, after every call to BTreeSet.remove or BTreeSet.add a new
* Iterator should be constructed. If no new Iterator is constructed than there is a
* chance of receiving a NullPointerException. The Iterator.delete method is supported.
*/
private class Iterator implements java.util.Iterator
{
private int index = 0;
private Stack parentIndex = new Stack(); // Contains all parentIndicies for currentNode
private Object lastReturned = null;
private Object next;
private BTreeNode currentNode;
Iterator()
{
currentNode = firstNode();
next = nextElement();
}
public boolean hasNext()
{
return next != null;
}
public Object next()
{
if (next == null) throw new NoSuchElementException();
lastReturned = next;
next = nextElement();
return lastReturned;
}
public void remove()
{
if (lastReturned == null) throw new NoSuchElementException();
BTreeSet.this.remove(lastReturned);
lastReturned = null;
}
private BTreeNode firstNode()
{
BTreeNode temp = BTreeSet.this.root;
while (temp.entries[0].child != null)
{
temp = temp.entries[0].child;
parentIndex.push(Integer.valueOf(0));
}
return temp;
}
private Object nextElement()
{
if (currentNode.isLeaf())
{
if (index < currentNode.nrElements) return currentNode.entries[index++].element;
else if (!parentIndex.empty())
{ //All elements have been returned, return successor of lastReturned if it exists
currentNode = currentNode.parent;
index = ((Integer)parentIndex.pop()).intValue();
while (index == currentNode.nrElements)
{
if (parentIndex.empty()) break;
currentNode = currentNode.parent;
index = ((Integer)parentIndex.pop()).intValue();
}
if (index == currentNode.nrElements) return null; //Reached root and he has no more children
return currentNode.entries[index++].element;
}
else
{ //Your a leaf and the root
if (index == currentNode.nrElements) return null;
return currentNode.entries[index++].element;
}
}
// else - You're not a leaf so simply find and return the successor of lastReturned
currentNode = currentNode.entries[index].child;
parentIndex.push(Integer.valueOf(index));
while (currentNode.entries[0].child != null)
{
currentNode = currentNode.entries[0].child;
parentIndex.push(Integer.valueOf(0));
}
index = 1;
return currentNode.entries[0].element;
}
}
public static class Entry
{
public Object element;
public BTreeNode child;
}
public class BTreeNode
{
public Entry[] entries;
public BTreeNode parent;
private int nrElements = 0;
private final int MIN = (BTreeSet.this.order - 1) / 2;
BTreeNode(BTreeNode parent)
{
this.parent = parent;
entries = new Entry[BTreeSet.this.order];
entries[0] = new Entry();
}
boolean insert(Object x, int parentIndex)
{
if (isFull())
{ // If full, you must split and promote splitNode before inserting
Object splitNode = entries[nrElements / 2].element;
BTreeNode rightSibling = split();
if (isRoot())
{ // Grow a level
splitRoot(splitNode, this, rightSibling);
// Determine where to insert
if (BTreeSet.this.compare(x, BTreeSet.this.root.entries[0].element) < 0) insert(x, 0);
else rightSibling.insert(x, 1);
}
else
{ // Promote splitNode
parent.insertSplitNode(splitNode, this, rightSibling, parentIndex);
if (BTreeSet.this.compare(x, parent.entries[parentIndex].element) < 0) {
return insert(x, parentIndex);
}
return rightSibling.insert(x, parentIndex + 1);
}
}
else if (isLeaf())
{ // If leaf, simply insert the non-duplicate element
int insertAt = childToInsertAt(x, true);
// Determine if the element already exists
if (insertAt == -1) {
return false;
}
insertNewElement(x, insertAt);
BTreeSet.this.size++;
return true;
}
else
{ // If not full and not leaf recursively find correct node to insert at
int insertAt = childToInsertAt(x, true);
return (insertAt == -1 ? false : entries[insertAt].child.insert(x, insertAt));
}
return false;
}
boolean includes(Object x)
{
int index = childToInsertAt(x, true);
if (index == -1) return true;
if (entries[index] == null || entries[index].child == null) return false;
return entries[index].child.includes(x);
}
boolean delete(Object x, int parentIndex)
{
int i = childToInsertAt(x, true);
int priorParentIndex = parentIndex;
BTreeNode temp = this;
if (i != -1)
{
do
{
if (temp.entries[i] == null || temp.entries[i].child == null) return false;
temp = temp.entries[i].child;
priorParentIndex = parentIndex;
parentIndex = i;
i = temp.childToInsertAt(x, true);
} while (i != -1);
} // Now temp contains element to delete and temp's parentIndex is parentIndex
if (temp.isLeaf())
{ // If leaf and have more than MIN elements, simply delete
if (temp.nrElements > MIN)
{
temp.deleteElement(x);
BTreeSet.this.size--;
return true;
}
// else - If leaf and have less than MIN elements, than prepare the BTreeSet for deletion
temp.prepareForDeletion(parentIndex);
temp.deleteElement(x);
BTreeSet.this.size--;
temp.fixAfterDeletion(priorParentIndex);
return true;
}
// else - Only delete at leaf so first switch with successor than delete
temp.switchWithSuccessor(x);
parentIndex = temp.childToInsertAt(x, false) + 1;
return temp.entries[parentIndex].child.delete(x, parentIndex);
}
private boolean isFull() { return nrElements == (BTreeSet.this.order - 1); }
private boolean isLeaf() { return entries[0].child == null; }
private boolean isRoot() { return parent == null; }
/*
* Splits a BTreeNode into two BTreeNodes, removing the splitNode from the
* calling BTreeNode.
*/
private BTreeNode split()
{
BTreeNode rightSibling = new BTreeNode(parent);
int index = nrElements / 2;
entries[index++].element = null;
for (int i = 0, nr = nrElements; index <= nr; i++, index++)
{
rightSibling.entries[i] = entries[index];
if (rightSibling.entries[i] != null && rightSibling.entries[i].child != null)
rightSibling.entries[i].child.parent = rightSibling;
entries[index] = null;
nrElements--;
rightSibling.nrElements++;
}
rightSibling.nrElements--; // Need to correct for copying the last Entry which has a null element and a child
return rightSibling;
}
/*
* Creates a new BTreeSet.root which contains only the splitNode and pointers
* to it's left and right child.
*/
private void splitRoot(Object splitNode, BTreeNode left, BTreeNode right)
{
BTreeNode newRoot = new BTreeNode(null);
newRoot.entries[0].element = splitNode;
newRoot.entries[0].child = left;
newRoot.entries[1] = new Entry();
newRoot.entries[1].child = right;
newRoot.nrElements = 1;
left.parent = right.parent = newRoot;
BTreeSet.this.root = newRoot;
}
private void insertSplitNode(Object splitNode, BTreeNode left, BTreeNode right, int insertAt)
{
for (int i = nrElements; i >= insertAt; i--) entries[i + 1] = entries[i];
entries[insertAt] = new Entry();
entries[insertAt].element = splitNode;
entries[insertAt].child = left;
entries[insertAt + 1].child = right;
nrElements++;
}
private void insertNewElement(Object x, int insertAt)
{
for (int i = nrElements; i > insertAt; i--) entries[i] = entries[i - 1];
entries[insertAt] = new Entry();
entries[insertAt].element = x;
nrElements++;
}
/*
* Possibly a deceptive name for a pretty cool method. Uses binary search
* to determine the postion in entries[] in which to traverse to find the correct
* BTreeNode in which to insert a new element. If the element exists in the calling
* BTreeNode than -1 is returned. When the parameter position is true and the element
* is present in the calling BTreeNode -1 is returned, if position is false and the
* element is contained in the calling BTreeNode than the position of the element
* in entries[] is returned.
*/
private int childToInsertAt(Object x, boolean position)
{
int index = nrElements / 2;
if (entries[index] == null || entries[index].element == null) return index;
int lo = 0, hi = nrElements - 1;
while (lo <= hi)
{
if (BTreeSet.this.compare(x, entries[index].element) > 0)
{
lo = index + 1;
index = (hi + lo) / 2;
}
else
{
hi = index - 1;
index = (hi + lo) / 2;
}
}
hi++;
if (entries[hi] == null || entries[hi].element == null) return hi;
return (!position ? hi : BTreeSet.this.compare(x, entries[hi].element) == 0 ? -1 : hi);
}
private void deleteElement(Object x)
{
int index = childToInsertAt(x, false);
for (; index < (nrElements - 1); index++) entries[index] = entries[index + 1];
if (nrElements == 1) entries[index] = new Entry(); // This is root and it is empty
else entries[index] = null;
nrElements--;
}
private void prepareForDeletion(int parentIndex)
{
if (isRoot()) return; // Don't attempt to steal or merge if your the root
// If not root then try to steal left
else if (parentIndex != 0 && parent.entries[parentIndex - 1].child.nrElements > MIN)
{
stealLeft(parentIndex);
return;
}
// If not root and can't steal left try to steal right
else if (parentIndex < entries.length && parent.entries[parentIndex + 1] != null && parent.entries[parentIndex + 1].child != null && parent.entries[parentIndex + 1].child.nrElements > MIN)
{
stealRight(parentIndex);
return;
}
// If not root and can't steal left or right then try to merge left
else if (parentIndex != 0) {
mergeLeft(parentIndex);
return;
}
// If not root and can't steal left or right and can't merge left you must be able to merge right
else mergeRight(parentIndex);
}
private void fixAfterDeletion(int parentIndex)
{
if (isRoot() || parent.isRoot()) return; // No fixing needed
if (parent.nrElements < MIN)
{ // If parent lost it's n/2 element repair it
BTreeNode temp = parent;
temp.prepareForDeletion(parentIndex);
if (temp.parent == null) return; // Root changed
if (!temp.parent.isRoot() && temp.parent.nrElements < MIN)
{ // If need be recurse
BTreeNode x = temp.parent.parent;
int i = 0;
// Find parent's parentIndex
for (; i < entries.length; i++) if (x.entries[i].child == temp.parent) break;
temp.parent.fixAfterDeletion(i);
}
}
}
private void switchWithSuccessor(Object x)
{
int index = childToInsertAt(x, false);
BTreeNode temp = entries[index + 1].child;
while (temp.entries[0] != null && temp.entries[0].child != null) temp = temp.entries[0].child;
Object successor = temp.entries[0].element;
temp.entries[0].element = entries[index].element;
entries[index].element = successor;
}
/*
* This method is called only when the BTreeNode has the minimum number of elements,
* has a leftSibling, and the leftSibling has more than the minimum number of elements.
*/
private void stealLeft(int parentIndex)
{
BTreeNode p = parent;
BTreeNode ls = parent.entries[parentIndex - 1].child;
if (isLeaf())
{ // When stealing from leaf to leaf don't worry about children
int add = childToInsertAt(p.entries[parentIndex - 1].element, true);
insertNewElement(p.entries[parentIndex - 1].element, add);
p.entries[parentIndex - 1].element = ls.entries[ls.nrElements - 1].element;
ls.entries[ls.nrElements - 1] = null;
ls.nrElements--;
}
else
{ // Was called recursively to fix an undermanned parent
entries[0].element = p.entries[parentIndex - 1].element;
p.entries[parentIndex - 1].element = ls.entries[ls.nrElements - 1].element;
entries[0].child = ls.entries[ls.nrElements].child;
entries[0].child.parent = this;
ls.entries[ls.nrElements] = null;
ls.entries[ls.nrElements - 1].element = null;
nrElements++;
ls.nrElements--;
}
}
/*
* This method is called only when stealLeft can't be called, the BTreeNode
* has the minimum number of elements, has a rightSibling, and the rightSibling
* has more than the minimum number of elements.
*/
private void stealRight(int parentIndex)
{
BTreeNode p = parent;
BTreeNode rs = p.entries[parentIndex + 1].child;
if (isLeaf())
{ // When stealing from leaf to leaf don't worry about children
entries[nrElements] = new Entry();
entries[nrElements].element = p.entries[parentIndex].element;
p.entries[parentIndex].element = rs.entries[0].element;
for (int i = 0; i < rs.nrElements; i++) rs.entries[i] = rs.entries[i + 1];
rs.entries[rs.nrElements - 1] = null;
nrElements++;
rs.nrElements--;
}
else
{ // Was called recursively to fix an undermanned parent
for (int i = 0; i <= nrElements; i++) entries[i] = entries[i + 1];
entries[nrElements].element = p.entries[parentIndex].element;
p.entries[parentIndex].element = rs.entries[0].element;
entries[nrElements + 1] = new Entry();
entries[nrElements + 1].child = rs.entries[0].child;
entries[nrElements + 1].child.parent = this;
for (int i = 0; i <= rs.nrElements; i++) rs.entries[i] = rs.entries[i + 1];
rs.entries[rs.nrElements] = null;
nrElements++;
rs.nrElements--;
}
}
/*
* This method is called only when stealLeft and stealRight could not be called,
* the BTreeNode has the minimum number of elements, has a leftSibling, and the
* leftSibling has more than the minimum number of elements. If after completion
* parent has fewer than the minimum number of elements than the parents entries[0]
* slot is left empty in anticipation of a recursive call to stealLeft, stealRight,
* mergeLeft, or mergeRight to fix the parent. All of the before-mentioned methods
* expect the parent to be in such a condition.
*/
private void mergeLeft(int parentIndex)
{
BTreeNode p = parent;
BTreeNode ls = p.entries[parentIndex - 1].child;
if (isLeaf())
{ // Don't worry about children
int add = childToInsertAt(p.entries[parentIndex - 1].element, true);
insertNewElement(p.entries[parentIndex - 1].element, add); // Could have been a successor switch
p.entries[parentIndex - 1].element = null;
for (int i = nrElements - 1, nr = ls.nrElements; i >= 0; i--)
entries[i + nr] = entries[i];
for (int i = ls.nrElements - 1; i >= 0; i--)
{
entries[i] = ls.entries[i];
nrElements++;
}
if (p.nrElements == MIN && p != BTreeSet.this.root)
{
for (int x = parentIndex - 1, y = parentIndex - 2; y >= 0; x--, y--)
p.entries[x] = p.entries[y];
p.entries[0] = new Entry();
p.entries[0].child = ls; //So p doesn't think it's a leaf this will be deleted in the next recursive call
}
else
{
for (int x = parentIndex - 1, y = parentIndex; y <= p.nrElements; x++, y++)
p.entries[x] = p.entries[y];
p.entries[p.nrElements] = null;
}
p.nrElements--;
if (p.isRoot() && p.nrElements == 0)
{ // It's the root and it's empty
BTreeSet.this.root = this;
parent = null;
}
}
else
{ // I'm not a leaf but fixing the tree structure
entries[0].element = p.entries[parentIndex - 1].element;
entries[0].child = ls.entries[ls.nrElements].child;
nrElements++;
for (int x = nrElements, nr = ls.nrElements; x >= 0; x--)
entries[x + nr] = entries[x];
for (int x = ls.nrElements - 1; x >= 0; x--)
{
entries[x] = ls.entries[x];
entries[x].child.parent = this;
nrElements++;
}
if (p.nrElements == MIN && p != BTreeSet.this.root)
{ // Push everything to the right
for (int x = parentIndex - 1, y = parentIndex - 2; y >= 0; x++, y++)
{
System.out.println(x + " " + y);
p.entries[x] = p.entries[y];
}
p.entries[0] = new Entry();
}
else
{ // Either p.nrElements > MIN or p == BTreeSet.this.root so push everything to the left
for (int x = parentIndex - 1, y = parentIndex; y <= p.nrElements; x++, y++)
p.entries[x] = p.entries[y];
p.entries[p.nrElements] = null;
}
p.nrElements--;
if (p.isRoot() && p.nrElements == 0)
{ // p == BTreeSet.this.root and it's empty
BTreeSet.this.root = this;
parent = null;
}
}
}
/*
* This method is called only when stealLeft, stealRight, and mergeLeft could not be called,
* the BTreeNode has the minimum number of elements, has a rightSibling, and the
* rightSibling has more than the minimum number of elements. If after completion
* parent has fewer than the minimum number of elements than the parents entries[0]
* slot is left empty in anticipation of a recursive call to stealLeft, stealRight,
* mergeLeft, or mergeRight to fix the parent. All of the before-mentioned methods
* expect the parent to be in such a condition.
*/
private void mergeRight(int parentIndex)
{
BTreeNode p = parent;
BTreeNode rs = p.entries[parentIndex + 1].child;
if (isLeaf())
{ // Don't worry about children
entries[nrElements] = new Entry();
entries[nrElements].element = p.entries[parentIndex].element;
nrElements++;
for (int i = 0, nr = nrElements; i < rs.nrElements; i++, nr++)
{
entries[nr] = rs.entries[i];
nrElements++;
}
p.entries[parentIndex].element = p.entries[parentIndex + 1].element;
if (p.nrElements == MIN && p != BTreeSet.this.root)
{
for (int x = parentIndex + 1, y = parentIndex; y >= 0; x--, y--)
p.entries[x] = p.entries[y];
p.entries[0] = new Entry();
p.entries[0].child = rs; // So it doesn't think it's a leaf, this child will be deleted in the next recursive call
}
else
{
for (int x = parentIndex + 1, y = parentIndex + 2; y <= p.nrElements; x++, y++)
p.entries[x] = p.entries[y];
p.entries[p.nrElements] = null;
}
p.nrElements--;
if (p.isRoot() && p.nrElements == 0)
{ // It's the root and it's empty
BTreeSet.this.root = this;
parent = null;
}
}
else
{ // It's not a leaf
entries[nrElements].element = p.entries[parentIndex].element;
nrElements++;
for (int x = nrElements + 1, y = 0; y <= rs.nrElements; x++, y++)
{
entries[x] = rs.entries[y];
rs.entries[y].child.parent = this;
nrElements++;
}
nrElements--;
p.entries[++parentIndex].child = this;
if (p.nrElements == MIN && p != BTreeSet.this.root)
{
for (int x = parentIndex - 1, y = parentIndex - 2; y >= 0; x--, y--)
p.entries[x] = p.entries[y];
p.entries[0] = new Entry();
}
else
{
for (int x = parentIndex - 1, y = parentIndex; y <= p.nrElements; x++, y++)
p.entries[x] = p.entries[y];
p.entries[p.nrElements] = null;
}
p.nrElements--;
if (p.isRoot() && p.nrElements == 0)
{ // It's the root and it's empty
BTreeSet.this.root = this;
parent = null;
}
}
}
}
}