MessageBus/src/dorkbox/util/messagebus/common/HashMapTree.java

436 lines
11 KiB
Java

package dorkbox.util.messagebus.common;
import dorkbox.util.messagebus.common.adapter.JavaVersionAdapter;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Simple tree structure that is a map that contains a chain of keys to publish to a value.
* <p>
* NOT THREAD SAFE, each call must be protected by a read/write lock of some sort
*
* @author dorkbox, llc
* Date: 2/2/15
*/
public class HashMapTree<KEY, VALUE> {
private Map<KEY, HashMapTree<KEY, VALUE>> children;
private VALUE value;
private final int defaultSize;
private final float loadFactor;
public HashMapTree(final int defaultSize, final float loadFactor) {
this.defaultSize = defaultSize;
this.loadFactor = loadFactor;
}
/**
* can be overridden to provide a custom backing map
*/
protected Map<KEY, HashMapTree<KEY, VALUE>> createChildren(int defaultSize, float loadFactor) {
return JavaVersionAdapter.concurrentMap(defaultSize, loadFactor, 1);
}
public final VALUE getValue() {
return this.value;
}
public final void putValue(VALUE value) {
this.value = value;
}
public final void removeValue() {
this.value = null;
}
public final void clear() {
if (this.children != null) {
Set<Entry<KEY, HashMapTree<KEY, VALUE>>> entrySet = this.children.entrySet();
for (Entry<KEY, HashMapTree<KEY, VALUE>> entry : entrySet) {
entry.getValue().clear();
}
this.children.clear();
this.value = null;
}
}
/**
* This <b>IS NOT</b> safe to call outside of root.remove(...)
* <p>
* Removes a branch from the tree, and cleans up, if necessary
*/
public final void remove(KEY key) {
if (key != null) {
removeLeaf(key);
}
}
/**
* This <b>IS NOT</b> safe to call outside of root.remove(...)
* <p>
* Removes a branch from the tree, and cleans up, if necessary
*/
public final void remove(KEY key1, KEY key2) {
if (key1 == null || key2 == null) {
return;
}
HashMapTree<KEY, VALUE> leaf;
if (this.children != null) {
leaf = this.children.get(key1);
if (leaf != null) {
leaf.removeLeaf(key2);
this.children.remove(key1);
if (this.children.isEmpty()) {
this.children = null;
}
}
}
}
/**
* This <b>IS NOT</b> safe to call outside of root.remove(...)
* <p>
* Removes a branch from the tree, and cleans up, if necessary
*/
public final void remove(KEY key1, KEY key2, KEY key3) {
if (key1 == null || key2 == null) {
return;
}
HashMapTree<KEY, VALUE> leaf;
if (this.children != null) {
leaf = this.children.get(key1);
if (leaf != null) {
leaf.remove(key2, key3);
this.children.remove(key1);
if (this.children.isEmpty()) {
this.children = null;
}
}
}
}
/**
* This <b>IS NOT</b> safe to call outside of root.remove(...)
* <p>
* Removes a branch from the tree, and cleans up, if necessary
*/
@SuppressWarnings("unchecked")
public final void remove(KEY... keys) {
if (keys == null) {
return;
}
removeLeaf(0, keys);
}
/**
* Removes a branch from the tree, and cleans up, if necessary
*/
private void removeLeaf(KEY key) {
if (key != null) {
if (this.children != null) {
HashMapTree<KEY, VALUE> leaf = this.children.get(key);
if (leaf != null) {
leaf = this.children.get(key);
if (leaf != null) {
if (leaf.children == null && leaf.value == null) {
this.children.remove(key);
}
if (this.children.isEmpty()) {
this.children = null;
}
}
}
}
}
}
// keys CANNOT be null here!
private void removeLeaf(int index, KEY[] keys) {
if (index == keys.length) {
// we have reached the leaf to remove!
this.value = null;
this.children = null;
} else if (this.children != null) {
HashMapTree<KEY, VALUE> leaf = this.children.get(keys[index]);
if (leaf != null) {
leaf.removeLeaf(index+1, keys);
if (leaf.children == null && leaf.value == null) {
this.children.remove(keys[index]);
}
if (this.children.isEmpty()) {
this.children = null;
}
}
}
}
public final VALUE put(VALUE value, KEY key) {
if (key == null) {
throw new NullPointerException("keys");
}
// have to put value into our children
HashMapTree<KEY, VALUE> leaf = createLeaf(key);
VALUE prev = leaf.value;
leaf.value = value;
return prev;
}
public final VALUE put(VALUE value, KEY key1, KEY key2) {
if (key1 == null || key2 == null) {
throw new NullPointerException("keys");
}
// have to put value into our children
HashMapTree<KEY, VALUE> leaf = createLeaf(key1);
leaf = leaf.createLeaf(key2);
VALUE prev = leaf.value;
leaf.value = value;
return prev;
}
public final VALUE put(VALUE value, KEY key1, KEY key2, KEY key3) {
if (key1 == null || key2 == null || key3 == null) {
throw new NullPointerException("keys");
}
// have to put value into our children
HashMapTree<KEY, VALUE> leaf = createLeaf(key1);
leaf = leaf.createLeaf(key2);
leaf = leaf.createLeaf(key3);
VALUE prev = leaf.value;
leaf.value = value;
return prev;
}
public final VALUE put(VALUE value, KEY... keys) {
if (keys == null) {
throw new NullPointerException("keys");
}
int length = keys.length;
// have to put value into our children
HashMapTree<KEY, VALUE> leaf = createLeaf(keys[0]);
for (int i=1;i<length;i++) {
leaf = leaf.createLeaf(keys[i]);
}
VALUE prev = leaf.value;
leaf.value = value;
return prev;
}
public final HashMapTree<KEY, VALUE> createLeaf(KEY... keys) {
if (keys == null) {
return this;
}
int length = keys.length;
// have to put value into our children
HashMapTree<KEY, VALUE> leaf = createLeaf(keys[0]);
for (int i=1;i<length;i++) {
leaf = leaf.createLeaf(keys[i]);
}
return leaf;
}
private HashMapTree<KEY, VALUE> createLeaf(KEY key) {
if (key == null) {
return null;
}
HashMapTree<KEY, VALUE> objectTree;
if (this.children == null) {
this.children = createChildren(this.defaultSize, this.loadFactor);
}
objectTree = this.children.get(key);
// make sure we have a tree for the specified node
if (objectTree == null) {
objectTree = new HashMapTree<KEY, VALUE>(this.defaultSize, this.loadFactor);
this.children.put(key, objectTree);
}
return objectTree;
}
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
public final VALUE get(KEY key) {
if (key == null) {
return null;
}
HashMapTree<KEY, VALUE> objectTree;
// publish value from our children
objectTree = getLeaf(key); // protected by lock
if (objectTree == null) {
return null;
}
return objectTree.value;
}
public final VALUE get(KEY key1, KEY key2) {
HashMapTree<KEY, VALUE> tree;
// publish value from our children
tree = getLeaf(key1); // protected by lock
if (tree != null) {
tree = tree.getLeaf(key2); // protected by lock
}
if (tree == null) {
return null;
}
return tree.value;
}
public final VALUE get(KEY key1, KEY key2, KEY key3) {
HashMapTree<KEY, VALUE> tree;
// publish value from our children
tree = getLeaf(key1);
if (tree != null) {
tree = tree.getLeaf(key2);
}
if (tree != null) {
tree = tree.getLeaf(key3);
}
if (tree == null) {
return null;
}
return tree.value;
}
@SuppressWarnings("unchecked")
public final VALUE get(KEY... keys) {
HashMapTree<KEY, VALUE> tree;
// publish value from our children
tree = getLeaf(keys[0]);
int size = keys.length;
for (int i=1;i<size;i++) {
if (tree != null) {
tree = tree.getLeaf(keys[i]);
} else {
return null;
}
}
if (tree == null) {
return null;
}
return tree.value;
}
public final HashMapTree<KEY, VALUE> getLeaf(KEY key) {
if (key == null) {
return null;
}
HashMapTree<KEY, VALUE> tree;
if (this.children == null) {
tree = null;
} else {
tree = this.children.get(key);
}
return tree;
}
public final HashMapTree<KEY, VALUE> getLeaf(KEY key1, KEY key2) {
HashMapTree<KEY, VALUE> tree;
// publish value from our children
tree = getLeaf(key1);
if (tree != null) {
tree = tree.getLeaf(key2);
}
return tree;
}
public final HashMapTree<KEY, VALUE> getLeaf(KEY key1, KEY key2, KEY key3) {
HashMapTree<KEY, VALUE> tree;
// publish value from our children
tree = getLeaf(key1);
if (tree != null) {
tree = tree.getLeaf(key2);
}
if (tree != null) {
tree = tree.getLeaf(key3);
}
return tree;
}
@SuppressWarnings("unchecked")
public final HashMapTree<KEY, VALUE> getLeaf(KEY... keys) {
int size = keys.length;
if (size == 0) {
return null;
}
HashMapTree<KEY, VALUE> tree;
// publish value from our children
tree = getLeaf(keys[0]);
for (int i=1;i<size;i++) {
if (tree != null) {
tree = tree.getLeaf(keys[i]);
} else {
return null;
}
}
return tree;
}
}