/* * 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.CollectionUtils; import com.jcwhatever.nucleus.utils.PreCon; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.annotation.Nullable; /** * An iterable tree node. * * <p>Recursively iterates starting from the node to all child nodes. Does not repeat * any element.</p> */ public class TreeNode<T> implements Iterable<TreeNode<T>> { private T _node; private TreeNode<T> _parent; private List<TreeNode<T>> _children; /** * Constructor. * * @param nodeValue The node value. */ public TreeNode(T nodeValue) { PreCon.notNull(nodeValue); _node = nodeValue; } /** * Determine if this 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 value. */ public T getValue() { return _node; } /** * Get the nodes parent. * * @return Null if the node is a root node. */ @Nullable public TreeNode<T> getParent() { return _parent; } /** * Get the nodes children. */ public List<TreeNode<T>> getChildren() { //noinspection unchecked return _children != null ? CollectionUtils.unmodifiableList(_children) : CollectionUtils.UNMODIFIABLE_EMPTY_LIST; } /** * Get the number of direct children * of the node. */ public int size() { if (_children == null) return 0; return _children.size(); } /** * Get the index of the direct child. * * @param childValue The child value. * * @return The list index or -1 if not found. */ public int getIndex(T childValue) { if (_children == null) return -1; for (int i=0; i < _children.size(); i++) { if (childValue.equals(_children.get(i).getValue())) return i; } return -1; } /** * Get the index of the direct child node. * * @param childNode The child node. * * @return The list index or -1 if not found. */ public int getIndex(TreeNode<T> childNode) { if (_children == null) return -1; for (int i=0; i < _children.size(); i++) { if (childNode.equals(_children.get(i))) return i; } return -1; } /** * Add a child node. * * @param childValue The child value. * * @return The child node. */ public TreeNode<T> add(T childValue) { TreeNode<T> node = new TreeNode<>(childValue); node._parent = this; if (_children == null) _children = new ArrayList<TreeNode<T>>(5); _children.add(node); return node; } /** * Add a child node. * * @param childNode The child node. * * @return The child node. */ public TreeNode<T> add(TreeNode<T> childNode) { childNode._parent = this; if (_children == null) _children = new ArrayList<TreeNode<T>>(5); _children.add(childNode); return childNode; } /** * Remove a child node. * * @param child The child value. * * @return The removed child node. Null if not found. */ @Nullable public TreeNode<T> remove(T child) { if (_children == null) return null; Iterator<TreeNode<T>> iterator = _children.iterator(); while (iterator.hasNext()) { TreeNode<T> childNode = iterator.next(); if (child.equals(childNode.getValue())) { iterator.remove(); childNode._parent = null; return childNode; } } return null; } /** * Remove a child node. * * @param childNode The child node. * * @return The removed child node. Null if not found. */ @Nullable public TreeNode<T> remove(TreeNode<T> childNode) { if (_children == null) return null; Iterator<TreeNode<T>> iterator = _children.iterator(); while (iterator.hasNext()) { TreeNode<T> node = iterator.next(); if (childNode.equals(node)) { iterator.remove(); node._parent = null; return node; } } return null; } /** * Clear all child nodes. */ public void clear() { if (_children == null) return; Iterator<TreeNode<T>> iterator = _children.iterator(); while (iterator.hasNext()) { TreeNode<T> node = iterator.next(); node._parent = null; iterator.remove(); } } @Override public Iterator<TreeNode<T>> iterator() { return new Iter(); } @Override public int hashCode() { return _node.hashCode(); } @Override public boolean equals(Object object) { if (object instanceof TreeNode) { return _node.equals(((TreeNode) object)._node); } return _node.equals(object); } // Tree node iterator. Iterates all children and sub children from the top down, // does not repeat any element. private class Iter implements Iterator<TreeNode<T>> { LinkedList<StackIterator> recursionStack = new LinkedList<>(); StackIterator currentIterator; TreeNode<T> currentNode; boolean iterSelf; Iter() { if (!isLeaf()) { currentIterator = new StackIterator(_children.iterator()); recursionStack.push(currentIterator); } } @Override public boolean hasNext() { if (!iterSelf) return true; if (recursionStack.isEmpty()) return false; currentIterator = recursionStack.peek(); boolean hasNext = currentIterator.hasNext(); while (!hasNext) { recursionStack.pop(); currentIterator = recursionStack.peek(); if (currentIterator == null) return false; hasNext = currentIterator.hasNext(); } return true; } @Override public TreeNode<T> next() { if (iterSelf) { currentNode = currentIterator.next(); if (!currentNode.isLeaf()) { recursionStack.push(new StackIterator(currentNode._children.iterator())); } } else { iterSelf = true; currentNode = TreeNode.this; } return currentNode; } @Override public void remove() { currentIterator.remove(); if (!currentIterator.hasNext()) { recursionStack.pop(); currentIterator = recursionStack.peek(); } } } // used to track recursion info private class StackIterator { Iterator<TreeNode<T>> childIterator; Boolean hasNext; StackIterator(Iterator<TreeNode<T>> iterator) { this.childIterator = iterator; } boolean hasNext() { if (hasNext == null) { hasNext = childIterator.hasNext(); } return hasNext; } TreeNode<T> next() { hasNext = null; return childIterator.next(); } void remove() { childIterator.remove(); } } }