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