/*
* This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT).
*
* Copyright (c) JCThePants (www.jcwhatever.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.jcwhatever.nucleus.collections;
import com.jcwhatever.nucleus.utils.PreCon;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nullable;
/**
* An iterable tree node whose nodes have key values.
*
* <p>Recursively iterates starting from the node to all child nodes. Does not repeat
* any element. Order of a nodes child nodes when iterated is not guaranteed. It is
* guaranteed that the parent node will be iterated just before its child nodes are.</p>
*/
public class TreeEntryNode<K, V> implements Iterable<TreeEntryNode<K, V>> {
private Entry<K, V> _node;
private TreeEntryNode<K, V> _parent;
private Map<K, TreeEntryNode<K, V>> _children;
/**
* Constructor.
*
* @param key The node key
* @param value The node value.
*/
public TreeEntryNode(K key, @Nullable V value) {
PreCon.notNull(key);
_node = new NodeEntry<>(key, value);
}
/**
* Determine if the node is the root node.
*/
public boolean isRoot() {
return _parent == null;
}
/**
* Determine if the node is a leaf
* node (has no children).
*/
public boolean isLeaf() {
return _children == null || _children.size() == 0;
}
/**
* Get the level/depth of the node. The root
* node is 0.
*/
public int getDepth() {
return _parent == null ? 0 : _parent.getDepth() + 1;
}
/**
* Get the nodes key.
*/
public K getKey() {
return _node.getKey();
}
/**
* Get the nodes value.
*/
@Nullable
public V getValue() {
return _node.getValue();
}
/**
* Get the nodes value.
*/
@Nullable
public void setValue(@Nullable V value) {
_node.setValue(value);
}
/**
* Get the nodes parent.
*
* @return Null if the node is a root node.
*/
@Nullable
public TreeEntryNode<K, V> getParent() {
return _parent;
}
/**
* Get the nodes children.
*/
public Collection<TreeEntryNode<K, V>> getChildren() {
return _children != null
? Collections.unmodifiableCollection(_children.values())
: new ArrayList<TreeEntryNode<K, V>>(0);
}
/**
* Get the child key set.
*/
public Set<K> getChildKeySet() {
return _children != null
? new HashSet<>(_children.keySet())
: new HashSet<K>(0);
}
/**
* Get the child key set.
*
* @param output The output collection to add results to.
*
* @return The output collection.
*/
public <T extends Collection<K>> T getChildKeySet(T output) {
PreCon.notNull(output);
if (_children == null)
return output;
output.addAll(_children.keySet());
return output;
}
/**
* Get the number of direct children
* of the node.
*/
public int totalChildren() {
if (_children == null)
return 0;
return _children.size();
}
/**
* Clear all children from the node.
*/
public void clearChildren() {
if (_children != null)
_children.clear();
}
/**
* Determine if the node has a child
* with the specified key.
*
* @param key The key to check.
*/
public boolean hasChild(K key) {
return _children != null && _children.containsKey(key);
}
/**
* Get a child node.
*
* @param key The child node key.
*
* @return The list index or -1 if not found.
*/
@Nullable
public TreeEntryNode<K, V> getChild(K key) {
if (_children == null)
return null;
return _children.get(key);
}
/**
* Add a child node.
*
* @param key The child key.
* @param value The child value.
*
* @return The created child node.
*/
public TreeEntryNode<K, V> putChild(K key, V value) {
TreeEntryNode<K, V> node = new TreeEntryNode<>(key, value);
node._parent = this;
if (_children == null)
_children = new HashMap<>(7);
_children.put(key, node);
return node;
}
/**
* Add a child node.
*
* @param childNode The child node.
*
* @return The previous child node of the same key.
*/
@Nullable
public TreeEntryNode<K, V> putChild(TreeEntryNode<K, V> childNode) {
childNode._parent = this;
if (_children == null)
_children = new HashMap<>(7);
return _children.put(childNode.getKey(), childNode);
}
/**
* Remove a child node.
*
* @param key The child key.
*
* @return The removed child node. Null if not found.
*/
@Nullable
public TreeEntryNode<K, V> removeChild(K key) {
if (_children == null)
return null;
return _children.remove(key);
}
/**
* Remove a child node.
*
* @param childNode The child node.
*
* @return The removed child node. Null if not found.
*/
@Nullable
public TreeEntryNode<K, V> removeChild(TreeEntryNode<K, V> childNode) {
PreCon.notNull(childNode);
if (_children == null)
return null;
return _children.remove(childNode.getKey());
}
@Override
public Iterator<TreeEntryNode<K, V>> iterator() {
return new Iter();
}
private static class NodeEntry<K, V> implements Entry<K, V> {
final K key;
V value;
NodeEntry(K key, @Nullable V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
@Nullable
public V getValue() {
return value;
}
@Override
public V setValue(@Nullable V value) {
V old = this.value;
this.value = value;
return old;
}
}
// Tree node iterator. Iterates all children and sub children from the top down,
// does not repeat any element. Order is not guaranteed.
private class Iter implements Iterator<TreeEntryNode<K, V>> {
LinkedList<StackIterator> recursionStack = new LinkedList<>();
StackIterator current;
Iter() {
if (!isLeaf()) {
current = new StackIterator(_children.values().iterator());
recursionStack.push(current);
}
}
@Override
public boolean hasNext() {
return !recursionStack.isEmpty() && recursionStack.peek().childIterator.hasNext();
}
@Override
public TreeEntryNode<K, V> next() {
current = recursionStack.peek();
TreeEntryNode<K, V> currentNode = current.childIterator.next();
if (!current.childIterator.hasNext())
recursionStack.pop();
if (!currentNode.isLeaf()) {
recursionStack.push(new StackIterator(currentNode._children.values().iterator()));
}
return currentNode;
}
@Override
public void remove() {
current.childIterator.remove();
if (!current.childIterator.hasNext()) {
recursionStack.pop();
current = recursionStack.peek();
}
}
}
// used to track recursion info
private class StackIterator {
Iterator<TreeEntryNode<K, V>> childIterator;
StackIterator(Iterator<TreeEntryNode<K, V>> iterator) {
this.childIterator = iterator;
}
}
}