/*
* Copyright (c) 2015 NOVA, All rights reserved.
* This library is free software, licensed under GNU Lesser General Public License version 3
*
* This file is part of NOVA.
*
* NOVA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NOVA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NOVA. If not, see <http://www.gnu.org/licenses/>.
*/
package nova.core.util.collection;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.stream.Stream;
/**
* A node inside of a tree structure.
*
* @param <S> - Self type
*/
public class TreeNode<S extends TreeNode> implements Iterable<S> {
/**
* The children of the node.
*/
public final Set<S> children = new HashSet<>();
/**
* The parent of the node. The root node has no parent.
*/
protected Optional<S> parent;
/**
* The parrent of this node
*
* @return The parrent of this node if there is one, otherwise Optional.empty
*/
public Optional<S> getParent() {
return parent;
}
/**
* Set's the parrent of this node
*
* @param parent The new parrent of this node
*/
public void setParent(Optional<S> parent) {
this.parent = parent;
}
/**
* Adds a childnode to the node
*
* @param node The childnode to add
* @return The merged node
*/
public S addChild(S node) {
children.add(node);
node.parent = Optional.of(this);
return node;
}
/**
* Remove a childnode
*
* @param child The childnode to remove
* @return This
*/
@SuppressWarnings("unchecked")
public S removeChild(S child) {
children.remove(child);
child.setParent(Optional.empty());
return (S) this;
}
/**
* Get the direct childnodes
*
* @return A set containing the direct childnodes
*/
@SuppressWarnings("unchecked")
public Set<S> children() {
return children;
}
/**
* Gets all child nodes recursively
*
* @return A set containing all child nodes and their descendants
*/
@SuppressWarnings("unchecked")
public Set<S> descendants() {
Set<S> perms = new HashSet<>();
for (S child : children) {
perms.add(child);
perms.addAll(child.descendants());
}
return perms;
}
/**
* Checks recursively to see if any of the children can match the given child.
*
* @param targetChild - The target match to find.
* @return True if the tree structure contains the targetPerm.
*/
@SuppressWarnings("unchecked")
public boolean exists(S targetChild) {
if (equals(targetChild)) {
return true;
}
for (S child : children) {
if (child.exists(targetChild)) {
return true;
}
}
return false;
}
/**
* @return Gets the hierarchy of the tree ordered from the root node to the current node.
*/
@SuppressWarnings("unchecked")
public List<S> hierarchy() {
List<S> hierarchy = new ArrayList<>();
Optional<S> currentParent = parent;
while (currentParent.isPresent()) {
hierarchy.add(currentParent.get());
currentParent = currentParent.get().getParent();
}
return Lists.reverse(hierarchy);
}
public Stream<S> stream() {
return children.stream();
}
@Override
public Iterator<S> iterator() {
return children.iterator();
}
@Override
public Spliterator<S> spliterator() {
return children.spliterator();
}
}