/** * */ package org.minnal.utils; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Stack; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author ganeshs * */ public abstract class Node<T extends Node<T, P, V>, P extends Node<T, P, V>.NodePath, V> { private T parent; private V value; private LinkedList<T> children = new LinkedList<T>(); private static final Logger logger = LoggerFactory.getLogger(Node.class); public Node(V value) { this.value = value; } public V getValue() { return value; } /** * Checks if the value is already visited in the tree. Override this method to handle cycles * * @param value * @return */ protected boolean visited(T node) { return false; } private boolean checkVisited(T node, T child) { logger.debug("Checking if the node {} is visited by {}", child, node); if (node == null) { return false; } if (node.visited(child)) { logger.debug("Node {} is visited by {}", child, node); return true; } if (node.getParent() != null) { logger.debug("Checking if the node {} is visited by the parent of {}", child, node); return checkVisited(node.getParent(), child); } return false; } /** * Marks the value as visited in the tree. Override this method to handle cycles * * @param value */ protected void markVisited(T node) { } protected abstract T getThis(); public T addChild(T child) { logger.debug("Attempting to add the child {} to the node {}", child, getThis()); return addChild(child, false); } public T addChild(T child, boolean first) { if (parent == null) { logger.debug("Marking the current node {} as visited as this is the root", getThis()); markVisited(getThis()); } if (checkVisited(getThis(), child)) { logger.debug("Node {} is already visited by this node {} or one of its ancestors", child, getThis()); return null; } child.setParent(getThis()); if (first) { children.addFirst(child); } else { children.addLast(child); } markVisited(child); return child; } public boolean hasChildren() { return !children.isEmpty(); } public boolean hasNode() { return false; } /** * Does a DFT starting from this node. On every node visit calls the visitor with the node being visited * * @param visitor */ public void traverse(Visitor<T> visitor) { traverse(getThis(), visitor); } /** * Recursive implementation of DFT. On every node visit calls the visitor with the node being visited * * @param root * @param visitor */ private void traverse(T root, Visitor<T> visitor) { for (T child : root.getChildren()) { traverse(child, visitor); } visitor.visit(root); } /** * Traverses all the available paths. * * @param visitor */ public void traverse(PathVisitor<P, T> visitor) { LinkedList<T> list = new LinkedList<T>(); Stack<T> stack = new Stack<T>(); stack.add(getThis()); T node = null; P path = null; while(! stack.isEmpty()) { node = stack.pop(); list.add(node); path = createNodePath(copy(list)); visitor.visit(path); if (node.hasChildren()) { for (T child : node.getChildren()) { stack.push(child); } } else { // Remove the last element if it doesn't have children or if it is not the parent of the top most element in the stack while(! list.isEmpty() && (! list.getLast().hasChildren() || (!stack.isEmpty() && ! stack.peek().getParent().equals(list.getLast())))) { list.removeLast(); } } } } protected abstract P createNodePath(List<T> path); /** * @return the parent */ public T getParent() { return parent; } /** * @param parent */ public void setParent(T parent) { this.parent = parent; } /** * @return the children */ public LinkedList<T> getChildren() { return children; } private List<T> copy(List<T> list) { return new ArrayList<T>(list); } public interface Visitor<T> { void visit(T node); } public interface PathVisitor<P, T> { void visit(P path); } public class NodePath implements Iterable<T> { private List<T> path; public NodePath(List<T> path) { this.path = path; } public Iterator<T> iterator() { return path.iterator(); } public int size() { return path.size(); } public T get(int index) { return path.get(index); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getOuterType().hashCode(); result = prime * result + ((path == null) ? 0 : path.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; NodePath other = (NodePath) obj; if (!getOuterType().equals(other.getOuterType())) return false; if (path == null) { if (other.path != null) return false; } else if (!path.equals(other.path)) return false; return true; } private Node getOuterType() { return Node.this; } } }