/**************************************************************************
* Copyright (c) 2001 by Punch Telematix. All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* 3. Neither the name of Punch Telematix nor the names of *
* other contributors may be used to endorse or promote products *
* derived from this software without specific prior written permission.*
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *
* IN NO EVENT SHALL PUNCH TELEMATIX OR OTHER CONTRIBUTORS BE LIABLE *
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF *
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, *
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE *
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN *
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************/
package java.util;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
/**
** Implentation of a SortedMap based on tree-structure.
** The internal tree is a RED-BLACK tree. The basic Tree Algorithms were based on the
** C-implementation found at:
**
** http://www.people.fas.harvard.edu/~toub/cs50/projects/datastructs/redblack/redblack.html
**
** So kind thanks for the people from Harvard for making this code public ...
** This code has a small optimisation for find - remove operation (this is used by the Set Iterators)
** A remove Operation doesn't need browse through the tree if the key was the last one used by a 'get',
** 'contiansKey', 'lastKey' or 'firstKey' (this means removing the first key or the last key can be done
** without comparing the keys to determine which key-value pair to remove.)
** This optimisation is very usefull for all SortedMaps return by this TreeMap.
*/
public class TreeMap extends AbstractMap implements SortedMap, Cloneable, java.io.Serializable {
static boolean RED = true;
static boolean BLACK = false;
private static final long serialVersionUID = 919286545866124006L;
transient Node leaf = new Node();
transient Node root = leaf;
transient Node lastFind;
transient int modcount;
transient int size;
Comparator comparator;
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
int count = s.readInt();
Object key;
for (int i=0; i < count ; i++){
key = s.readObject();
put(key, s.readObject());
}
}
private void writeObject(ObjectOutputStream s) throws IOException{
s.defaultWriteObject();
s.writeInt(size);
Iterator it = entrySet().iterator();
Map.Entry me;
for (int i=0; i < size ; i++){
me = (Map.Entry)it.next();
s.writeObject(me.getKey());
s.writeObject(me.getValue());
}
}
public TreeMap() {}
public TreeMap(Map m) {
this.putAll(m);
}
public TreeMap(SortedMap s) {
comparator = s.comparator();
this.putAll(s);
}
public TreeMap(Comparator comp) {
comparator = comp;
}
private void rotateLeft(Node x) {
Node y = x.right;
x.right = y.left;
if (y.left != leaf){ y.left.parent = x; }
if (y != leaf){ y.parent = x.parent; }
if (x.parent != null) {
if (x == x.parent.left) {
x.parent.left = y;
}
else {
x.parent.right = y;
}
}
else { root = y; }
y.left = x;
if (x != leaf) { x.parent = y; }
}
private void rotateRight(Node n) {
Node y = n.left;
n.left = y.right;
if (y.right != leaf){ y.right.parent = n; }
if (y != leaf){ y.parent = n.parent; }
if (n.parent != null) {
if (n == n.parent.right) {
n.parent.right = y;
}
else {
n.parent.left = y;
}
}
else { root = y; }
y.right = n;
if (n != leaf) { n.parent = y; }
}
private void insertFixup(Node n) {
Node y;
while (n != root && n.parent.color == RED) {
if (n.parent == n.parent.parent.left){
y = n.parent.parent.right;
if( y.color == RED ) {//uncle is red
n.parent.color = BLACK;
y.color = BLACK;
n.parent.parent.color = RED;
n = n.parent.parent;
}
else { // uncle is black
if (n == n.parent.right) {
n = n.parent;
rotateLeft(n);
}
n.parent.color = BLACK;
n.parent.parent.color = RED;
rotateRight(n.parent.parent);
}
}
else {
y = n.parent.parent.left;
if (y.color == RED){//uncle is red
n.parent.color = BLACK;
y.color = BLACK;
n.parent.parent.color = RED;
n = n.parent.parent;
}
else { // uncle is black
if (n == n.parent.left) {
n = n.parent;
rotateRight(n);
}
n.parent.color = BLACK;
n.parent.parent.color = RED;
rotateLeft(n.parent.parent);
}
}
}
root.color = BLACK;
}
public Object put(Object key, Object value) {
Node current=root;
Node parent=null;
int comp;
while (current != leaf) {
comp = compare(key, current.key);
if (comp == 0) {
key = current.value;
current.value = value;
return key;
}
parent = current;
current = (comp < 0)? current.left : current.right;
}
Node n = new Node(parent,key,value,leaf);
modcount++;
if (parent != null) {
if (compare(key,parent.key) < 0) {
parent.left = n;
}
else {
parent.right = n;
}
}
else {
root = n;
}
insertFixup(n);
lastFind=null;
size++;
return null;
}
private void deleteFixup(Node n){
Node y;
while (n != root && n.color == BLACK) {
if (n == n.parent.left){
y = n.parent.right;
if ( y.color == RED ) {
y.color = BLACK;
n.parent.color = RED;
rotateLeft(n.parent);
y = n.parent.right;
}
if ( y.left.color == BLACK && y.right.color == BLACK ) {
y.color = RED;
n = n.parent;
}
else {
if (y.right.color == BLACK) {
y.left.color = BLACK;
y.color = RED;
rotateRight(y);
y = n.parent.right;
}
y.color = n.parent.color;
n.parent.color = BLACK;
y.right.color = BLACK;
rotateLeft(n.parent);
n = root;
}
}
else {
y = n.parent.left;
if (y.color == RED){
y.color = BLACK;
n.parent.color = RED;
rotateRight(n.parent);
y = n.parent.left;
}
if ( y.left.color == BLACK && y.right.color == BLACK ) {
y.color = RED;
n = n.parent;
}
else {
if (y.left.color == BLACK) {
y.right.color = BLACK;
y.color = RED;
rotateLeft(y);
y = n.parent.left;
}
y.color = n.parent.color;
n.parent.color = BLACK;
y.left.color = BLACK;
rotateRight(n.parent);
n = root;
}
}
}
n.color = BLACK;
}
public Object remove(Object key){
Node n,y,x;
if (lastFind != null && compare(key, lastFind.key) == 0) {
n = lastFind;
}
else {
n = root;
int comp;
while (n != leaf) {
comp = compare(key , n.key);
if (comp == 0) { break; }
else {
n = (comp < 0) ? n.left : n.right;
}
}
if (n == leaf) { return null; }
}
key = n.value;
modcount++;
if (n.right == leaf || n.left == leaf) {
y = n;
}
else {
y = n.right;
while (y.left != leaf) { y = y.left; }
}
x = (y.left != leaf) ? y.left : y.right;
x.parent = y.parent;
if (y.parent != null) {
if (y == y.parent.left) {
y.parent.left = x;
}
else {
y.parent.right = x;
}
}
else {
root = x;
}
if ( y != n ) {
n.key = y.key;
n.value = y.value;
}
if (y.color == BLACK) { deleteFixup(x); }
lastFind = null;
size--;
return key;
}
public Object get(Object key) {
Node n = root;
int comp;
while ( n != leaf ) {
comp = compare(key , n.key);
if (comp == 0) {
lastFind = n;
return n.value;
}
else {
n = (comp < 0 )? n.left : n.right;
}
}
return null;
}
Node getNode(Object key) {
Node n = root;
while ( n != leaf ) {
int comp = compare(key , n.key);
if (comp == 0) {
lastFind = n;
return n;
}
else {
n = (comp < 0 )? n.left : n.right;
}
}
return null;
}
public boolean containsKey(Object key) {
Node n = root;
int comp;
while ( n != leaf ) {
comp = compare(key , n.key);
if (comp == 0) {
lastFind = n;
return true;
}
else {
n = (comp < 0 )? n.left : n.right;
}
}
return false;
}
/**
** copies the behaviour of the iterator but without generating all the extra objects
*/
public boolean containsValue(Object o){
if(size > 0){
Node n = root;
while(n.right != leaf){
n = n.right;
}
if(o == null){
while(n != null){
if(n.value == null){
return true;
}
n = nextNode(n);
}
}
else {
while(n != null){
if(o.equals(n.value)){
return true;
}
n = nextNode(n);
}
}
}
return false;
}
public Set entrySet(){
return new SubTreeSet(leaf,leaf);
}
public Set keySet() {
return new KeySubSet(leaf,leaf);
}
public Collection values(){
return new ValueSubSet(leaf,leaf);
}
public void putAll(Map m){
Iterator it = m.entrySet().iterator();
Map.Entry me;
while (it.hasNext()){
me = (Map.Entry)it.next();
put(me.getKey(),me.getValue());
}
}
public void clear() {
lastFind = null;
leaf.parent=null;
root = leaf;
size = 0;
modcount++;
}
public Object clone(){
TreeMap s = null;
try { s = (TreeMap) super.clone(); }
catch(CloneNotSupportedException cnse){}
s.clear();
s.putAll(this);
return s;
}
public Comparator comparator() {
return comparator;
}
public Object firstKey(){
if (size == 0) {
throw new NoSuchElementException();
}
Node n = root;
while (n.left != leaf) { n = n.left; }
lastFind = n;
return n.key;
}
public Object lastKey(){
if (size == 0) {
throw new NoSuchElementException();
}
Node n = root;
while (n.right != leaf) { n = n.right; }
lastFind = n;
return n.key;
}
public boolean isEmpty(){
return (size == 0);
}
public int size(){
return size;
}
public SortedMap headMap(Object toV) {
return new SubTree(leaf,toV);
}
public SortedMap tailMap(Object fromV) {
return new SubTree(fromV,leaf);
}
public SortedMap subMap(Object fromV, Object toV) {
if ( compare(fromV,toV) > 0 ){
throw new IllegalArgumentException();
}
return new SubTree(fromV,toV);
}
int compare(Object nkey, Object key) {
int ret;
if (comparator != null) {
ret = comparator.compare(nkey, key);
}
else {
if (nkey == null) {
if (key == null){
ret = 0;
}
else {
ret = -((Comparable)key).compareTo(nkey);
}
}
else {
ret = ((Comparable)nkey).compareTo(key);
}
}
return ret;
}
// InnerClass convenience Methods ...
Node locateLowerNode(Object from) throws NoSuchElementException {
Node next = root;
if (next == leaf){
throw new NoSuchElementException("Map is Empty");
}
if (from == leaf){
while (next.left != leaf){ next = next.left; }
return next;
}
if (compare(from, next.key)<=0){
while (next.left != leaf){ next = next.left; }
}
while (next != null){
if (compare(from,next.key)<=0){
return next;
}
next = nextNode(next);
}
throw new NoSuchElementException();
}
Node locateUpperNode(Object to) throws NoSuchElementException {
Node next = root;
if (next == leaf){
throw new NoSuchElementException("Map is Empty");
}
if (leaf == to){
while (next.right != leaf){ next = next.right; }
return next;
}
if (compare(to, next.key)<=0){
while (next.left != leaf){ next = next.left; }
}
if (compare(to, next.key)<=0){
throw new NoSuchElementException();
}
Node prev=next;
next = nextNode(next);
while (next != null){
if (compare(to,next.key)<=0){
return prev;
}
prev = next;
next = nextNode(next);
}
return prev;
}
Node nextNode(Node next){
Object key = next.key;
while (next != null){
if (compare(key,next.key)<0){
return next;
}
if (next.right != leaf && compare(key,next.right.key)<0){
next = next.right;
while (next.left != leaf){
next = next.left;
}
return next;
}
next = next.parent;
}
return next;
}
Object locateLowerKey(Object from, Object to) {
Object key = (from == leaf ? firstKey(): locateLowerNode(from).key);
if (to == leaf || compare(key,to)<0){
return key;
}
throw new NoSuchElementException("Map is Empty");
}
Object locateUpperKey(Object from, Object to) {
Object key = (to == leaf ? lastKey(): locateUpperNode(to).key);
if (from == leaf || compare(key,from)>=0){
return key;
}
throw new NoSuchElementException("Map is Empty");
}
int calculateSize(Object from,Object to){
int size = 1;
try {
Node next = locateLowerNode(from);
Object key = next.key;
if (to != leaf && compare(next.key,to) >= 0){
return 0;
}
while (next != null){
if (compare(key,next.key)<0){
if (to != leaf && compare(next.key,to) >= 0){
break;
}
size++;
key = next.key;
}else {
if (next.right != leaf && compare(key,next.right.key)<0){
next = next.right;
while (next.left != leaf){
next = next.left;
}
if (to != leaf && compare(next.key,to) >= 0){
break;
}
size++;
key = next.key;
}
else {
next = next.parent;
}
}
}
}
catch(NoSuchElementException nse){
size = 0;
}
return size;
}
// END of InnerClass convenience Methods ...
static final class Node {
public Node parent;
public Node left;
public Node right;
public boolean color;
public Object key;
public Object value;
public Node(){
parent = null;
left = this;
right = this;
color = BLACK;
}
public Node(Node p, Object k, Object v, Node leaf) {
key = k;
value = v;
parent = p;
color = RED;
left = leaf;
right = leaf;
}
}
private class SubTree extends AbstractMap implements SortedMap {
private Object from;
private Object to;
private void checkRange(Object k){
if ((from != leaf && compare(k,from) < 0)
|| (to != leaf && compare(k,to) >= 0)){
throw new IllegalArgumentException();
}
}
public SubTree(Object f, Object t){
from = f;
to = t;
}
public Object firstKey() {
return locateLowerKey(from,to);
}
public Object lastKey() {
return locateUpperKey(from,to);
}
public Comparator comparator() {
return comparator;
}
public SortedMap headMap(Object toV){
checkRange(toV);
return new SubTree(from,toV);
}
public SortedMap subMap(Object fr, Object t){
if ( compare(fr,t) > 0 ){
throw new IllegalArgumentException();
}
checkRange(fr);
checkRange(t);
return new SubTree(fr,t);
}
public SortedMap tailMap(Object fr){
checkRange(fr);
return new SubTree(fr,to);
}
public Set entrySet(){
return new SubTreeSet(from,to);
}
public Set keySet() {
return new KeySubSet(from,to);
}
public Collection values(){
return new ValueSubSet(from,to);
}
public Object put(Object key, Object value){
checkRange(key);
return TreeMap.this.put(key,value);
}
public Object remove(Object key){
if ((from != leaf && compare(key,from) < 0)
|| (to != leaf && compare(key,to) >= 0)){
return null;
}
return TreeMap.this.remove(key);
}
}
private class KeySubSet extends AbstractSet {
private Object from;
private Object to;
private int smc;
private int size;
public KeySubSet(Object fr, Object t){
from = fr;
to = t;
smc = -1;
}
public int size(){
if (smc != modcount) {
size = calculateSize(from,to);
}
return size;
}
public Iterator iterator(){
Node n = null;
try {
n = locateLowerNode(from);
}
catch(NoSuchElementException nse){}//null will do in the constructor
return new SetIterator(n,to,1);
}
}
private class ValueSubSet extends AbstractCollection {
private Object from;
private Object to;
private int smc;
private int size;
public ValueSubSet(Object fr, Object t){
from = fr;
to = t;
smc = -1;
}
public synchronized int size(){
if (smc != modcount) {
size = calculateSize(from,to);
}
return size;
}
public Iterator iterator(){
Node n = null;
try {
n = locateLowerNode(from);
}
catch(NoSuchElementException nse){}//null will do in the constructor
return new SetIterator(n,to,-1);
}
}
private class SubTreeSet extends AbstractSet {
private Object from;
private Object to;
private int smc;
private int size;
public SubTreeSet(Object fr, Object t){
from = fr;
to = t;
smc = -1;
}
public synchronized int size(){
if (smc != modcount) {
size = calculateSize(from,to);
}
return size;
}
public Iterator iterator(){
Node n = null;
try {
n = locateLowerNode(from);
}
catch(NoSuchElementException nse){}//null will do in the constructor
return new SetIterator(n,to,0);
}
}
private class SetIterator implements Iterator{
private Node node;
private int mc;
private Object end;
private Node prevKey;
private int rType;
public SetIterator(Node n, Object to, int returnType){
if (n == leaf){
n = null;
}
node = n;
end = to;
mc = modcount;
prevKey=leaf;
rType = returnType;
}
public boolean hasNext(){
return (node != null);
}
public Object next(){
if (node == null){
throw new NoSuchElementException();
}
if (mc != modcount) {
throw new ConcurrentModificationException();
}
prevKey = node;
Object ret;
if (rType == 0) {
ret = new MapEntry(node);
}
else if (rType == 1){
ret = node.key;
}
else {
ret = node.value;
}
node = nextNode(node);
if (end != leaf && node != null && compare(node.key,end) >= 0){
node = null;
}
return ret;
}
public void remove(){
if (prevKey == leaf){
throw new IllegalStateException();
}
if (mc != modcount) {
throw new ConcurrentModificationException();
}
lastFind = prevKey;
if(node != null){
Object key = node.key;
TreeMap.this.remove(prevKey.key);
node = getNode(key);
}
else {
TreeMap.this.remove(prevKey.key);
}
prevKey = leaf;
mc++;
}
}
private class MapEntry implements Map.Entry {
private Object key;
private Object value;
public MapEntry(Node n) {
key = n.key;
value = n.value;
}
public Object getKey() {
return key;
}
public Object getValue() {
return value;
}
public Object setValue(Object nv) {
Object old = value;
TreeMap.this.put(key,nv);
value = nv;
return old;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))return false;
Map.Entry e = (Map.Entry)o;
return ( (key == null ? e.getKey()==null : key.equals(e.getKey())) &&
(value == null ? e.getValue()==null : value.equals(e.getValue())));
}
public int hashCode() {
int kc = key == null ? 0 : key.hashCode();
int vc = value == null ? 0 : value.hashCode();
return kc ^ vc;
}
}
}