/* * Copyright (c) 2008, Jan Stender, Bjoern Kolbeck, Mikael Hoegqvist, * Felix Hupfeld, Zuse Institute Berlin * * Licensed under the BSD License, see LICENSE file for details. * */ package de.mxro.thrd.babudb05.index.overlay; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.Map.Entry; import de.mxro.thrd.babudb05.api.database.ResultSet; import de.mxro.thrd.babudb05.index.OverlayMergeIterator; /** * A layered in-memory tree structure. * * @author stender * */ public class MultiOverlayTree<K, V> { static class OverlayTreeList<K, V> { public TreeMap<K, V> tree; public OverlayTreeList<K, V> next; public OverlayTreeList(TreeMap<K, V> tree, OverlayTreeList<K, V> next) { this.tree = tree; this.next = next; } } /** * value that marks an entry as deleted */ private final V nullValue; /** * Comparator for keys */ private Comparator<K> comparator; /** * the ID of the current overlay */ private int overlayId; /** * overlay ID -> sublist of overlay trees */ private Map<Integer, OverlayTreeList<K, V>> overlayMap; /** * the list of overlay trees */ private OverlayTreeList<K, V> treeList; /** * Creates a new multi-overlay tree. This call is equivalent to * <code>MultiOverlayTree(markerElement, null)</code>. * * @param nullValue * A value that will never be inserted in the tree. This value * will be used to mark entries as deleted. */ public MultiOverlayTree(V nullValue) { this(nullValue, null); } /** * Creates a new multi-overlay tree. * * @param nullValue * A value that will never be inserted in the tree. This value * will be used to mark entries as deleted. * @param comparator * The comparator for the keys. If a <code>null</code> comparator * is provided, the natural ordering of the keys will be used if * defined. */ public MultiOverlayTree(V nullValue, Comparator<K> comparator) { if (comparator == null) { this.comparator = new Comparator<K>() { public int compare(K o1, K o2) { return ((Comparable<K>) o1).compareTo(o2); } }; } else this.comparator = comparator; treeList = new OverlayTreeList<K, V>(new TreeMap<K, V>(comparator), null); overlayMap = Collections.synchronizedMap(new HashMap<Integer, OverlayTreeList<K, V>>()); this.nullValue = nullValue; } /** * Adds a new overlay to the tree. The new overlay becomes writable. * * @return the ID of the previous overlay */ public int newOverlay() { overlayMap.put(overlayId, treeList); treeList = new OverlayTreeList<K, V>(new TreeMap<K, V>(comparator), treeList); return overlayId++; } /** * Destroys any read-only overlay trees, such that only the current * read-write tree remains. */ public void cleanup() { overlayMap.clear(); treeList.next = null; overlayId = 0; } /** * Inserts a key-value pair in the LSM tree. If the value is * <code>null</code>, the key will be removed. * * @param key * the key * @param value * the value */ public void insert(K key, V value) { // delete ... if (value == null) treeList.tree.put(key, nullValue); // insert ... else treeList.tree.put(key, value); } /** * Retrieves the value for the given key in the current overlay. * * @param key * the key * @return the value associated with the key */ public V lookup(K key) { return lookup(key, treeList); } /** * Retrives the value for the given key in the given overlay. * * @param key * the key * @param overlayId * the overlay ID * @return the value associated with the key in the overlay associated with * the overlay ID */ public V lookup(K key, int overlayId) { return lookup(key, overlayMap.get(overlayId)); } /** * Returns an iterator with all values assocaited with keys between * <code>from</code> (inclusively) and <code>to</code> (exclusively). * * @param from * the first key (inclusively); if <code>null</code>, the first * key in the map will be used (inclusively) * @param to * the last key (exclusively); if <code>null</code>, the last key * in the map will be used (inclusively) * @param includeDeletedEntries * If <code>true</code>, entries that have been marked as deleted * will be included in the iterator. The value of such entries * will be the <code>nullValue</code> specified in the * constructor method. * @param ascending * If <code>true</code>, entries will be returned in ascending * order; otherwise, they will be returned in descending order * @return an iterator with values */ public ResultSet<K, V> rangeLookup(K from, K to, boolean includeDeletedEntries, boolean ascending) { return rangeLookup(from, to, treeList, includeDeletedEntries, ascending); } /** * Returns an iterator with all key-value pairs assocaited with keys between * <code>from</code> (inclusively) and <code>to</code> (exclusively) in the * given overlay tree. * * @param from * the first key (inclusively); if <code>null</code>, the first * key in the map will be used * @param to * the last key (exclusively); if <code>null</code>, the last key * in the map will be used (inclusively) * @param overlayId * the ID of the overlay * @param includeDeletedEntries * If <code>true</code>, entries that have been marked as deleted * will be included in the iterator. The value of such entries * will be the <code>nullValue</code> specified in the * constructor method. * @param ascending * If <code>true</code>, entries will be returned in ascending * order; otherwise, they will be returned in descending order * @return an iterator with key-value pairs */ public ResultSet<K, V> rangeLookup(K from, K to, int overlayId, boolean includeDeletedEntries, boolean ascending) { return rangeLookup(from, to, overlayMap.get(overlayId), includeDeletedEntries, ascending); } private V lookup(K key, OverlayTreeList<K, V> list) { for (; list != null; list = list.next) { V value = list.tree.get(key); if (value != null) return value; } return null; } private ResultSet<K, V> rangeLookup(K from, K to, OverlayTreeList<K, V> treeList, boolean includeDeletedEntries, boolean ascending) { // initialize a final list w/ submap iterators of all overlays final List<Iterator<Entry<K, V>>> itList = new ArrayList<Iterator<Entry<K, V>>>(); for (OverlayTreeList<K, V> list = treeList; list != null; list = list.next) { if (from != null && to != null) { // both boundaries are provided if (ascending) itList.add(list.tree.subMap(from, to).entrySet().iterator()); else itList.add(list.tree.descendingMap().subMap(from, to).entrySet().iterator()); } else if (from == null && to == null) { // no boundary is provided if (ascending) itList.add(list.tree.entrySet().iterator()); else itList.add(list.tree.descendingMap().entrySet().iterator()); } else if (from != null && to == null) { // only 'from' obundary is provided if (ascending) itList.add(list.tree.tailMap(from).entrySet().iterator()); else itList.add(list.tree.descendingMap().tailMap(from).entrySet().iterator()); } else { // only 'to' boundary is provided if (ascending) itList.add(list.tree.headMap(to).entrySet().iterator()); else itList.add(list.tree.descendingMap().headMap(to).entrySet().iterator()); } } return new OverlayMergeIterator<K, V>(itList, comparator, includeDeletedEntries ? null : nullValue, ascending); } }