Java tutorial
/* * NimbleTree.java * * Created on 16 October 2006, 16:45 * * Tree structure */ //package Util.Structures; import java.util.Stack; import java.util.ArrayList; import java.util.Collections; import java.util.StringTokenizer; /** * Lightweight tree n-arity structure * @author EHemberg */ public class NimbleTree<E> { private TreeNode<E> root, currentNode; private Stack<TreeNode<E>> freeNodes;// nodes that have been taken off the tree and recycled private int nodeCount; //Tracks number of nodes in the tree private int depth; // maximum depth of tree private int currentLevel; // private int maxStackSize; /** Creates a new instance of NimbleTree */ public NimbleTree() { this.root = newNode(); this.currentNode = this.root; this.nodeCount = 1; this.root.setID(this.nodeCount); this.depth = 0; this.currentLevel = 0; //Important to make a new stack this.freeNodes = new Stack<TreeNode<E>>(); this.maxStackSize = 10; } /** Copy constructor * @param n copied tree */ public NimbleTree(NimbleTree<E> n) { this.root = n.root; this.currentNode = n.currentNode; this.nodeCount = n.nodeCount; this.freeNodes = new Stack<TreeNode<E>>(); this.depth = n.depth; this.currentLevel = n.currentLevel; } protected TreeNode<E> newNode() { return new TreeNode<E>(); } /** * A static constructor (is this the right term?) where a lisp-style s-expression * is passed in as a string. This can't be a true constructor because the result * is a tree over String, not a generic tree. */ public static NimbleTree<String> makeTreeOverStringFromSExpression(String input) { NimbleTree<String> tree = new NimbleTree<String>(); Stack<TreeNode<String>> previousParents = new Stack<TreeNode<String>>(); // Make sure the string is tokenizable // FIXME allow [] and maybe other delimiters? input = input.replace("(", " ( "); input = input.replace(")", " ) "); StringTokenizer st = new StringTokenizer(input); boolean firstTimeThrough = true; while (st.hasMoreTokens()) { String currTok = st.nextToken().trim(); if (currTok.equals("")) { // Tokenizer gave us an empty token, do nothing. } else if (currTok.equals("(")) { // Get the *next* token and make a new subtree on it. currTok = st.nextToken().trim(); if (firstTimeThrough == true) { // The tree is of the form "(x)" // This is the root node: just set its data firstTimeThrough = false; tree.getRoot().setData(currTok); } else { // This is the root of a new subtree. Save parent, // then set this node to be the new parent. tree.addChild(currTok); tree.getCurrentNode().getEnd().setID(tree.getNodeCount()); previousParents.push(tree.getCurrentNode()); tree.setCurrentNode(tree.getCurrentNode().getEnd()); } } else if (currTok.equals(")")) { // Finished adding children to current parent. Go back // to previous parent (if there was none, it's because // current parent is root, so we're finished anyway). if (!previousParents.empty()) { tree.setCurrentNode(previousParents.pop()); } } else { if (firstTimeThrough == true) { // The tree is of the form "x". // This is to be the root node: just set its data. firstTimeThrough = false; tree.getRoot().setData(currTok); } else { // Add a child node to current parent. tree.addChild(currTok); tree.getCurrentNode().getEnd().setID(tree.getNodeCount()); } } } return tree; } /** * Set max stack size * @param i max stack size */ public void setMaxStackSize(int i) { this.maxStackSize = i; } /** * Get max stack size * @return max stack size */ public int getMaxStackSize() { return this.maxStackSize; } /** * Create nodes and push to the stack */ public void populateStack() { TreeNode<E> tn; for (int i = 0; i < maxStackSize; i++) { tn = newNode(); this.freeNodes.push(tn); } } /** * Get root of tree * @return tree root */ public TreeNode<E> getRoot() { return this.root; } /** * Set tree root * @param tn root of tree */ public void setRoot(TreeNode<E> tn) { this.root = tn; } /** * Get current node * @return current node */ public TreeNode<E> getCurrentNode() { return this.currentNode; } /** * Set current node * @param tn node to be current */ public void setCurrentNode(TreeNode<E> tn) { this.currentNode = tn; } /** * Get node count * @return number of nodes in tree */ public int getNodeCount() { return this.nodeCount; } /** * Set node count * @param i number to set */ public void setNodeCount(int i) { this.nodeCount = i; } /** * Set depth of tree * @param i depth */ public void setDepth(int i) { this.depth = i; } /** * Get maximum depth of tree * @return tree max depth */ public int getDepth() { return this.depth; } /** * Get current level * @return current level */ public int getCurrentLevel() { return this.currentLevel; } /** * Set current level * @param i level to set */ public void setCurrentLevel(int i) { this.currentLevel = i; } /** * Find all the paths from root to leaves. Used by NGramEDAReproductionOperation. * @return list of list of TreeNode, each list starting at the root and ending at a leaf. */ public ArrayList<ArrayList<TreeNode<E>>> getRootToLeafPaths() { ArrayList<TreeNode<E>> leafNodes = new ArrayList<TreeNode<E>>(); // traverse the tree and find the leaf nodes for (TreeNode<E> node : depthFirstTraversal(getRoot())) { if (node.size() == 0) { leafNodes.add(node); } } ArrayList<ArrayList<TreeNode<E>>> paths = new ArrayList<ArrayList<TreeNode<E>>>(); for (TreeNode<E> leaf : leafNodes) { ArrayList<TreeNode<E>> path = new ArrayList<TreeNode<E>>(); TreeNode<E> current = leaf; while (current != getRoot()) { path.add(current); current = current.getParent(); } // add the root path.add(current); Collections.reverse(path); paths.add(path); } return paths; } /** * Get all the chains of ancestors of length n in this tree. Used * as sources for NGram model. */ public ArrayList<ArrayList<TreeNode<E>>> getAncestorChains(int n) { ArrayList<ArrayList<TreeNode<E>>> retval = new ArrayList<ArrayList<TreeNode<E>>>(); for (TreeNode<E> node : depthFirstTraversal(getRoot())) { ArrayList<TreeNode<E>> chain = getAncestorChain(node, n); if (chain.size() == n) { Collections.reverse(chain); retval.add(chain); } } return retval; } /** * Starting at a given node, get a chain of ancestors of length n or less. */ public ArrayList<TreeNode<E>> getAncestorChain(TreeNode<E> node, int n) { ArrayList<TreeNode<E>> retval = new ArrayList<TreeNode<E>>(); TreeNode<E> current = node; while (retval.size() < n && current != null && current.getData() != null) { retval.add(current); current = current.getParent(); } return retval; } /** * Do a depth-first traversal of the tree starting at a given node. * @return a list of TreeNodes in depth-first order. */ public ArrayList<TreeNode<E>> depthFirstTraversal(TreeNode<E> root) { ArrayList<TreeNode<E>> retval = new ArrayList<TreeNode<E>>(); retval.add(root); for (TreeNode<E> child : root) { retval.addAll(depthFirstTraversal(child)); } return retval; } /** * Add a child to the current node. Take a node from the free nodes. * INFINITE LOOP POSSIBILITY??!! * @param data data contained in the child */ public void addChild(E data) { if (!this.freeNodes.empty()) { TreeNode<E> n = this.freeNodes.pop(); n.setData(data); n.setParent(this.currentNode); n.clear(); this.setDepth(this.depth + 1); this.currentNode.add(n); this.nodeCount++; n.setID(this.nodeCount); } else { populateStack(); addChild(data);//Infinite loop possibility } } @Override public String toString() { String s = ""; s += root.toString(); return s; } /** * Get some code for drawing this tree using Graphviz. */ public String getGraphvizCode() { String retval = "graph dotFile {\n"; // Write out the nodes. for (TreeNode<E> n : depthFirstTraversal(root)) { if (n != null && n.getData() != null) { retval += "id" + n.getID() + " [label=\"" + n.getData() + "\"];\n"; } } // Write out the incoming edge for every node that has a parent. for (TreeNode<E> n : depthFirstTraversal(root)) { if (n != null && n.getParent() != null && n.getParent().getData() != null) { retval += "id" + n.getParent().getID() + " -- id" + n.getID() + ";\n"; } } retval += "}"; return retval; } } class TreeNode<E> extends ArrayList<TreeNode<E>> { private TreeNode<E> parent; private E data; private int id; /** Creates a new instance of TreeNode */ public TreeNode() { super(); id = 0; } /** * Create node with parent and data * @param parent node parent * @param data node data */ public TreeNode(TreeNode<E> parent, E data) { super(); this.parent = parent; this.data = data; this.id = 0; } /** Copy constructor * @param copy node to copy */ public TreeNode(TreeNode<E> copy) { super(copy); for (TreeNode<E> aCopy : copy) { this.add(new TreeNode<E>(aCopy)); } this.parent = new TreeNode<E>(copy.parent); this.data = copy.data; this.id = copy.id; } /** * Get parent node * @return parent node */ public TreeNode<E> getParent() { return this.parent; } /** * Set parent node * @param tn parent node */ public void setParent(TreeNode<E> tn) { this.parent = tn; } /** * Get data in node * @return data */ public E getData() { return data; } /** * Set data in node * @param data node data */ public void setData(E data) { this.data = data; } /** * Get node id * @return int */ public int getID() { return id; } /** * Set node id * @param int id */ public void setID(int id) { this.id = id; } /** * Get the last node * @return the last node */ public TreeNode<E> getEnd() { return this.get(this.size() - 1); } /** * Collapse the node to a string * @return string representation */ public String collapse() { String s = ""; s += this.getData(); if (this.getParent() != null) { s += this.getParent().collapse(); } for (TreeNode<E> e : this) { s += e.getData().toString(); s += e.getParent().collapse(); } return s; } //Not working @Override public String toString() { String s = ""; s += this.getData() + ":"; for (TreeNode<E> e : this) { s += e.toString() + " "; } return s; } }