source: trunk/EventBenchCore/src/de/ugoe/cs/eventbench/models/TrieNode.java @ 399

Last change on this file since 399 was 399, checked in by sherbold, 12 years ago
  • adapted implementation of de.ugoe.cs.eventbench.models.Trie.getNumLeafAncestors() and de.ugoe.cs.eventbench.TrieNode?().getLeafAncestors() as the assumptions of these methods were invalidated by the new and more precise equals functions
File size: 10.4 KB
Line 
1package de.ugoe.cs.eventbench.models;
2
3import java.io.Serializable;
4import java.security.InvalidParameterException;
5import java.util.Collection;
6import java.util.LinkedList;
7import java.util.List;
8
9import de.ugoe.cs.eventbench.models.Trie.Edge;
10import de.ugoe.cs.eventbench.models.Trie.TrieVertex;
11import de.ugoe.cs.util.StringTools;
12import edu.uci.ics.jung.graph.DelegateTree;
13import edu.uci.ics.jung.graph.Tree;
14
15/**
16 * <p>
17 * This class implements a node of a trie. Each node is associated with a symbol
18 * and has a counter. The counter marks the number of occurences of the sequence
19 * defined by the path from the root of the trie to this node.
20 * </p>
21 *
22 * @author Steffen Herbold
23 *
24 * @param <T>
25 *            Type of the symbols that are stored in the trie.
26 * @see Trie
27 */
28class TrieNode<T> implements Serializable {
29
30        /**
31         * <p>
32         * Id for object serialization.
33         * </p>
34         */
35        private static final long serialVersionUID = 1L;
36
37        /**
38         * <p>
39         * Counter for the number of occurences of the sequence.
40         * </p>
41         */
42        private int count;
43
44        /**
45         * <p>
46         * Symbol associated with the node.
47         * </p>
48         */
49        private final T symbol;
50
51        /**
52         * <p>
53         * Child nodes of this node. If the node is a leaf this collection is empty.
54         * </p>
55         */
56        private Collection<TrieNode<T>> children;
57
58        /**
59         * <p>
60         * Constructor. Creates a new TrieNode without a symbol associated.<br>
61         * <b>This constructor should only be used to create the root node of the
62         * trie!</b>
63         * </p>
64         */
65        TrieNode() {
66                this.symbol = null;
67                count = 0;
68                children = new LinkedList<TrieNode<T>>();
69        }
70
71        /**
72         * <p>
73         * Constructor. Creates a new TrieNode. The symbol must not be null.
74         * </p>
75         *
76         * @param symbol
77         *            symbol associated with the trie node
78         */
79        TrieNode(T symbol) {
80                if (symbol == null) {
81                        throw new InvalidParameterException(
82                                        "symbol must not be null. null is reserved for root node!");
83                }
84                this.symbol = symbol;
85                count = 0;
86                children = new LinkedList<TrieNode<T>>();
87        }
88
89        /**
90         * <p>
91         * Copy-Constructor. Creates a new TrieNode as copy of other. Other must not
92         * be null.
93         * </p>
94         *
95         * @param other
96         */
97        TrieNode(TrieNode<T> other) {
98                if (other == null) {
99                        throw new InvalidParameterException("other must not be null");
100                }
101                symbol = other.symbol;
102                count = other.count;
103                children = new LinkedList<TrieNode<T>>();
104                for (TrieNode<T> child : other.children) {
105                        children.add(new TrieNode<T>(child));
106                }
107        }
108
109        /**
110         * <p>
111         * Adds a given subsequence to the trie and increases the counters
112         * accordingly.
113         * </p>
114         *
115         * @param subsequence
116         *            subsequence whose counters are increased
117         * @see Trie#add(List)
118         */
119        public void add(List<T> subsequence) {
120                if (!subsequence.isEmpty()) {
121                        if (!symbol.equals(subsequence.get(0))) { // should be guaranteed by
122                                                                                                                // the
123                                                                                                                // recursion/TrieRoot!
124                                throw new AssertionError("Invalid trie operation!");
125                        }
126                        count++;
127                        subsequence.remove(0);
128                        if (!subsequence.isEmpty()) {
129                                T nextSymbol = subsequence.get(0);
130                                getChildCreate(nextSymbol).add(subsequence);
131                        }
132                }
133        }
134
135        /**
136         * <p>
137         * Returns the symbol associated with the node.
138         * </p>
139         *
140         * @return symbol associated with the node
141         */
142        public T getSymbol() {
143                return symbol;
144        }
145
146        /**
147         * <p>
148         * Returns the number of occurences of the sequence represented by the node.
149         * </p>
150         *
151         * @return number of occurences of the sequence represented by the node
152         */
153        public int getCount() {
154                return count;
155        }
156
157        /**
158         * <p>
159         * Returns the child of the node associated with the given symbol or creates
160         * it if it does not exist yet.
161         * </p>
162         *
163         * @param symbol
164         *            symbol whose node is required
165         * @return node associated with the symbol
166         * @see Trie#getChildCreate(Object)
167         */
168        protected TrieNode<T> getChildCreate(T symbol) {
169                TrieNode<T> node = getChild(symbol);
170                if (node == null) {
171                        node = new TrieNode<T>(symbol);
172                        children.add(node);
173                }
174                return node;
175        }
176
177        /**
178         * <p>
179         * Returns the child of the node associated with the given symbol or null if
180         * it does not exist.
181         * </p>
182         *
183         * @param symbol
184         *            symbol whose node is required
185         * @return node associated with the symbol; null if no such node exists
186         * @see Trie#getChild(Object)
187         */
188        protected TrieNode<T> getChild(T symbol) {
189                for (TrieNode<T> child : children) {
190                        if (child.getSymbol().equals(symbol)) {
191                                return child;
192                        }
193                }
194                return null;
195        }
196
197        /**
198         * <p>
199         * Returns all children of this node.
200         * </p>
201         *
202         * @return children of this node
203         */
204        protected Collection<TrieNode<T>> getChildren() {
205                return children;
206        }
207
208        /**
209         * <p>
210         * Searches the sub-trie of this trie node for a given sequence and returns
211         * the node associated with the sequence or null if no such node is found.
212         * </p>
213         *
214         * @param sequence
215         *            sequence that is searched for
216         * @return node associated with the sequence
217         * @see Trie#find(List)
218         */
219        public TrieNode<T> find(List<T> subsequence) {
220                TrieNode<T> result = null;
221                if (subsequence.isEmpty()) {
222                        result = this;
223                } else {
224                        TrieNode<T> node = getChild(subsequence.get(0));
225                        if (node != null) {
226                                subsequence.remove(0);
227                                result = node.find(subsequence);
228                        }
229                }
230                return result;
231        }
232
233        /**
234         * <p>
235         * Returns a collection of all symbols that follow a this node, i.e., the
236         * symbols associated with the children of this node.
237         * </p>
238         *
239         * @return symbols follow this node
240         * @see TrieNode#getFollowingSymbols()
241         */
242        public Collection<T> getFollowingSymbols() {
243                Collection<T> followingSymbols = new LinkedList<T>();
244                for (TrieNode<T> child : children) {
245                        followingSymbols.add(child.getSymbol());
246                }
247                return followingSymbols;
248        }
249
250        /**
251         * <p>
252         * The string representation of a node is {@code symbol.toString()#count}
253         * </p>
254         *
255         * @see java.lang.Object#toString()
256         */
257        @Override
258        public String toString() {
259                String str = symbol.toString() + " #" + count;
260                if (!children.isEmpty()) {
261                        str += StringTools.ENDLINE + children.toString();
262                }
263                return str;
264        }
265
266        /**
267         * <p>
268         * Generates a {@link Tree} represenation of the trie.
269         * </p>
270         *
271         * @param parent
272         *            parent vertex in the generated tree
273         * @param graph
274         *            complete tree
275         */
276        void getGraph(TrieVertex parent, DelegateTree<TrieVertex, Edge> graph) {
277                TrieVertex currentVertex;
278                if (isRoot()) {
279                        currentVertex = new TrieVertex("root");
280                        graph.addVertex(currentVertex);
281                } else {
282                        currentVertex = new TrieVertex(getSymbol().toString() + "#"
283                                        + getCount());
284                        graph.addChild(new Edge(), parent, currentVertex);
285                }
286                for (TrieNode<T> node : children) {
287                        node.getGraph(currentVertex, graph);
288                }
289        }
290
291        /**
292         * <p>
293         * Appends the current node to the dot representation of the trie.
294         * </p>
295         *
296         * @param stringBuilder
297         *            {@link StringBuilder} to which the dot representation is
298         *            appended
299         */
300        void appendDotRepresentation(StringBuilder stringBuilder) {
301                String thisSaneId;
302                if (isRoot()) {
303                        thisSaneId = "root";
304                } else {
305                        thisSaneId = symbol.toString().replace("\"", "\\\"")
306                                        .replaceAll("[\r\n]", "")
307                                        + "#" + count;
308                }
309                stringBuilder.append(" " + hashCode() + " [label=\"" + thisSaneId
310                                + "\"];" + StringTools.ENDLINE);
311                for (TrieNode<T> childNode : children) {
312                        stringBuilder.append(" " + hashCode() + " -> "
313                                        + childNode.hashCode() + ";" + StringTools.ENDLINE);
314                }
315                for (TrieNode<T> childNode : children) {
316                        childNode.appendDotRepresentation(stringBuilder);
317                }
318        }
319
320        /**
321         * <p>
322         * Checks if the node is a leaf.
323         * </p>
324         *
325         * @return true if the node is a leaf, false otherwise.
326         */
327        protected boolean isLeaf() {
328                return children.isEmpty();
329        }
330
331        /**
332         * <p>
333         * Checks if the node is the root.
334         * </p>
335         *
336         * @return true if the node is the root of the trie, false otherwise
337         */
338        protected boolean isRoot() {
339                return symbol == null;
340        }
341
342        /**
343         * <p>
344         * Recursive methods that collects all nodes that are ancestors of leafs and
345         * stores them in the set.
346         * </p>
347         *
348         * @param ancestors
349         *            set of all ancestors of leafs
350         */
351        protected void getLeafAncestors(List<TrieNode<T>> ancestors) {
352                boolean isAncestor = false;
353                for (TrieNode<T> child : children) {
354                        child.getLeafAncestors(ancestors);
355                        isAncestor |= child.isLeaf();
356                }
357                if (isAncestor) {
358                        ancestors.add(this);
359                }
360        }
361
362        /**
363         * <p>
364         * Returns the number of descendants of this node that are leafs. This does
365         * not only include direct children of this node, but all leafs in the
366         * sub-trie with this node as root.
367         * </p>
368         *
369         * @return number of leafs in this sub-trie
370         */
371        protected int getNumLeafs() {
372                int numLeafs = 0;
373                for (TrieNode<T> child : children) {
374                        if (child.isLeaf()) {
375                                numLeafs++;
376                        } else {
377                                numLeafs += child.getNumLeafs();
378                        }
379                }
380                return numLeafs;
381        }
382
383        /**
384         * <p>
385         * Sets the {@link #count} of this node.
386         * </p>
387         * <p>
388         * This function should only be used sparingly and very carefully! The count
389         * is usually maintained automatically by the training procedures.
390         * </p>
391         *
392         * @param count
393         *            new count
394         */
395        protected void setCount(int count) {
396                this.count = count;
397        }
398
399        /**
400         * <p>
401         * Two TrieNodes are defined as equal, if their {@link #count},
402         * {@link #symbol}, and {@link #children} are equal.
403         * </p>
404         *
405         * @see java.lang.Object#equals(java.lang.Object)
406         */
407        @SuppressWarnings("rawtypes")
408        @Override
409        public boolean equals(Object other) {
410                if (other == this) {
411                        return true;
412                }
413                if (other instanceof TrieNode) {
414                        TrieNode otherNode = (TrieNode) other;
415                        if (symbol == null) {
416                                return count == otherNode.count && otherNode.symbol == null
417                                                && children.equals(otherNode.children);
418                        } else {
419                                return count == otherNode.count
420                                                && symbol.equals(((TrieNode) other).symbol)
421                                                && children.equals(((TrieNode) other).children);
422                        }
423                }
424                return false;
425        }
426
427        /*
428         * (non-Javadoc)
429         *
430         * @see java.lang.Object#hashCode()
431         */
432        @Override
433        public int hashCode() {
434                int multiplier = 17;
435                int hash = 42;
436                if (symbol != null) {
437                        hash = multiplier * hash + symbol.hashCode();
438                }
439                hash = multiplier * hash + count;
440                hash = multiplier * hash + children.hashCode();
441                return hash;
442        }
443}
Note: See TracBrowser for help on using the repository browser.