File Coverage

blib/lib/Tree/MultiNode.pm
Criterion Covered Total %
statement 249 383 65.0
branch 53 136 38.9
condition 12 37 32.4
subroutine 39 55 70.9
pod 1 1 100.0
total 354 612 57.8


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             Tree::MultiNode -- a multi-node tree object. Most useful for
5             modeling hierarchical data structures.
6              
7             =head1 SYNOPSIS
8              
9             use Tree::MultiNode;
10             use strict;
11             use warnings;
12             my $tree = new Tree::MultiNode;
13             my $handle = new Tree::MultiNode::Handle($tree);
14              
15             $handle->set_key("top");
16             $handle->set_value("level");
17              
18             $handle->add_child("child","1");
19             $handle->add_child("child","2");
20              
21             $handle->first();
22             $handle->down();
23              
24             $handle->add_child("grandchild","1-1");
25             $handle->up();
26              
27             $handle->last();
28             $handle->down();
29              
30             $handle->add_child("grandchild","2-1");
31             $handle->up();
32            
33             $handle->top();
34             &dump_tree($handle);
35              
36             my $depth = 0;
37             sub dump_tree
38             {
39             ++$depth;
40             my $handle = shift;
41             my $lead = ' ' x ($depth*2);
42             my($key,$val);
43            
44             ($key,$val) = $handle->get_data();
45              
46             print $lead, "key: $key\n";
47             print $lead, "val: $val\n";
48             print $lead, "depth: $depth\n";
49            
50             my $i;
51             for( $i = 0; $i < scalar($handle->children); ++$i ) {
52             $handle->down($i);
53             &dump_tree($handle);
54             $handle->up();
55             }
56             --$depth;
57             }
58              
59             =head1 DESCRIPTION
60              
61             Tree::MultiNode, Tree::MultiNode::Node, and MultiNode::Handle are objects
62             modeled after C++ classes that I had written to help me model hierarchical
63             information as data structures (such as the relationships between records in
64             an RDBMS). The tree is basically a list of lists type data structure, where
65             each node has a key, a value, and a list of children. The tree has no
66             internal sorting, though all operations preserve the order of the child
67             nodes.
68              
69             =head2 Creating a Tree
70              
71             The concept of creating a handle based on a tree lets you have multiple handles
72             into a single tree without having to copy the tree. You have to use a handle
73             for all operations on the tree (other than construction).
74              
75             When you first construct a tree, it will have a single empty node. When you
76             construct a handle into that tree, it will set the top node in the tree as
77             it's current node.
78              
79             my $tree = new Tree::MultiNode;
80             my $handle = new Tree::MultiNode::Handle($tree);
81              
82             =head2 Using a Handle to Manipulate the Tree
83              
84             At this point, you can set the key/value in the top node, or start adding
85             child nodes.
86              
87             $handle->set_key("blah");
88             $handle->set_value("foo");
89              
90             $handle->add_child("quz","baz");
91             # or
92             $handle->add_child();
93              
94             add_child can take 3 parameters -- a key, a value, and a position. The key
95             and value will set the key/value of the child on construction. If pos is
96             passed, the new child will be inserted into the list of children.
97              
98             To move the handle so it points at a child (so you can start manipulating that
99             child), there are a series of methods to call:
100              
101             $handle->first(); # sets the current child to the first in the list
102             $handle->next(); # sets the next, or first if there was no next
103             $handle->prev(); # sets the previous, or last if there was no next
104             $handle->last(); # sets to the last child
105             $handle->down(); # positions the handle's current node to the
106             # current child
107              
108             To move back up, you can call the method up:
109              
110             $handle->up(); # moves to this node's parent
111              
112             up() will fail if the current node has no parent node. Most of the member
113             functions return either undef to indicate failure, or some other value to
114             indicate success.
115              
116             =head2 $Tree::MultiNode::debug
117              
118             If set to a true value, it enables debugging output in the code. This will
119             likely be removed in future versions as the code becomes more stable.
120              
121             =head1 API REFERENCE
122              
123             =cut
124              
125             ################################################################################
126              
127             =head2 Tree::MultiNode
128              
129             The tree object.
130              
131             =cut
132              
133             package Tree::MultiNode;
134 2     2   139809 use strict;
  2         12  
  2         66  
135 2     2   12 use vars qw( $VERSION @ISA );
  2         2  
  2         380  
136             require 5.004;
137              
138             $VERSION = '1.0.14';
139             @ISA = ();
140              
141             =head2 Tree::MultiNode::new
142              
143             @param package name or tree object [scalar]
144             @returns new tree object
145              
146             Creates a new Tree. The tree will have a single top level node when created.
147             The first node will have no value (undef) in either it's key or it's value.
148              
149             my $tree = new Tree::MultiNode;
150              
151             =cut
152              
153             sub new {
154 2     2 1 1913 my $this = shift;
155 2   33     18 my $class = ref($this) || $this;
156 2         5 my $self = {};
157 2         4 bless $self, $class;
158              
159 2         8 $self->{'top'} = Tree::MultiNode::Node->new();
160 2         7 return $self;
161             }
162              
163             #
164             # this destructor is for clearing the circular references between
165             # the tree, the nodes, and their children.
166             #
167             sub DESTROY {
168 2     2   555 my $self = shift;
169 2 50       11 $self->{'top'}->_clearrefs() if $self->{'top'};
170             }
171              
172             1;
173             ################################################################################
174             package Tree::MultiNode::Node;
175 2     2   15 use strict;
  2         4  
  2         58  
176 2     2   11 use Carp;
  2         5  
  2         2112  
177              
178             =head2 Tree::MultiNode::Node
179              
180             Please note that the Node object is used internally by the MultiNode object.
181             Though you have the ability to interact with the nodes, it is unlikely that
182             you should need to. That being said, the interface is documented here anyway.
183              
184             =cut
185              
186             =head2 Tree::MultiNode::Node::new
187              
188             new($)
189             @param package name or node object to clone [scalar]
190             @returns new node object
191              
192             new($$)
193             @param key [scalar]
194             @param value [scalar]
195             @returns new node object
196              
197             Creates a new Node. There are three behaviors for new. A constructor with no
198             arguments creates a new, empty node. A single argument of another node object
199             will create a clone of the node object. If two arguments are passed, the first
200             is stored as the key, and the second is stored as the value.
201              
202             # clone an existing node
203             my $node = new Tree::MultiNode::Node($oldNode);
204             # or
205             my $node = $oldNode->new();
206              
207             # create a new node
208             my $node = new Tree::MultiNode::Node;
209             my $node = new Tree::MultiNode::Node("fname");
210             my $node = new Tree::MultiNode::Node("fname","Larry");
211              
212             =cut
213              
214             sub new {
215 12     12   20 my $this = shift;
216 12   33     40 my $class = ref($this) || $this;
217 12         23 my $self = {};
218 12         23 bless $self, $class;
219              
220 12         16 my $node = shift;
221 12 50       31 if ( ref($node) eq "Tree::MultiNode::Node" ) {
222              
223             # become a copy of that node...
224 0         0 $self->_clone($node);
225             }
226             else {
227 12         17 my ( $key, $value );
228 12         20 $key = $node;
229 12         32 $value = shift;
230 12 50       26 print __PACKAGE__, "::new() key,val = $key,$value\n"
231             if $Tree::MultiNode::debug;
232 12         27 $self->{'children'} = [];
233 12         24 $self->{'parent'} = undef;
234 12   100     31 $self->{'key'} = $key || undef;
235 12 100       33 $self->{'value'} = defined $value ? $value : undef;
236             }
237              
238 12         26 return $self;
239             }
240              
241             #
242             # internal method for making the current node a clone of another
243             # node...
244             #
245             sub _clone {
246 0     0   0 my $self = shift;
247 0         0 my $them = shift;
248 0         0 $self->{'parent'} = $them->parent;
249 0         0 $self->{'children'} = [ $them->children ];
250 0         0 $self->{'key'} = $them->key;
251 0         0 $self->{'value'} = $them->value;
252             }
253              
254             =head2 Tree::MultiNode::Node::key
255              
256             @param key [scalar]
257             @returns the key [scalar]
258              
259             Used to set, or retrieve the key for a node. If a parameter is passed,
260             it sets the key for the node. The value of the key member is always
261             returned.
262              
263             print $node3->key(), "\n"; # 'fname'
264              
265             =cut
266              
267             sub key {
268 17     17   33 my ( $self, $key ) = @_;
269              
270 17 100       40 if ( @_ > 1 ) {
271 2 50       5 print __PACKAGE__, "::key() setting key: $key on $self\n"
272             if $Tree::MultiNode::debug;
273 2         5 $self->{'key'} = $key;
274             }
275              
276 17         47 return $self->{'key'};
277             }
278              
279             =head2 Tree::MultiNode::Node::value
280              
281             @param the value to set [scalar]
282             @returns the value [scalar]
283              
284             Used to set, or retrieve the value for a node. If a parameter is passed,
285             it sets the value for the node. The value of the value member is always
286             returned.
287              
288             print $node3->value(), "\n"; # 'Larry'
289              
290             =cut
291              
292             sub value {
293 13     13   20 my $self = shift;
294 13         15 my $value = shift;
295              
296 13 100       30 if ( defined $value ) {
297 1 50       4 print __PACKAGE__, "::value() setting value: $value on $self\n"
298             if $Tree::MultiNode::debug;
299 1         2 $self->{'value'} = $value;
300             }
301              
302 13         80 return $self->{'value'};
303             }
304              
305             =head2 Tree::MultiNode::Node::clear_key
306              
307             @returns the deleted key
308              
309             Clears the key member by deleting it.
310              
311             $node3->clear_key();
312              
313             =cut
314              
315             sub clear_key {
316 0     0   0 my $self = shift;
317 0         0 return delete $self->{'key'};
318             }
319              
320             =head2 Tree::MultiNode::Node::clear_value
321              
322             @returns the deleted value
323              
324             Clears the value member by deleting it.
325              
326             $node3->clear_value();
327              
328             =cut
329              
330             sub clear_value {
331 0     0   0 my $self = shift;
332 0         0 return delete $self->{'value'};
333             }
334              
335             =head2 Tree::MultiNode::Node::children
336              
337             @returns reference to children [array reference]
338              
339             Returns a reference to the array that contains the children of the
340             node object.
341              
342             $array_ref = $node3->children();
343              
344             =cut
345              
346             sub children {
347 64     64   86 my $self = shift;
348 64         105 return $self->{'children'};
349             }
350              
351             =head2 Tree::MultiNode::Node::child_keys
352             Tree::MultiNode::Node::child_values
353             Tree::MultiNode::Node::child_kv_pairs
354              
355             These functions return arrays consisting of the appropriate data
356             from the child nodes.
357              
358             my @keys = $handle->child_keys();
359             my @vals = $handle->child_values();
360             my %kv_pairs = $handle->child_kv_pairs();
361              
362             =cut
363              
364             sub child_keys {
365 1     1   2 my $self = shift;
366 1         2 my $children = $self->{'children'};
367 1         3 my @keys;
368             my $node;
369              
370 1         3 foreach $node (@$children) {
371 2         5 push @keys, $node->key();
372             }
373              
374 1         9 return @keys;
375             }
376              
377             sub child_values {
378 0     0   0 my $self = shift;
379 0         0 my $children = $self->{'children'};
380 0         0 my @values;
381             my $node;
382              
383 0         0 foreach $node (@$children) {
384 0         0 push @values, $node->value();
385             }
386              
387 0         0 return @values;
388             }
389              
390             sub child_kv_pairs {
391 1     1   2 my $self = shift;
392 1         2 my $children = $self->{'children'};
393 1         2 my %h;
394             my $node;
395              
396 1         2 foreach $node (@$children) {
397 2         4 $h{ $node->key() } = $node->value();
398             }
399              
400 1         6 return %h;
401             }
402              
403             =head2 Tree::MultiNode::Node::child_key_positions
404              
405             This function returns a hash table that consists of the
406             child keys as the hash keys, and the position in the child
407             array as the value. This allows for a quick and dirty way
408             of looking up the position of a given key in the child list.
409              
410             my %h = $node->child_key_positions();
411              
412             =cut
413              
414             sub child_key_positions {
415 0     0   0 my $self = shift;
416 0         0 my $children = $self->{'children'};
417 0         0 my ( %h, $i, $node );
418              
419 0         0 $i = 0;
420 0         0 foreach $node (@$children) {
421 0         0 $h{ $node->key() } = $i++;
422             }
423              
424 0         0 return %h;
425             }
426              
427             =head2 Tree::MultiNode::Node::parent
428              
429             Returns a reference to the parent node of the current node.
430              
431             $node_parent = $node3->parent();
432              
433             =cut
434              
435             sub parent {
436 7     7   10 my $self = shift;
437 7         11 return $self->{'parent'};
438             }
439              
440             =head2 Tree::MultiNode::Node::dump
441              
442             Used for diagnostics, it prints out the members of the node.
443              
444             $node3->dump();
445              
446             =cut
447              
448             sub dump {
449 0     0   0 my $self = shift;
450              
451 0         0 print "[dump] key: ", $self->{'key'}, "\n";
452 0         0 print "[dump] val: ", $self->{'value'}, "\n";
453 0         0 print "[dump] parent: ", $self->{'parent'}, "\n";
454 0         0 print "[dump] children: ", $self->{'children'}, "\n";
455             }
456              
457             sub _clearrefs {
458 11     11   15 my $self = shift;
459 11         19 delete $self->{'parent'};
460 11         14 foreach my $child ( @{ $self->children() } ) {
  11         21  
461 9         20 $child->_clearrefs();
462             }
463 11         163 delete $self->{'children'};
464             }
465              
466             1;
467             ################################################################################
468             package Tree::MultiNode::Handle;
469 2     2   15 use strict;
  2         5  
  2         66  
470 2     2   11 use Carp;
  2         6  
  2         6875  
471              
472             =head2 Tree::MultiNode::Handle
473              
474             Handle is used as a 'pointer' into the tree. It has a few attributes that it keeps
475             track of. These are:
476              
477             1. the top of the tree
478             2. the current node
479             3. the current child node
480             4. the depth of the current node
481              
482             The top of the tree never changes, and you can reset the handle to point back at
483             the top of the tree by calling the top() method.
484              
485             The current node is where the handle is 'pointing' in the tree. The current node
486             is changed with functions like top(), down(), and up().
487              
488             The current child node is used for traversing downward into the tree. The members
489             first(), next(), prev(), last(), and position() can be used to set the current child,
490             and then traverse down into it.
491              
492             The depth of the current node is a measure of the length of the path
493             from the top of the tree to the current node, i.e., the top of the node
494             has a depth of 0, each of its children has a depth of 1, etc.
495              
496             =cut
497              
498             =head2 Tree::MultiNode::Handle::New
499              
500             Constructs a new handle. You must pass a tree object to Handle::New.
501              
502             my $tree = new Tree::MultiNode;
503             my $handle = new Tree::MultiNode::Handle($tree);
504              
505             =cut
506              
507             sub new {
508 3     3   13 my $this = shift;
509 3   33     10 my $class = ref($this) || $this;
510              
511 3         6 my $self = {};
512 3         5 bless $self, $class;
513 3         5 my $data = shift;
514 3 50       6 print __PACKAGE__, "::new() ref($data) is: ", ref($data), "\n"
515             if $Tree::MultiNode::debug;
516 3 100       9 if ( ref($data) eq "Tree::MultiNode::Handle" ) {
517 1         3 $self->_clone($data);
518             }
519             else {
520 2 50       6 unless ( ref($data) eq "Tree::MultiNode" ) {
521 0         0 confess "Error, invalid Tree::MultiNode reference: $data\n";
522             }
523              
524 2         8 $self->{'tree'} = $data;
525 2         6 $self->{'curr_pos'} = undef;
526 2         5 $self->{'curr_node'} = $data->{'top'};
527 2         3 $self->{'curr_child'} = undef;
528 2         5 $self->{'curr_depth'} = 0;
529             }
530 3         13 return $self;
531             }
532              
533             #
534             # internal method for making the current handle a copy of another
535             # handle...
536             #
537             sub _clone {
538 1     1   2 my $self = shift;
539 1         2 my $them = shift;
540 1 50       4 print __PACKAGE__, "::_clone() cloning: $them\n"
541             if $Tree::MultiNode::debug;
542 1 50       4 print __PACKAGE__, "::_clone() depth: ", $them->{'curr_depth'}, "\n"
543             if $Tree::MultiNode::debug;
544 1         2 $self->{'tree'} = $them->{'tree'};
545 1         3 $self->{'curr_pos'} = $them->{'curr_pos'};
546 1         1 $self->{'curr_node'} = $them->{'curr_node'};
547 1         3 $self->{'curr_child'} = $them->{'curr_child'};
548 1         1 $self->{'curr_depth'} = $them->{'curr_depth'};
549 1         2 return 1;
550             }
551              
552             =head2 Tree::MultiNode::Handle::tree
553              
554             Returns the tree that was used to construct the node. Useful if you're
555             trying to create another node into the tree.
556              
557             my $handle2 = new Tree::MultiNode::Handle($handle->tree());
558              
559             =cut
560              
561             sub tree {
562 0     0   0 my $self = shift;
563 0         0 return $self->{'tree'};
564             }
565              
566             =head2 Tree::MultiNode::Handle::get_data
567              
568             Retrieves both the key, and value (as an array) for the current node.
569              
570             my ($key,$val) = $handle->get_data();
571              
572             =cut
573              
574             sub get_data {
575 7     7   25 my $self = shift;
576 7         11 my $node = $self->{'curr_node'};
577              
578 7         14 return ( $node->key, $node->value );
579             }
580              
581             =head2 Tree::MultiNode::Handle::get_key
582              
583             Retrieves the key for the current node.
584              
585             $key = $handle->get_key();
586              
587             =cut
588              
589             sub get_key {
590 1     1   3 my $self = shift;
591 1         2 my $node = $self->{'curr_node'};
592              
593 1         4 my $key = $node->key();
594              
595 1 50       10 print __PACKAGE__, "::get_key() getting from $node : $key\n"
596             if $Tree::MultiNode::debug;
597              
598 1         4 return $key;
599             }
600              
601             =head2 Tree::MultiNode::Handle::set_key
602              
603             Sets the key for the current node.
604              
605             $handle->set_key("lname");
606              
607             =cut
608              
609             sub set_key {
610 2     2   781 my $self = shift;
611 2         4 my $key = shift;
612 2         4 my $node = $self->{'curr_node'};
613              
614 2 50       7 print __PACKAGE__, "::set_key() setting key \"$key\" on: $node\n"
615             if $Tree::MultiNode::debug;
616              
617 2         6 return $node->key($key);
618             }
619              
620             =head2 Tree::MultiNode::Handle::get_value
621              
622             Retrieves the value for the current node.
623              
624             $val = $handle->get_value();
625              
626             =cut
627              
628             sub get_value {
629 2     2   6 my $self = shift;
630 2         3 my $node = $self->{'curr_node'};
631              
632 2         6 my $value = $node->value();
633              
634 2 50       6 print __PACKAGE__, "::get_value() getting from $node : $value\n",
635             if $Tree::MultiNode::debug;
636              
637 2         9 return $value;
638             }
639              
640             =head2 Tree::MultiNode::Handle::set_value
641              
642             Sets the value for the current node.
643              
644             $handle->set_value("Wall");
645              
646             =cut
647              
648             sub set_value {
649 1     1   2 my $self = shift;
650 1         2 my $value = shift;
651 1         2 my $node = $self->{'curr_node'};
652              
653 1 50       6 print __PACKAGE__, "::set_value() setting value \"$value\" on: $node\n"
654             if $Tree::MultiNode::debug;
655              
656 1         3 return $node->value($value);
657             }
658              
659             =head2 Tree::MultiNode::Handle::get_child
660              
661             get_child takes an optional parameter which is the position of the child
662             that is to be retrieved. If this position is not specified, get_child
663             attempts to return the current child. get_child returns a Node object.
664              
665             my $child_node = $handle->get_child();
666              
667             =cut
668              
669             sub get_child {
670 9     9   14 my $self = shift;
671 9         17 my $children = $self->{'curr_node'}->children;
672 9   66     49 my $pos = shift || $self->{'curr_pos'};
673              
674 9 50       20 print __PACKAGE__, "::get_child() children: $children $pos\n"
675             if $Tree::MultiNode::debug;
676              
677 9 50       16 unless ( defined $children ) {
678 0         0 return undef;
679             }
680              
681 9 50 33     23 unless ( defined $pos && $pos <= $#{$children} ) {
  9         29  
682 0         0 my $num = $#{$children};
  0         0  
683 0         0 confess "Error, $pos is an invalid position [$num] $children.\n";
684             }
685              
686             print __PACKAGE__, "::get_child() returning [$pos]: ",
687 9 50       21 ${$children}[$pos], "\n" if $Tree::MultiNode::debug;
  0         0  
688 9         10 return ( ${$children}[$pos] );
  9         21  
689             }
690              
691             =head2 Tree::MultiNode::Handle::add_child
692              
693             This member adds a new child node to the end of the array of children for the
694             current node. There are three optional parameters:
695              
696             - a key
697             - a value
698             - a position
699              
700             If passed, the key and value will be set in the new child. If a position is
701             passed, the new child will be inserted into the current array of children at
702             the position specified.
703              
704             $handle->add_child(); # adds a blank child
705             $handle->add_child("language","perl"); # adds a child to the end
706             $handle->add_child("language","C++",0); # adds a child to the front
707              
708             =cut
709              
710             sub add_child {
711 10     10   890 my $self = shift;
712 10         24 my ( $key, $value, $pos ) = @_;
713 10         23 my $children = $self->{'curr_node'}->children;
714 10 50       24 print __PACKAGE__, "::add_child() children: $children\n"
715             if $Tree::MultiNode::debug;
716 10         16 my $curr_pos = $self->{'curr_pos'};
717 10         15 my $curr_node = $self->{'curr_node'};
718              
719 10         25 my $child = Tree::MultiNode::Node->new( $key, $value );
720 10         14 $child->{'parent'} = $curr_node;
721              
722 10 50       21 print __PACKAGE__, "::add_child() adding child $child ($key,$value) ",
723             "to: $children\n" if $Tree::MultiNode::debug;
724              
725 10 50       36 if ( defined $pos ) {
726 0 0       0 print __PACKAGE__, "::add_child() adding at $pos $child\n"
727             if $Tree::MultiNode::debug;
728 0 0       0 unless ( $pos <= $#{$children} ) {
  0         0  
729 0         0 my $num = $#{$children};
  0         0  
730 0         0 confess "Position $pos is invalid for child position [$num] $children.\n";
731             }
732 0         0 splice( @{$children}, $pos, 1, $child, ${$children}[$pos] );
  0         0  
  0         0  
733             }
734             else {
735 10 50       22 print __PACKAGE__, "::add_child() adding at end $child\n"
736             if $Tree::MultiNode::debug;
737 10         12 push @{$children}, $child;
  10         17  
738             }
739              
740             print __PACKAGE__, "::add_child() children:",
741 10 50       46 join( ',', @{ $self->{'curr_node'}->children } ), "\n"
  0         0  
742             if $Tree::MultiNode::debug;
743             }
744              
745             =head2 Tree::MultiNode::Handle::add_child_node
746              
747             Recently added via RT # 5435 -- Currently in need of proper documentation and test patches
748              
749             I've patched Tree::MultiNode 1.0.10 to add a method I'm currently calling add_child_node().
750             It works just like add_child() except it takes either a Tree::MultiNode::Node or a
751             Tree::MultiNode object instead. I found this extremely useful when using recursion to populate
752             a tree. It could also be used to subsume any tree into another tree, so this touches on the
753             topic of the other bug item here asking for methods to copy/move trees/nodes.
754              
755             =cut
756              
757             sub add_child_node {
758 0     0   0 my $self = shift;
759 0         0 my ( $child, $pos ) = @_;
760 0         0 my $children = $self->{'curr_node'}->children;
761 0 0       0 print __PACKAGE__, "::add_child_node() children: $children\n"
762             if $Tree::MultiNode::debug;
763 0         0 my $curr_pos = $self->{'curr_pos'};
764 0         0 my $curr_node = $self->{'curr_node'};
765 0 0       0 if ( ref($child) eq 'Tree::MultiNode' ) {
766 0         0 my $top = $child->{'top'};
767 0         0 $child->{'top'} = undef;
768 0         0 $child = $top;
769             }
770 0 0       0 confess "Invalid child argument.\n"
771             if ( ref($child) ne 'Tree::MultiNode::Node' );
772              
773 0         0 $child->{'parent'} = $curr_node;
774              
775 0 0       0 print __PACKAGE__, "::add_child_node() adding child $child ",
776             "to: $children\n" if $Tree::MultiNode::debug;
777              
778 0 0       0 if ( defined $pos ) {
779 0 0       0 print __PACKAGE__, "::add_child_node() adding at $pos $child\n"
780             if $Tree::MultiNode::debug;
781 0 0       0 unless ( $pos <= $#{$children} ) {
  0         0  
782 0         0 my $num = $#{$children};
  0         0  
783 0         0 confess "Position $pos is invalid for child position [$num] $children.\n";
784             }
785 0         0 splice( @{$children}, $pos, 1, $child, ${$children}[$pos] );
  0         0  
  0         0  
786             }
787             else {
788 0 0       0 print __PACKAGE__, "::add_child_node() adding at end $child\n"
789             if $Tree::MultiNode::debug;
790 0         0 push @{$children}, $child;
  0         0  
791             }
792              
793             print __PACKAGE__, "::add_child_node() children:",
794 0 0       0 join( ',', @{ $self->{'curr_node'}->children } ), "\n"
  0         0  
795             if $Tree::MultiNode::debug;
796             }
797              
798             =head2 Tree::MultiNode::Handle::depth
799              
800             Gets the depth for the current node.
801              
802             my $depth = $handle->depth();
803              
804             =cut
805              
806             sub depth {
807 7     7   32 my $self = shift;
808 7         12 my $node = $self->{'curr_node'};
809              
810 7 50       14 print __PACKAGE__, "::depth() getting depth \"$self->{'curr_depth'}\" ",
811             "on: $node\n" if $Tree::MultiNode::debug;
812              
813 7         19 return $self->{'curr_depth'};
814             }
815              
816             =head2 Tree::MultiNode::Handle::select
817              
818             Sets the current child via a specified value -- basically it iterates
819             through the array of children, looking for a match. You have to
820             supply the key to look for, and optionally a sub ref to find it. The
821             default for this sub is
822              
823             sub { return shift eq shift; }
824              
825             Which is sufficient for testing the equality of strings (the most common
826             thing that I think will get stored in the tree). If you're storing multiple
827             data types as keys, you'll have to write a sub that figures out how to
828             perform the comparisons in a sane manner.
829              
830             The code reference should take two arguments, and compare them -- return
831             false if they don't match, and true if they do.
832              
833             $handle->select('lname', sub { return shift eq shift; } );
834              
835             =cut
836              
837             sub select {
838 1     1   3 my $self = shift;
839 1         2 my $key = shift;
840 1   50 2   10 my $code = shift || sub { return shift eq shift; };
  2         7  
841 1         2 my ( $child, $pos );
842 1         2 my $found = undef;
843              
844 1         3 $pos = 0;
845 1         3 foreach $child ( $self->children() ) {
846 2 100       6 if ( $code->( $key, $child->key() ) ) {
847 1         3 $self->{'curr_pos'} = $pos;
848 1         3 $self->{'curr_child'} = $child;
849 1         3 ++$found;
850 1         3 last;
851             }
852 1         3 ++$pos;
853             }
854              
855 1         7 return $found;
856             }
857              
858             =head2 Tree::MultiNode::Handle::position
859              
860             Sets, or retrieves the current child position.
861              
862             print "curr child pos is: ", $handle->position(), "\n";
863             $handle->position(5); # sets the 6th child as the current child
864              
865             =cut
866              
867             sub position {
868 8     8   9 my $self = shift;
869 8         11 my $pos = shift;
870              
871 8 50       15 print __PACKAGE__, "::position() $self $pos\n"
872             if $Tree::MultiNode::debug;
873              
874 8 50       16 unless ( defined $pos ) {
875 0         0 return $self->{'curr_pos'};
876             }
877              
878 8         17 my $children = $self->{'curr_node'}->children;
879 8 50       15 print __PACKAGE__, "::position() children: $children\n"
880             if $Tree::MultiNode::debug;
881             print __PACKAGE__, "::position() position is $pos ",
882 8 50       18 $#{$children}, "\n" if $Tree::MultiNode::debug;
  0         0  
883 8 50       12 unless ( $pos <= $#{$children} ) {
  8         21  
884 0         0 my $num = $#{$children};
  0         0  
885 0         0 confess "Error, $pos is invalid [$num] $children.\n";
886             }
887 8         11 $self->{'curr_pos'} = $pos;
888 8         18 $self->{'curr_child'} = $self->get_child($pos);
889 8         23 return $self->{'curr_pos'};
890             }
891              
892             =head2 Tree::MultiNode::Handle::first
893             Tree::MultiNode::Handle::next
894             Tree::MultiNode::Handle::prev
895             Tree::MultiNode::Handle::last
896              
897             These functions manipulate the current child member. first() sets the first
898             child as the current child, while last() sets the last. next(), and prev() will
899             move to the next/prev child respectively. If there is no current child node,
900             next() will have the same effect as first(), and prev() will operate as last().
901             prev() fails if the current child is the first child, and next() fails if the
902             current child is the last child -- i.e., they do not wrap around.
903              
904             These functions will fail if there are no children for the current node.
905              
906             $handle->first(); # sets to the 0th child
907             $handle->next(); # to the 1st child
908             $handle->prev(); # back to the 0th child
909             $handle->last(); # go straight to the last child.
910              
911             =cut
912              
913             sub first {
914 0     0   0 my $self = shift;
915              
916 0         0 $self->{'curr_pos'} = 0;
917 0         0 $self->{'curr_child'} = $self->get_child(0);
918             print __PACKAGE__, "::first() set child[", $self->{'curr_pos'}, "]: ",
919 0 0       0 $self->{'curr_child'}, "\n" if $Tree::MultiNode::debug;
920 0         0 return $self->{'curr_pos'};
921             }
922              
923             sub next {
924 0     0   0 my $self = shift;
925 0         0 my $pos = $self->{'curr_pos'} + 1;
926 0         0 my $children = $self->{'curr_node'}->children;
927 0 0       0 print __PACKAGE__, "::next() children: $children\n"
928             if $Tree::MultiNode::debug;
929              
930 0 0 0     0 unless ( $pos >= 0 && $pos <= $#{$children} ) {
  0         0  
931 0         0 return undef;
932             }
933              
934 0         0 $self->{'curr_pos'} = $pos;
935 0         0 $self->{'curr_child'} = $self->get_child($pos);
936 0         0 return $self->{'curr_pos'};
937             }
938              
939             sub prev {
940 0     0   0 my $self = shift;
941 0         0 my $pos = $self->{'curr_pos'} - 1;
942 0         0 my $children = $self->{'curr_node'}->children;
943 0 0       0 print __PACKAGE__, "::prev() children: $children\n"
944             if $Tree::MultiNode::debug;
945              
946 0 0 0     0 unless ( $pos >= 0 && $pos <= $#{$children} ) {
  0         0  
947 0         0 return undef;
948             }
949              
950 0         0 $self->{'curr_pos'} = $pos;
951 0         0 $self->{'curr_child'} = $self->get_child($pos);
952 0         0 return $self->{'curr_pos'};
953             }
954              
955             sub last {
956 1     1   3 my $self = shift;
957 1         4 my $children = $self->{'curr_node'}->children;
958 1         2 my $pos = $#{$children};
  1         3  
959 1 50       5 print __PACKAGE__, "::last() children [$pos]: $children\n"
960             if $Tree::MultiNode::debug;
961              
962 1         2 $self->{'curr_pos'} = $pos;
963 1         4 $self->{'curr_child'} = $self->get_child($pos);
964 1         4 return $self->{'curr_pos'};
965             }
966              
967             =head2 Tree::MultiNode::Handle::down
968              
969             down() moves the handle to point at the current child node. It fails
970             if there is no current child node. When down() is called, the current
971             child becomes invalid (undef).
972              
973             $handle->down();
974              
975             =cut
976              
977             sub down {
978 10     10   17 my $self = shift;
979 10         19 my $pos = shift;
980 10         12 my $node = $self->{'curr_node'};
981 10 50       25 return undef unless defined $node;
982 10         18 my $children = $node->children;
983 10 50       22 print __PACKAGE__, "::down() children: $children\n"
984             if $Tree::MultiNode::debug;
985              
986 10 100       20 if ( defined $pos ) {
987 8 50       19 unless ( defined $self->position($pos) ) {
988 0         0 confess "Error, $pos was an invalid position.\n";
989             }
990             }
991              
992 10         14 $self->{'curr_pos'} = undef;
993 10         20 $self->{'curr_node'} = $self->{'curr_child'};
994 10         13 $self->{'curr_child'} = undef;
995 10         18 ++$self->{'curr_depth'};
996 10 50       17 print __PACKAGE__, "::down() set to: ", $self->{'curr_node'}, "\n"
997             if $Tree::MultiNode::debug;
998              
999 10         27 return 1;
1000             }
1001              
1002             =head2 Tree::MultiNode::Handle::up
1003              
1004             down() moves the handle to point at the parent of the current node. It fails
1005             if there is no parent node. When up() is called, the current child becomes
1006             invalid (undef).
1007              
1008             $handle->up();
1009              
1010             =cut
1011              
1012             sub up {
1013 7     7   33 my $self = shift;
1014 7         13 my $node = $self->{'curr_node'};
1015 7 50       18 return undef unless defined $node;
1016 7         15 my $parent = $node->parent();
1017              
1018 7 50       15 unless ( defined $parent ) {
1019 0         0 return undef;
1020             }
1021              
1022 7         11 $self->{'curr_pos'} = undef;
1023 7         10 $self->{'curr_node'} = $parent;
1024 7         11 $self->{'curr_child'} = undef;
1025 7         11 --$self->{'curr_depth'};
1026              
1027 7         16 return 1;
1028             }
1029              
1030             =head2 Tree::MultiNode::Handle::top
1031              
1032             Resets the handle to point back at the top of the tree.
1033             When top() is called, the current child becomes invalid (undef).
1034              
1035             $handle->top();
1036              
1037             =cut
1038              
1039             sub top {
1040 2     2   4 my $self = shift;
1041 2         4 my $tree = $self->{'tree'};
1042              
1043 2         7 $self->{'curr_pos'} = undef;
1044 2         4 $self->{'curr_node'} = $tree->{'top'};
1045 2         9 $self->{'curr_child'} = undef;
1046 2         4 $self->{'curr_depth'} = 0;
1047              
1048 2         8 return 1;
1049             }
1050              
1051             =head2 Tree::MultiNode::Handle::children
1052              
1053             This returns an array of Node objects that represents the children of the
1054             current Node. Unlike Node::children(), the array Handle::children() is not
1055             a reference to an array, but an array. Useful if you need to iterate through
1056             the children of the current node.
1057              
1058             print "There are: ", scalar($handle->children()), " children\n";
1059             foreach $child ($handle->children()) {
1060             print $child->key(), " : ", $child->value(), "\n";
1061             }
1062              
1063             =cut
1064              
1065             sub children {
1066 14     14   26 my $self = shift;
1067 14         24 my $node = $self->{'curr_node'};
1068 14 50       33 return undef unless defined $node;
1069 14         25 my $children = $node->children;
1070              
1071 14         18 return @{$children};
  14         40  
1072             }
1073              
1074             =head2 Tree::MultiNode::Handle::child_key_positions
1075              
1076             This function returns a hash table that consists of the
1077             child keys as the hash keys, and the position in the child
1078             array as the value. This allows for a quick and dirty way
1079             of looking up the position of a given key in the child list.
1080              
1081             my %h = $handle->child_key_positions();
1082              
1083             =cut
1084              
1085             sub child_key_positions {
1086 0     0   0 my $self = shift;
1087 0         0 my $node = $self->{'curr_node'};
1088              
1089 0         0 return $node->child_key_positions();
1090             }
1091              
1092             =head2 Tree::MultiNode::Handle::get_child_key
1093              
1094             Returns the key at the specified position, or from the corresponding child
1095             node.
1096              
1097             my $key = $handle->get_child_key();
1098              
1099             =cut
1100              
1101             sub get_child_key {
1102 0     0   0 my $self = shift;
1103 0         0 my $pos = shift;
1104 0 0       0 $pos = $self->{'curr_pos'} unless defined $pos;
1105              
1106 0         0 my $node = $self->get_child($pos);
1107 0 0       0 return defined $node ? $node->key() : undef;
1108             }
1109              
1110             =head2 Tree::MultiNode::Handle::get_child_value
1111              
1112             Returns the value at the specified position, or from the corresponding child
1113             node.
1114              
1115             my $value = $handle->get_child_value();
1116              
1117             =cut
1118              
1119             sub get_child_value {
1120 0     0   0 my $self = shift;
1121 0   0     0 my $pos = shift || $self->{'curr_pos'};
1122              
1123 0 0       0 print __PACKAGE__, "::sub get_child_value() pos is: $pos\n"
1124             if $Tree::MultiNode::debug;
1125 0         0 my $node = $self->get_child($pos);
1126 0 0       0 return defined $node ? $node->value() : undef;
1127             }
1128              
1129             =head2 Tree::MultiNode::Handle::remove_child
1130              
1131             Returns Tree::MultiNode::Node::child_kv_paris() for the
1132             current node for this handle.
1133              
1134             my %pairs = $handle->kv_pairs();
1135              
1136             =cut
1137              
1138             sub kv_pairs {
1139 1     1   6 my $self = shift;
1140 1         5 my $node = $self->{'curr_node'};
1141              
1142 1         2 return $node->child_kv_pairs();
1143             }
1144              
1145             =head2 Tree::MultiNode::Handle::remove_child
1146              
1147             =cut
1148              
1149             sub remove_child {
1150 1     1   4 my $self = shift;
1151 1   33     5 my $pos = shift || $self->{'curr_pos'};
1152              
1153 1 50       2 print __PACKAGE__, "::remove_child() pos is: $pos\n"
1154             if $Tree::MultiNode::debug;
1155              
1156 1         3 my $children = $self->{'curr_node'}->children;
1157              
1158 1 50       3 unless ( defined $children ) {
1159 0         0 return undef;
1160             }
1161              
1162 1 50 33     15 unless ( defined $pos && $pos >= 0 && $pos <= $#{$children} ) {
  1   33     4  
1163 0         0 my $num = $#{$children};
  0         0  
1164 0         0 confess "Error, $pos is an invalid position [$num] $children.\n";
1165             }
1166              
1167 1         3 my $node = splice( @{$children}, $pos, 1 );
  1         3  
1168              
1169 1         3 return ( $node->key, $node->value );
1170             }
1171              
1172             =head2 Tree::MultiNode::Handle::child_keys
1173              
1174             Returns the keys from the current node's children.
1175             Returns undef if there is no current node.
1176              
1177             =cut
1178              
1179             sub child_keys {
1180 1     1   3 my $self = shift;
1181 1         3 my $node = $self->{'curr_node'};
1182 1 50       4 return undef unless $node;
1183 1         4 return $node->child_keys();
1184             }
1185              
1186             =head2 Tree::MultiNode::Handle::traverse
1187              
1188             $handle->traverse(sub {
1189             my $h = pop;
1190             printf "%sk: %s v: %s\n",(' ' x $handle->depth()),$h->get_data();
1191             });
1192              
1193             Traverse takes a subroutine reference, and will visit each node of the tree,
1194             starting with the node the handle currently points to, recursively down from the
1195             current position of the handle. Each time the subroutine is called, it will be
1196             passed a handle which points to the node to be visited. Any additional
1197             arguments after the sub ref will be passed to the traverse function _before_
1198             the handle is passed. This should allow you to pass constant arguments to the
1199             sub ref.
1200              
1201             Modifying the node that the handle points to will cause traverse to work
1202             from the new node forward.
1203              
1204             =cut
1205              
1206             sub traverse {
1207 1     1   5 my ( $self, $subref, @args ) = @_;
1208 1 50       4 confess "Error, invalid sub ref: $subref\n" unless 'CODE' eq ref($subref);
1209              
1210             # operate on a cloned handle
1211 1         5 return Tree::MultiNode::Handle->new($self)->_traverseImpl( $subref, @args );
1212             }
1213              
1214             sub _traverseImpl {
1215 7     7   23 my ( $self, $subref, @args ) = @_;
1216 7         19 $subref->( @args, $self );
1217 7         3825 for ( my $i = 0; $i < scalar( $self->children ); ++$i ) {
1218 6         17 $self->down($i);
1219 6         23 $self->_traverseImpl( $subref, @args );
1220 6         12 $self->up();
1221             }
1222 7         16 return;
1223             }
1224              
1225             =head2 Tree::MultiNode::Handle::traverse
1226             or to have
1227             the subref to be a method on an object (and still pass the object's
1228             'self' to the method).
1229              
1230             $handle->traverse( \&Some::Object::method, $obj, $const1, \%const2 );
1231              
1232             ...
1233             sub method
1234             {
1235             my $handle = pop;
1236             my $self = shift;
1237             my $const1 = shift;
1238             my $const2 = shift;
1239             # do something
1240             }
1241             =cut
1242              
1243             sub otraverse {
1244 0     0     my ( $self, $subref, @args ) = @_;
1245 0 0         confess "Error, invalid sub ref: $subref\n" unless 'CODE' eq ref($subref);
1246              
1247             # operate on a cloned handle
1248 0           return Tree::MultiNode::Handle->new($self)->_otraverseImpl( $subref, @args );
1249             }
1250              
1251             sub _otraverseImpl {
1252 0     0     my ( $self, $obj, $method, @args ) = @_;
1253 0           $obj->$method( @args, $self );
1254 0           for ( my $i = 0; $i < scalar( $self->children ); ++$i ) {
1255 0           $self->down($i);
1256 0           $self->_otraverseImpl( $obj, $method, @args );
1257 0           $self->up();
1258             }
1259 0           return;
1260             }
1261              
1262             =head1 SEE ALSO
1263              
1264             Algorithms in C++
1265             Robert Sedgwick
1266             Addison Wesley 1992
1267             ISBN 0201510596
1268              
1269             The Art of Computer Programming, Volume 1: Fundamental Algorithms,
1270             third edition, Donald E. Knuth
1271              
1272             =head1 AUTHORS
1273              
1274             Kyle R. Burton (initial version, and maintenence)
1275              
1276             Daniel X. Pape (see Changes file from the source archive)
1277              
1278             Eric Joanis
1279              
1280             Todd Rinaldo
1281              
1282             =head1 BUGS
1283              
1284             - There is currently no way to remove a child node.
1285              
1286             =cut
1287              
1288             1;