/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed 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.
******************************************************************************/
/*
* 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.jdbm;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
/**
* B-Tree Map which uses primitive long as key.
* Main advantage is new instanceof of Long does not have to be created for each lookup.
* <p/>
* This code comes from Android, which in turns comes to Apache Harmony.
* This class was modified to use primitive longs and stripped down to consume less space.
* <p/>
* Author of JDBM modifications: Jan Kotek
* <p/>
* It is much slower then LongKeyChainedHashMap, but may be usefull in future for better licence.
*
* @param <V>
*/
public class LongTreeMap<V> {
private Entry<V> root;
private int size;
/**
* counts modifications to throw ConcurrentAccessException
*/
private transient int modCount;
/**
* Returns the value of the mapping with the specified key.
*
* @param key the key.
* @return the value of the mapping with the specified key.
* @throws ClassCastException if the key cannot be compared with the keys in this map.
* @throws NullPointerException if the key is {@code null} and the comparator cannot handle
* {@code null}.
* @since Android 1.0
*/
public V get(long key) {
Entry<V> node = find(key);
if (node != null) {
return node.value;
}
return null;
}
/**
* Maps the specified key to the specified value.
*
* @param key the key.
* @param value the value.
* @return the value of any previous mapping with the specified key or
* {@code null} if there was no mapping.
* @throws ClassCastException if the specified key cannot be compared with the keys in this
* map.
* @throws NullPointerException if the specified key is {@code null} and the comparator
* cannot handle {@code null} keys.
* @since Android 1.0
*/
public V put(long key, V value) {
Entry<V> entry = rbInsert(key);
V result = entry.value;
entry.value = value;
return result;
}
/**
* Removes the mapping with the specified key from this map.
*
* @param key the key of the mapping to remove.
* @return the value of the removed mapping or {@code null} if no mapping
* for the specified key was found.
* @throws ClassCastException if the specified key cannot be compared with the keys in this
* map.
* @throws NullPointerException if the specified key is {@code null} and the comparator
* cannot handle {@code null} keys.
* @since Android 1.0
*/
public V remove(long key) {
if (size == 0) {
return null;
}
Entry<V> node = find(key);
if (node == null) {
return null;
}
V result = node.value;
rbDelete(node);
return result;
}
/**
* Removes all mappings from this TreeMap, leaving it empty.
*
* @see Map#isEmpty()
* @see #size()
* @since Android 1.0
*/
public void clear() {
root = null;
size = 0;
modCount++;
}
/**
* Entry is an internal class which is used to hold the entries of a
* TreeMap.
*/
private static class Entry<V> {
Entry<V> parent, left, right;
long key;
V value;
boolean color;
Entry(long key, V value) {
this.key = key;
this.value = value;
}
public String toString() {
return super.toString() + " - " + key + " - " + value;
}
}
/**
* @return iterator over values in map
*/
public Iterator<V> valuesIterator() {
return new ValueIterator();
}
/**
* @return iterator over keys in map
*/
public LongIterator keyIterator() {
return new LongIterator();
}
private class MapIterator {
int expectedModCount;
Entry<V> node;
Entry<V> lastNode;
MapIterator() {
expectedModCount = modCount;
if (root != null)
node = minimum(root);
}
public boolean hasNext() {
return node != null;
}
final public void remove() {
if (expectedModCount == modCount) {
if (lastNode != null) {
rbDelete(lastNode);
lastNode = null;
expectedModCount++;
} else {
throw new IllegalStateException();
}
} else {
throw new ConcurrentModificationException();
}
}
final void makeNext() {
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
} else if (node == null) {
throw new NoSuchElementException();
}
lastNode = node;
node = successor(node);
}
}
private class ValueIterator extends MapIterator implements Iterator<V> {
public V next() {
makeNext();
return lastNode.value;
}
}
public class LongIterator extends MapIterator implements Iterator<Long> {
public Long next() {
makeNext();
return lastNode.key;
}
public long nextLong() {
makeNext();
return lastNode.key;
}
}
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
public String toString() {
String s = this.getClass().getName();
s += "[";
LongIterator iter = keyIterator();
boolean first = true;
while (iter.hasNext()) {
if (!first) {
s += ", ";
}
first = false;
long k = iter.nextLong();
s += k + "=" + get(k);
}
s += "]";
return s;
}
private Entry<V> find(long object) {
Entry<V> x = root;
while (x != null) {
// result = object != null ? object.compareTo(x.key) : comparator
// .compare(key, x.key);
// if (result == 0) {
// return x;
// }
// x = result < 0 ? x.left : x.right;
if (object == x.key)
return x;
x = object < x.key ? x.left : x.right;
}
return null;
}
private Entry<V> minimum(Entry<V> x) {
while (x.left != null) {
x = x.left;
}
return x;
}
Entry<V> successor(Entry<V> x) {
if (x.right != null) {
return minimum(x.right);
}
Entry<V> y = x.parent;
while (y != null && x == y.right) {
x = y;
y = y.parent;
}
return y;
}
void rbDelete(Entry<V> z) {
Entry<V> y = z.left == null || z.right == null ? z : successor(z);
Entry<V> x = y.left != null ? y.left : y.right;
if (x != null) {
x.parent = y.parent;
}
if (y.parent == null) {
root = x;
} else if (y == y.parent.left) {
y.parent.left = x;
} else {
y.parent.right = x;
}
modCount++;
if (y != z) {
z.key = y.key;
z.value = y.value;
}
if (!y.color && root != null) {
if (x == null) {
fixup(y.parent);
} else {
fixup(x);
}
}
size--;
}
private void fixup(Entry<V> x) {
Entry<V> w;
while (x != root && !x.color) {
if (x == x.parent.left) {
w = x.parent.right;
if (w == null) {
x = x.parent;
continue;
}
if (w.color) {
w.color = false;
x.parent.color = true;
leftRotate(x.parent);
w = x.parent.right;
if (w == null) {
x = x.parent;
continue;
}
}
if ((w.left == null || !w.left.color)
&& (w.right == null || !w.right.color)) {
w.color = true;
x = x.parent;
} else {
if (w.right == null || !w.right.color) {
w.left.color = false;
w.color = true;
rightRotate(w);
w = x.parent.right;
}
w.color = x.parent.color;
x.parent.color = false;
w.right.color = false;
leftRotate(x.parent);
x = root;
}
} else {
w = x.parent.left;
if (w == null) {
x = x.parent;
continue;
}
if (w.color) {
w.color = false;
x.parent.color = true;
rightRotate(x.parent);
w = x.parent.left;
if (w == null) {
x = x.parent;
continue;
}
}
if ((w.left == null || !w.left.color)
&& (w.right == null || !w.right.color)) {
w.color = true;
x = x.parent;
} else {
if (w.left == null || !w.left.color) {
w.right.color = false;
w.color = true;
leftRotate(w);
w = x.parent.left;
}
w.color = x.parent.color;
x.parent.color = false;
w.left.color = false;
rightRotate(x.parent);
x = root;
}
}
}
x.color = false;
}
private void leftRotate(Entry<V> x) {
Entry<V> y = x.right;
x.right = y.left;
if (y.left != null) {
y.left.parent = x;
}
y.parent = x.parent;
if (x.parent == null) {
root = y;
} else {
if (x == x.parent.left) {
x.parent.left = y;
} else {
x.parent.right = y;
}
}
y.left = x;
x.parent = y;
}
private void rightRotate(Entry<V> x) {
Entry<V> y = x.left;
x.left = y.right;
if (y.right != null) {
y.right.parent = x;
}
y.parent = x.parent;
if (x.parent == null) {
root = y;
} else {
if (x == x.parent.right) {
x.parent.right = y;
} else {
x.parent.left = y;
}
}
y.right = x;
x.parent = y;
}
private Entry<V> rbInsert(long object) {
boolean smaller = false;
Entry<V> y = null;
if (size != 0) {
Entry<V> x = root;
while (x != null) {
y = x;
// result = key != null ? key.compareTo(x.key) : comparator
// .compare(object, x.key);
// if (result == 0) {
// return x;
// }
// x = result < 0 ? x.left : x.right;
if (object == x.key)
return x;
if (object < x.key) {
x = x.left;
smaller = true;
} else {
x = x.right;
smaller = false;
}
}
}
size++;
modCount++;
Entry<V> z = new Entry<V>(object, null);
if (y == null) {
return root = z;
}
z.parent = y;
if (smaller) {
y.left = z;
} else {
y.right = z;
}
balance(z);
return z;
}
void balance(Entry<V> x) {
Entry<V> y;
x.color = true;
while (x != root && x.parent.color) {
if (x.parent == x.parent.parent.left) {
y = x.parent.parent.right;
if (y != null && y.color) {
x.parent.color = false;
y.color = false;
x.parent.parent.color = true;
x = x.parent.parent;
} else {
if (x == x.parent.right) {
x = x.parent;
leftRotate(x);
}
x.parent.color = false;
x.parent.parent.color = true;
rightRotate(x.parent.parent);
}
} else {
y = x.parent.parent.left;
if (y != null && y.color) {
x.parent.color = false;
y.color = false;
x.parent.parent.color = true;
x = x.parent.parent;
} else {
if (x == x.parent.left) {
x = x.parent;
rightRotate(x);
}
x.parent.color = false;
x.parent.parent.color = true;
leftRotate(x.parent.parent);
}
}
}
root.color = false;
}
}