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

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