/*******************************************************************************
* 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.
******************************************************************************/
package de.mxro.thrd.jdbm2V22.btree;
import java.io.IOError;
import java.io.IOException;
import java.util.AbstractSet;
import java.util.Comparator;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedMap;
import de.mxro.thrd.jdbm2V22.PrimaryTreeMap;
import de.mxro.thrd.jdbm2V22.RecordListener;
import de.mxro.thrd.jdbm2V22.RecordManager;
import de.mxro.thrd.jdbm2V22.helper.AbstractPrimaryMap;
import de.mxro.thrd.jdbm2V22.helper.Tuple;
import de.mxro.thrd.jdbm2V22.helper.TupleBrowser;
public class BTreeSortedMap<K,V> extends AbstractPrimaryMap<K,V> implements PrimaryTreeMap<K,V>{
protected final BTree<K,V> tree;
protected final K fromKey;
protected final K toKey;
protected final boolean readonly;
public BTreeSortedMap(BTree<K,V> tree, boolean readonly){
this(tree,readonly, null,null);
}
protected BTreeSortedMap(BTree<K,V> tree, boolean readonly, K fromKey, K toKey){
this.tree = tree;
this.fromKey = fromKey;
this.toKey = toKey;
this.readonly = readonly;
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return new AbstractSet<Entry<K,V>>(){
protected Entry<K,V> newEntry(K k,V v){
return new SimpleEntry<K,V>(k,v){
private static final long serialVersionUID = 978651696969194154L;
public V setValue(V arg0) {
BTreeSortedMap.this.put(getKey(), arg0);
return super.setValue(arg0);
};
};
}
public boolean add(java.util.Map.Entry<K, V> e) {
if(readonly)
throw new UnsupportedOperationException("readonly");
try {
if(e.getKey() == null)
throw new NullPointerException("Can not add null key");
if(!inBounds(e.getKey()))
throw new IllegalArgumentException("key outside of bounds");
return tree.insert(e.getKey(), e.getValue(), true) == null;
} catch (IOException e1) {
throw new IOError(e1);
}
}
@SuppressWarnings("unchecked")
public boolean contains(Object o) {
if(o instanceof Entry){
Entry<K,V> e = (java.util.Map.Entry<K, V>) o;
try {
if(!inBounds(e.getKey()))
return false;
if(e.getKey()!=null && tree.find(e.getKey())!=null)
return true;
} catch (IOException e1) {
throw new IOError(e1);
}
}
return false;
}
public Iterator<java.util.Map.Entry<K, V>> iterator() {
try {
final TupleBrowser<K,V> br = fromKey==null ?
tree.browse() : tree.browse(fromKey);
return new Iterator<Entry<K,V>>(){
private Entry<K,V> next;
private K lastKey;
void ensureNext(){
try{
Tuple<K,V> t = new Tuple<K,V>();
if(br.getNext(t) && inBounds(t.getKey()))
next = newEntry(t.getKey(),t.getValue());
else
next = null;
} catch (IOException e1) {
throw new IOError(e1);
}
}
{
ensureNext();
}
public boolean hasNext() {
return next!=null;
}
public java.util.Map.Entry<K, V> next() {
if(next == null)
throw new NoSuchElementException();
Entry<K,V> ret = next;
lastKey = ret.getKey();
//move to next position
ensureNext();
return ret;
}
public void remove() {
if(readonly)
throw new UnsupportedOperationException("readonly");
if(lastKey == null)
throw new IllegalStateException();
BTreeSortedMap.this.remove(lastKey);
lastKey = null;
}};
} catch (IOException e) {
throw new IOError(e);
}
}
@SuppressWarnings("unchecked")
public boolean remove(Object o) {
if(readonly)
throw new UnsupportedOperationException("readonly");
if(o instanceof Entry){
Entry<K,V> e = (java.util.Map.Entry<K, V>) o;
try {
//check for nulls
if(e.getKey() == null || e.getValue() == null)
return false;
if(!inBounds(e.getKey()))
throw new IllegalArgumentException("out of bounds");
//find old value, must be same as item in entry
V v = get(e.getKey());
if(v == null || !e.getValue().equals(v))
return false;
V v2 = tree.remove(e.getKey());
return v2!=null;
} catch (IOException e1) {
throw new IOError(e1);
}
}
return false;
}
@SuppressWarnings("unchecked")
public int size() {
if(fromKey == null && toKey == null)
return tree.size();
else{
int counter = 0;
Iterator i = iterator();
while(i.hasNext()){
i.next();
counter++;
}
return counter;
}
}
};
}
public boolean inBounds(K e) {
if(fromKey!=null && comparator().compare(e,fromKey)<0)
return false;
if(toKey!=null && comparator().compare(e,toKey)>=0)
return false;
return true;
}
@SuppressWarnings("unchecked")
@Override
public V get(Object key) {
try{
if(key == null)
return null;
if(!inBounds((K)key))
return null;
return tree.find((K) key);
}catch (ClassCastException e){
return null;
}catch (IOException e){
throw new IOError(e);
}
}
@SuppressWarnings("unchecked")
@Override
public V remove(Object key) {
if(readonly)
throw new UnsupportedOperationException("readonly");
try{
if(key == null || tree.find((K) key)== null)
return null;
if(!inBounds((K) key))
throw new IllegalArgumentException("out of bounds");
return tree.remove((K) key);
}catch (ClassCastException e){
return null;
}catch (IOException e){
throw new IOError(e);
}
}
public V put(K key, V value) {
if(readonly)
throw new UnsupportedOperationException("readonly");
try {
if(key == null || value == null)
throw new NullPointerException("Null key or value");
if(!inBounds(key))
throw new IllegalArgumentException("out of bounds");
return tree.insert(key, value, true);
} catch (IOException e) {
throw new IOError(e);
}
};
@SuppressWarnings("unchecked")
@Override
public boolean containsKey(Object key) {
if(key == null)
return false;
try {
if(!inBounds((K) key))
return false;
V v = tree.find((K) key);
return v!=null;
} catch (IOException e) {
throw new IOError(e);
} catch (ClassCastException e){
return false;
}
}
public Comparator<? super K> comparator() {
return tree._comparator;
}
public K firstKey() {
if(size() == 0)
throw new NoSuchElementException();
try{
TupleBrowser<K,V> b = fromKey==null?tree.browse():tree.browse(fromKey);
Tuple<K,V> t = new Tuple<K,V>();
b.getNext(t);
return t.getKey();
}catch(IOException e){
throw new IOError(e);
}
}
public K lastKey() {
if(size() == 0)
throw new NoSuchElementException();
try{
TupleBrowser<K,V> b = toKey==null?tree.browse(null):tree.browse(toKey);
Tuple<K,V> t = new Tuple<K,V>();
b.getPrevious(t);
return t.getKey();
}catch(IOException e){
throw new IOError(e);
}
}
public SortedMap<K, V> headMap(K toKey) {
return new BTreeSortedMap<K, V>(tree, readonly,null, toKey);
}
public SortedMap<K, V> subMap(K fromKey, K toKey) {
if(comparator().compare(fromKey,toKey)>0)
throw new IllegalArgumentException("fromKey is bigger then toKey");
return new BTreeSortedMap<K, V>(tree, readonly,fromKey, toKey);
}
public SortedMap<K, V> tailMap(K fromKey) {
return new BTreeSortedMap<K, V>(tree, readonly,fromKey, null);
}
public BTree<K, V> getTree() {
return tree;
}
public void addRecordListener(RecordListener<K, V> listener) {
tree.addRecordListener(listener);
}
public RecordManager getRecordManager() {
return tree.getRecordManager();
}
public void removeRecordListener(RecordListener<K, V> listener) {
tree.removeRecordListener(listener);
}
public Integer newIntegerKey() {
if(isEmpty())
return new Integer(0);
K k = lastKey();
return new Integer(((Integer)k).intValue()+1);
}
public Long newLongKey() {
if(isEmpty())
return new Long(0);
K k = lastKey();
return new Long(((Long)k).longValue()+1);
}
public void clear(){
Iterator<K> keyIter = keySet().iterator();
while(keyIter.hasNext())
remove(keyIter.next());
}
}