package de.ugoe.cs.eventbench.models; import java.io.Serializable; import java.security.InvalidParameterException; import java.util.Collection; import java.util.LinkedList; import java.util.List; import de.ugoe.cs.eventbench.models.Trie.Edge; import de.ugoe.cs.eventbench.models.Trie.TrieVertex; import de.ugoe.cs.util.StringTools; import edu.uci.ics.jung.graph.DelegateTree; import edu.uci.ics.jung.graph.Tree; /** *

* This class implements a node of a trie. Each node is associated with a symbol * and has a counter. The counter marks the number of occurences of the sequence * defined by the path from the root of the trie to this node. *

* * @author Steffen Herbold * * @param * Type of the symbols that are stored in the trie. * @see Trie */ class TrieNode implements Serializable { /** *

* Id for object serialization. *

*/ private static final long serialVersionUID = 1L; /** *

* Counter for the number of occurences of the sequence. *

*/ private int count; /** *

* Symbol associated with the node. *

*/ private final T symbol; /** *

* Child nodes of this node. If the node is a leaf this collection is empty. *

*/ private Collection> children; /** *

* Constructor. Creates a new TrieNode without a symbol associated.
* This constructor should only be used to create the root node of the * trie! *

*/ TrieNode() { this.symbol = null; count = 0; children = new LinkedList>(); } /** *

* Constructor. Creates a new TrieNode. The symbol must not be null. *

* * @param symbol * symbol associated with the trie node */ TrieNode(T symbol) { if (symbol == null) { throw new InvalidParameterException( "symbol must not be null. null is reserved for root node!"); } this.symbol = symbol; count = 0; children = new LinkedList>(); } /** *

* Copy-Constructor. Creates a new TrieNode as copy of other. Other must not * be null. *

* * @param other */ TrieNode(TrieNode other) { if (other == null) { throw new InvalidParameterException("other must not be null"); } symbol = other.symbol; count = other.count; children = new LinkedList>(); for (TrieNode child : other.children) { children.add(new TrieNode(child)); } } /** *

* Adds a given subsequence to the trie and increases the counters * accordingly. *

* * @param subsequence * subsequence whose counters are increased * @see Trie#add(List) */ public void add(List subsequence) { if (!subsequence.isEmpty()) { if (!symbol.equals(subsequence.get(0))) { // should be guaranteed by // the // recursion/TrieRoot! throw new AssertionError("Invalid trie operation!"); } count++; subsequence.remove(0); if (!subsequence.isEmpty()) { T nextSymbol = subsequence.get(0); getChildCreate(nextSymbol).add(subsequence); } } } /** *

* Returns the symbol associated with the node. *

* * @return symbol associated with the node */ public T getSymbol() { return symbol; } /** *

* Returns the number of occurences of the sequence represented by the node. *

* * @return number of occurences of the sequence represented by the node */ public int getCount() { return count; } /** *

* Returns the child of the node associated with the given symbol or creates * it if it does not exist yet. *

* * @param symbol * symbol whose node is required * @return node associated with the symbol * @see Trie#getChildCreate(Object) */ protected TrieNode getChildCreate(T symbol) { TrieNode node = getChild(symbol); if (node == null) { node = new TrieNode(symbol); children.add(node); } return node; } /** *

* Returns the child of the node associated with the given symbol or null if * it does not exist. *

* * @param symbol * symbol whose node is required * @return node associated with the symbol; null if no such node exists * @see Trie#getChild(Object) */ protected TrieNode getChild(T symbol) { for (TrieNode child : children) { if (child.getSymbol().equals(symbol)) { return child; } } return null; } /** *

* Returns all children of this node. *

* * @return children of this node */ protected Collection> getChildren() { return children; } /** *

* Searches the sub-trie of this trie node for a given sequence and returns * the node associated with the sequence or null if no such node is found. *

* * @param sequence * sequence that is searched for * @return node associated with the sequence * @see Trie#find(List) */ public TrieNode find(List subsequence) { TrieNode result = null; if (subsequence.isEmpty()) { result = this; } else { TrieNode node = getChild(subsequence.get(0)); if (node != null) { subsequence.remove(0); result = node.find(subsequence); } } return result; } /** *

* Returns a collection of all symbols that follow a this node, i.e., the * symbols associated with the children of this node. *

* * @return symbols follow this node * @see TrieNode#getFollowingSymbols() */ public Collection getFollowingSymbols() { Collection followingSymbols = new LinkedList(); for (TrieNode child : children) { followingSymbols.add(child.getSymbol()); } return followingSymbols; } /** *

* The string representation of a node is {@code symbol.toString()#count} *

* * @see java.lang.Object#toString() */ @Override public String toString() { String str = symbol.toString() + " #" + count; if (!children.isEmpty()) { str += StringTools.ENDLINE + children.toString(); } return str; } /** *

* Generates a {@link Tree} represenation of the trie. *

* * @param parent * parent vertex in the generated tree * @param graph * complete tree */ void getGraph(TrieVertex parent, DelegateTree graph) { TrieVertex currentVertex; if (isRoot()) { currentVertex = new TrieVertex("root"); graph.addVertex(currentVertex); } else { currentVertex = new TrieVertex(getSymbol().toString() + "#" + getCount()); graph.addChild(new Edge(), parent, currentVertex); } for (TrieNode node : children) { node.getGraph(currentVertex, graph); } } /** *

* Appends the current node to the dot representation of the trie. *

* * @param stringBuilder * {@link StringBuilder} to which the dot representation is * appended */ void appendDotRepresentation(StringBuilder stringBuilder) { String thisSaneId; if (isRoot()) { thisSaneId = "root"; } else { thisSaneId = symbol.toString().replace("\"", "\\\"") .replaceAll("[\r\n]", "") + "#" + count; } stringBuilder.append(" " + hashCode() + " [label=\"" + thisSaneId + "\"];" + StringTools.ENDLINE); for (TrieNode childNode : children) { stringBuilder.append(" " + hashCode() + " -> " + childNode.hashCode() + ";" + StringTools.ENDLINE); } for (TrieNode childNode : children) { childNode.appendDotRepresentation(stringBuilder); } } /** *

* Checks if the node is a leaf. *

* * @return true if the node is a leaf, false otherwise. */ protected boolean isLeaf() { return children.isEmpty(); } /** *

* Checks if the node is the root. *

* * @return true if the node is the root of the trie, false otherwise */ protected boolean isRoot() { return symbol == null; } /** *

* Recursive methods that collects all nodes that are ancestors of leafs and * stores them in the set. *

* * @param ancestors * set of all ancestors of leafs */ protected void getLeafAncestors(List> ancestors) { boolean isAncestor = false; for (TrieNode child : children) { child.getLeafAncestors(ancestors); isAncestor |= child.isLeaf(); } if (isAncestor) { ancestors.add(this); } } /** *

* Returns the number of descendants of this node that are leafs. This does * not only include direct children of this node, but all leafs in the * sub-trie with this node as root. *

* * @return number of leafs in this sub-trie */ protected int getNumLeafs() { int numLeafs = 0; for (TrieNode child : children) { if (child.isLeaf()) { numLeafs++; } else { numLeafs += child.getNumLeafs(); } } return numLeafs; } /** *

* Sets the {@link #count} of this node. *

*

* This function should only be used sparingly and very carefully! The count * is usually maintained automatically by the training procedures. *

* * @param count * new count */ protected void setCount(int count) { this.count = count; } /** *

* Two TrieNodes are defined as equal, if their {@link #count}, * {@link #symbol}, and {@link #children} are equal. *

* * @see java.lang.Object#equals(java.lang.Object) */ @SuppressWarnings("rawtypes") @Override public boolean equals(Object other) { if (other == this) { return true; } if (other instanceof TrieNode) { TrieNode otherNode = (TrieNode) other; if (symbol == null) { return count == otherNode.count && otherNode.symbol == null && children.equals(otherNode.children); } else { return count == otherNode.count && symbol.equals(((TrieNode) other).symbol) && children.equals(((TrieNode) other).children); } } return false; } /* * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { int multiplier = 17; int hash = 42; if (symbol != null) { hash = multiplier * hash + symbol.hashCode(); } hash = multiplier * hash + count; hash = multiplier * hash + children.hashCode(); return hash; } }