1 # $Id: Node.pm,v 1.6 2007-02-28 17:34:54 mike Exp $
3 package ZOOM::IRSpy::Node;
13 ZOOM::IRSpy::Node - node in a tree of names
17 $node1 = new ZOOM::IRSpy::Node("LowLevelTest");
18 $node2 = new ZOOM::IRSpy::Node("AnotherTest");
19 $node3 = new ZOOM::IRSpy::Node("Aggregate", $node1, $node2);
20 $node = new ZOOM::IRSpy::Node("Main", $node3);
22 $subnode = $node->select("0:1");
23 die "oops" if $subnode->name() ne "AnotherTest";
27 IRSpy maintains a declarative hierarchy of the tests that each
28 connection may be required to perform, which it compiles recursively
29 from the C<subtests()> method of the top-level test and each of its
30 subtests, sub-subtests, etc. The result of this compilation is a
31 hierarchy represented by a tree of C<ZOOM::IRSpy::Node> objects.
33 Note that each node contains a test I<name>, not an actual test
34 object. Test objects are different, and are implemented by the
35 C<ZOOM::IRSpy::Test> class and its subclasses. In fact, there is
36 nothing test-specific about the Node module: it can be used to build
37 hierarchies of anything.
39 You can't do much with a node. Each node carries a name string and a
40 list of its subnodes, both of which are specified at creation time and
41 can be retrieved by accessor methods; trees can be pretty-printed, but
42 that's really only useful for debugging; and finally, nodes can be
43 selected from a tree using an address, which is a bit like a totally
48 $node = new ZOOM::IRSpy::Node($name, @subnodes);
50 Creates a new node with the name specified as the first argument of
51 the constructor. If further arguments are provided, they are taken to
52 be existing nodes that become subnodes of the new one. Once a node
53 has been created, neither its name nor its list of subnodes can be
60 my($name, @subnodes) = @_;
63 subnodes => \@subnodes,
64 address => undef, # filled in by resolve()
65 previous => undef, # filled in by resolve()
66 next => undef, # filled in by resolve()
74 print "Node is called '", $node->name(), "'\n";
76 Returns the name of the node.
87 @nodes = $node->subnodes();
88 print "Node has ", scalar(@nodes), " subnodes\n";
90 Returns a list of the subnodes of the node.
96 return @{ $this->{subnodes} };
103 Pretty-prints the node and, recursively, all its children. The
104 parameter is the level of indentation to use in printing the node;
105 this method recursively invokes itself with higher levels.
113 print "\t" x $level, $this->name();
114 if (my @sub = $this->subnodes()) {
116 foreach my $sub (@sub) {
117 $sub->print($level+1);
119 print "\t" x $level, "}";
126 $sameNode = $node->select("");
127 $firstSubNode $node->select("0");
128 $secondSubNode $node->select("1");
129 $deepNode $node->select("0:3:2");
131 Returns a specified node from the tree of which C<$node> is the root,
132 or an undefined value if the specified node does not exist. The sole
133 argument is the address of the node to be returned, which consists of
134 zero or more colon-separated components. Each component is an
135 integer, a zero-based index into the subnodes at that level. Example
142 The node itself, i.e. the root of the tree.
146 Subnode number 0 (i.e. the first subnode) of the root.
150 Subnode number 1 (i.e. the second subnode) of the root.
154 Subnode 2 of subnode 3 of subnode zero of the root (i.e. the third
155 subnode of the fourth subnode of the first subnode of the root).
165 my @sub = $this->subnodes();
166 if ($address eq "") {
168 } elsif (my($head, $tail) = $address =~ /(.*?):(.*)/) {
169 return $sub[$head]->select($tail);
171 return $sub[$address];
176 =head2 resolve(), address(), parent(), previous(), next()
179 assert(!defined $root->parent());
180 print $node->address();
181 assert($node eq $node->next()->previous());
183 C<resolve()> walks the tree rooted at C<$root>, adding addresses and
184 parent/previous/next links to each node in the tree, such that they
185 can respond to the C<address()>, C<parent()>, C<previous()> and
188 C<address()> returns the address of the node within the tree whose root
189 it was resolved from.
191 C<parent()> returns the parent node of this one, or an undefined value
194 C<previous()> returns the node that occurs before this one in a pre-order
197 C<next()> causes global thermonuclear warfare. Do not use C<next()>
198 in a production environment.
207 # Returns the last child-node in the subtree
212 $this->{address} = $address;
213 my $previous = $this;
215 my @subnodes = $this->subnodes();
216 foreach my $i (0 .. @subnodes-1) {
217 my $subnode = $subnodes[$i];
218 $subnode->{parent} = $this;
219 $subnode->{previous} = $previous;
220 $previous->{next} = $subnode;
222 my $subaddr = $address;
223 $subaddr .= ":" if $subaddr ne "";
225 $previous = $subnode->_resolve($subaddr);
231 sub address { shift()->{address} }
232 sub parent { shift()->{parent} }
233 sub previous { shift()->{previous} }
234 sub next { shift()->{next} }
243 Mike Taylor, E<lt>mike@indexdata.comE<gt>
245 =head1 COPYRIGHT AND LICENSE
247 Copyright (C) 2006 by Index Data ApS.
249 This library is free software; you can redistribute it and/or modify
250 it under the same terms as Perl itself, either Perl version 5.8.7 or,
251 at your option, any later version of Perl 5 you may have available.