File Coverage

blib/lib/Tree/MultiNode/Handle.pm
Criterion Covered Total %
statement 309 320 96.5
branch 62 78 79.4
condition 21 36 58.3
subroutine 40 41 97.5
pod 31 31 100.0
total 463 506 91.5


line stmt bran cond sub pod time code
1             package Tree::MultiNode::Handle;
2 17     17   97 use strict;
  17         27  
  17         530  
3 17     17   70 use warnings;
  17         24  
  17         607  
4 17     17   66 use Carp;
  17         22  
  17         886  
5 17     17   102 use Scalar::Util qw(weaken);
  17         40  
  17         1357  
6              
7             our $VERSION = '2.02';
8              
9             sub _debug {
10 1529 50   1529   2547 return unless $Tree::MultiNode::debug;
11 17     17   110 no warnings 'uninitialized';
  17         45  
  17         51831  
12 0         0 print @_;
13             }
14              
15             =head1 NAME
16              
17             Tree::MultiNode::Handle -- a cursor for navigating a Tree::MultiNode tree.
18              
19             =head1 DESCRIPTION
20              
21             Handle is used as a 'pointer' into the tree. It has a few attributes that it keeps
22             track of. These are:
23              
24             1. the top of the tree
25             2. the current node
26             3. the current child node
27             4. the depth of the current node
28              
29             The top of the tree never changes, and you can reset the handle to point back at
30             the top of the tree by calling the top() method.
31              
32             The current node is where the handle is 'pointing' in the tree. The current node
33             is changed with functions like top(), down(), and up().
34              
35             The current child node is used for traversing downward into the tree. The members
36             first(), next(), prev(), last(), and position() can be used to set the current child,
37             and then traverse down into it.
38              
39             The depth of the current node is a measure of the length of the path
40             from the top of the tree to the current node, i.e., the top of the node
41             has a depth of 0, each of its children has a depth of 1, etc.
42              
43             =cut
44              
45             =head2 Tree::MultiNode::Handle::new
46              
47             Constructs a new handle. You must pass a tree object to Handle::new.
48              
49             my $tree = Tree::MultiNode->new;
50             my $handle = Tree::MultiNode::Handle->new($tree);
51              
52             =cut
53              
54             sub new {
55 101     101 1 152102 my $this = shift;
56 101   33     254 my $class = ref($this) || $this;
57              
58 101         150 my $self = {};
59 101         146 bless $self, $class;
60 101         126 my $data = shift;
61 101         446 _debug(__PACKAGE__, "::new() ref($data) is: ", ref($data), "\n");
62 101 100       198 if ( ref($data) eq "Tree::MultiNode::Handle" ) {
63 10         25 $self->_clone($data);
64             }
65             else {
66 91 100       203 unless ( ref($data) eq "Tree::MultiNode" ) {
67 1         262 confess "Error, invalid Tree::MultiNode reference: $data\n";
68             }
69              
70 90         191 $self->{'tree'} = $data;
71 90         136 $self->{'curr_pos'} = undef;
72 90         136 $self->{'curr_node'} = $data->{'top'};
73 90         146 $self->{'curr_child'} = undef;
74 90         166 $self->{'curr_depth'} = 0;
75             }
76 100         221 return $self;
77             }
78              
79             #
80             # internal method for making the current handle a copy of another
81             # handle...
82             #
83             sub _clone {
84 10     10   14 my $self = shift;
85 10         16 my $them = shift;
86 10         29 _debug(__PACKAGE__, "::_clone() cloning: ", $them, "\n");
87 10         26 _debug(__PACKAGE__, "::_clone() depth: ", $them->{'curr_depth'}, "\n");
88 10         17 $self->{'tree'} = $them->{'tree'};
89 10         20 $self->{'curr_pos'} = $them->{'curr_pos'};
90 10         18 $self->{'curr_node'} = $them->{'curr_node'};
91 10         18 $self->{'curr_child'} = $them->{'curr_child'};
92 10         29 $self->{'curr_depth'} = $them->{'curr_depth'};
93 10         17 return 1;
94             }
95              
96             =head2 Tree::MultiNode::Handle::tree
97              
98             Returns the tree that was used to construct the node. Useful if you're
99             trying to create another node into the tree.
100              
101             my $handle2 = Tree::MultiNode::Handle->new($handle->tree());
102              
103             =cut
104              
105             sub tree {
106 1     1 1 5 my $self = shift;
107 1         3 return $self->{'tree'};
108             }
109              
110             =head2 Tree::MultiNode::Handle::get_data
111              
112             Retrieves both the key, and value (as an array) for the current node.
113              
114             my ($key,$val) = $handle->get_data();
115              
116             =cut
117              
118             sub get_data {
119 8     8 1 18 my $self = shift;
120 8         14 my $node = $self->{'curr_node'};
121              
122 8         34 return ( $node->key, $node->value );
123             }
124              
125             =head2 Tree::MultiNode::Handle::get_key
126              
127             Retrieves the key for the current node.
128              
129             $key = $handle->get_key();
130              
131             =cut
132              
133             sub get_key {
134 48     48 1 1369 my $self = shift;
135 48         71 my $node = $self->{'curr_node'};
136              
137 48         148 my $key = $node->key();
138              
139 48         119 _debug(__PACKAGE__, "::get_key() getting from ", $node, " : ", $key, "\n");
140              
141 48         199 return $key;
142             }
143              
144             =head2 Tree::MultiNode::Handle::set_key
145              
146             Sets the key for the current node.
147              
148             $handle->set_key("lname");
149              
150             =cut
151              
152             sub set_key {
153 63     63 1 728 my $self = shift;
154 63         81 my $key = shift;
155 63         107 my $node = $self->{'curr_node'};
156              
157 63         141 _debug(__PACKAGE__, "::set_key() setting key \"", $key, "\" on: ", $node, "\n");
158              
159 63         143 return $node->key($key);
160             }
161              
162             =head2 Tree::MultiNode::Handle::get_value
163              
164             Retrieves the value for the current node.
165              
166             $val = $handle->get_value();
167              
168             =cut
169              
170             sub get_value {
171 7     7 1 19 my $self = shift;
172 7         67 my $node = $self->{'curr_node'};
173              
174 7         77 my $value = $node->value();
175              
176 7         29 _debug(__PACKAGE__, "::get_value() getting from ", $node, " : ", $value, "\n");
177              
178 7         29 return $value;
179             }
180              
181             =head2 Tree::MultiNode::Handle::set_value
182              
183             Sets the value for the current node.
184              
185             $handle->set_value("Wall");
186              
187             =cut
188              
189             sub set_value {
190 9     9 1 28 my $self = shift;
191 9         13 my $value = shift;
192 9         20 my $node = $self->{'curr_node'};
193              
194 9         27 _debug(__PACKAGE__, "::set_value() setting value \"", $value, "\" on: ", $node, "\n");
195              
196 9         22 return $node->value($value);
197             }
198              
199             =head2 Tree::MultiNode::Handle::get_child
200              
201             get_child takes an optional parameter which is the position of the child
202             that is to be retrieved. If this position is not specified, get_child
203             attempts to return the current child. get_child returns a Node object.
204              
205             my $child_node = $handle->get_child();
206              
207             =cut
208              
209             sub get_child {
210 118     118 1 182 my $self = shift;
211 118         276 my $children = $self->{'curr_node'}->children;
212 118         188 my $pos = shift;
213 118 50       277 $pos = defined $pos ? $pos : $self->{'curr_pos'};
214              
215 118         272 _debug(__PACKAGE__, "::get_child() children: ", $children, " ", $pos, "\n");
216              
217 118 50       206 unless ( defined $children ) {
218 0         0 return undef;
219             }
220              
221 118 50 33     244 unless ( defined $pos && $pos <= $#{$children} ) {
  118         296  
222 0         0 my $num = $#{$children};
  0         0  
223 0         0 confess "Error, $pos is an invalid position [$num] $children.\n";
224             }
225              
226             _debug(__PACKAGE__, "::get_child() returning [$pos]: ",
227 118         237 ${$children}[$pos], "\n");
  118         290  
228 118         145 return ( ${$children}[$pos] );
  118         253  
229             }
230              
231             =head2 Tree::MultiNode::Handle::add_child
232              
233             This member adds a new child node to the end of the array of children for the
234             current node. There are three optional parameters:
235              
236             - a key
237             - a value
238             - a position
239              
240             If passed, the key and value will be set in the new child. If a position is
241             passed, the new child will be inserted into the current array of children at
242             the position specified.
243              
244             $handle->add_child(); # adds a blank child
245             $handle->add_child("language","perl"); # adds a child to the end
246             $handle->add_child("language","C++",0); # adds a child to the front
247              
248             =cut
249              
250             sub add_child {
251 176     176 1 1401 my $self = shift;
252 176         404 my ( $key, $value, $pos ) = @_;
253 176         388 my $children = $self->{'curr_node'}->children;
254 176         317 _debug(__PACKAGE__, "::add_child() children: ", $children, "\n");
255 176         352 my $child = Tree::MultiNode::Node->new( $key, $value );
256 176         261 $child->{'parent'} = $self->{'curr_node'};
257 176         244 weaken($child->{'parent'});
258              
259 176         370 _debug(__PACKAGE__, "::add_child() adding child ", $child, " (", $key, ",", $value, ") ",
260             "to: ", $children, "\n");
261              
262 176 100       297 if ( defined $pos ) {
263 2         5 _debug(__PACKAGE__, "::add_child() adding at ", $pos, " ", $child, "\n");
264 2 100 66     9 unless ( $pos >= 0 && $pos <= $#{$children} ) {
  1         4  
265 1         1 my $num = $#{$children};
  1         9  
266 1         273 confess "Position $pos is invalid for child position [$num] $children.\n";
267             }
268 1         1 splice( @{$children}, $pos, 1, $child, ${$children}[$pos] );
  1         2  
  1         2  
269             }
270             else {
271 174         271 _debug(__PACKAGE__, "::add_child() adding at end ", $child, "\n");
272 174         206 push @{$children}, $child;
  174         276  
273             }
274              
275             _debug(__PACKAGE__, "::add_child() children:",
276 175         217 join( ',', @{ $self->{'curr_node'}->children } ), "\n");
  175         298  
277             }
278              
279             =head2 Tree::MultiNode::Handle::add_child_node
280              
281             Adds an existing node (or the top node of another tree) as a child of the
282             current node. Works like C but accepts a pre-built
283             L or L object instead of a key/value
284             pair.
285              
286             When a Tree::MultiNode (tree) object is passed, its top node is extracted
287             and added as a child. The original tree remains valid but now shares
288             structure with this tree -- the caller should not modify the original tree
289             after this call.
290              
291             When a position is given, the new child is inserted before the existing
292             child at that position. Without a position, the child is appended to the
293             end.
294              
295             # append an existing node as the last child
296             my $node = Tree::MultiNode::Node->new("color", "red");
297             $handle->add_child_node($node);
298              
299             # insert at a specific position
300             $handle->add_child_node($node, 0); # insert as first child
301              
302             # graft another tree's root node
303             my $other = Tree::MultiNode->new();
304             $handle->add_child_node($other);
305              
306             =cut
307              
308             sub add_child_node {
309 9     9 1 685 my $self = shift;
310 9         18 my ( $child, $pos ) = @_;
311 9         21 my $children = $self->{'curr_node'}->children;
312 9         17 _debug(__PACKAGE__, "::add_child_node() children: ", $children, "\n");
313 9 100       19 if ( ref($child) eq 'Tree::MultiNode' ) {
314 2         4 $child = $child->{'top'};
315             }
316 9 100       744 confess "Invalid child argument.\n"
317             if ( ref($child) ne 'Tree::MultiNode::Node' );
318              
319 6         11 $child->{'parent'} = $self->{'curr_node'};
320 6         11 weaken($child->{'parent'});
321              
322 6         33 _debug(__PACKAGE__, "::add_child_node() adding child ", $child,
323             " to: ", $children, "\n");
324              
325 6 100       11 if ( defined $pos ) {
326 2         6 _debug(__PACKAGE__, "::add_child_node() adding at ", $pos, " ", $child, "\n");
327 2 100 66     7 unless ( $pos >= 0 && $pos <= $#{$children} ) {
  1         3  
328 1         1 my $num = $#{$children};
  1         2  
329 1         220 confess "Position $pos is invalid for child position [$num] $children.\n";
330             }
331 1         2 splice( @{$children}, $pos, 1, $child, ${$children}[$pos] );
  1         2  
  1         2  
332             }
333             else {
334 4         10 _debug(__PACKAGE__, "::add_child_node() adding at end ", $child, "\n");
335 4         4 push @{$children}, $child;
  4         8  
336             }
337              
338             _debug(__PACKAGE__, "::add_child_node() children:",
339 5         8 join( ',', @{ $self->{'curr_node'}->children } ), "\n");
  5         14  
340             }
341              
342             =head2 Tree::MultiNode::Handle::depth
343              
344             Gets the depth for the current node.
345              
346             my $depth = $handle->depth();
347              
348             =cut
349              
350             sub depth {
351 29     29 1 123 my $self = shift;
352 29         57 my $node = $self->{'curr_node'};
353              
354 29         95 _debug(__PACKAGE__, "::depth() getting depth \"", $self->{'curr_depth'}, "\" ",
355             "on: ", $node, "\n");
356              
357 29         123 return $self->{'curr_depth'};
358             }
359              
360             =head2 Tree::MultiNode::Handle::select
361              
362             Sets the current child via a specified value -- basically it iterates
363             through the array of children, looking for a match. You have to
364             supply the key to look for, and optionally a sub ref to find it. The
365             default for this sub is
366              
367             sub { return shift eq shift; }
368              
369             Which is sufficient for testing the equality of strings (the most common
370             thing that I think will get stored in the tree). If you're storing multiple
371             data types as keys, you'll have to write a sub that figures out how to
372             perform the comparisons in a sane manner.
373              
374             The code reference should take two arguments, and compare them -- return
375             false if they don't match, and true if they do.
376              
377             $handle->select('lname', sub { return shift eq shift; } );
378              
379             =cut
380              
381             sub select {
382 8     8 1 28 my $self = shift;
383 8         12 my $key = shift;
384 8   66 12   44 my $code = shift || sub { return shift eq shift; };
  12         32  
385 8         12 my ( $child, $pos );
386 8         15 my $found = undef;
387              
388 8         11 $pos = 0;
389 8         21 foreach $child ( $self->children() ) {
390 14 100       30 if ( $code->( $key, $child->key() ) ) {
391 5         13 $self->{'curr_pos'} = $pos;
392 5         8 $self->{'curr_child'} = $child;
393 5         7 ++$found;
394 5         13 last;
395             }
396 9         23 ++$pos;
397             }
398              
399 8         39 return $found;
400             }
401              
402             =head2 Tree::MultiNode::Handle::position
403              
404             Sets, or retrieves the current child position.
405              
406             print "curr child pos is: ", $handle->position(), "\n";
407             $handle->position(5); # sets the 6th child as the current child
408              
409             =cut
410              
411             sub position {
412 40     40 1 3498 my $self = shift;
413 40         71 my $pos = shift;
414              
415 40         98 _debug(__PACKAGE__, "::position() ", $self, " ", $pos, "\n");
416              
417 40 100       82 unless ( defined $pos ) {
418 11         50 return $self->{'curr_pos'};
419             }
420              
421 29         63 my $children = $self->{'curr_node'}->children;
422 29         57 _debug(__PACKAGE__, "::position() children: ", $children, "\n");
423             _debug(__PACKAGE__, "::position() position is $pos ",
424 29         59 $#{$children}, "\n");
  29         98  
425 29 100 66     85 unless ( $pos >= 0 && $pos <= $#{$children} ) {
  28         77  
426 1         2 my $num = $#{$children};
  1         2  
427 1         213 confess "Error, $pos is invalid [$num] $children.\n";
428             }
429 28         60 $self->{'curr_pos'} = $pos;
430 28         59 $self->{'curr_child'} = $self->get_child($pos);
431 28         95 return $self->{'curr_pos'};
432             }
433              
434             =head2 Tree::MultiNode::Handle::first
435             Tree::MultiNode::Handle::next
436             Tree::MultiNode::Handle::prev
437             Tree::MultiNode::Handle::last
438              
439             These functions manipulate the current child member. first() sets the first
440             child as the current child, while last() sets the last. next(), and prev() will
441             move to the next/prev child respectively. If there is no current child node,
442             next() will have the same effect as first(), and prev() will operate as last().
443             prev() fails if the current child is the first child, and next() fails if the
444             current child is the last child -- i.e., they do not wrap around.
445              
446             These functions will fail if there are no children for the current node.
447              
448             $handle->first(); # sets to the 0th child
449             $handle->next(); # to the 1st child
450             $handle->prev(); # back to the 0th child
451             $handle->last(); # go straight to the last child.
452              
453             =cut
454              
455             sub first {
456 40     40 1 122 my $self = shift;
457 40         121 my $children = $self->{'curr_node'}->children;
458              
459 40 100 66     117 unless ( defined $children && @{$children} ) {
  40         113  
460 1         6 return undef;
461             }
462              
463 39         67 $self->{'curr_pos'} = 0;
464 39         105 $self->{'curr_child'} = $self->get_child(0);
465             _debug(__PACKAGE__, "::first() set child[", $self->{'curr_pos'}, "]: ",
466 39         119 $self->{'curr_child'}, "\n");
467 39         103 return $self->{'curr_pos'};
468             }
469              
470             sub next {
471 9     9 1 20 my $self = shift;
472 9         34 my $children = $self->{'curr_node'}->children;
473 9         26 _debug(__PACKAGE__, "::next() children: ", $children, "\n");
474              
475             # If no current child, behave like first() per documented contract
476 9 100       25 unless ( defined $self->{'curr_pos'} ) {
477 2         7 return $self->first();
478             }
479              
480 7         10 my $pos = $self->{'curr_pos'} + 1;
481 7 100 66     23 unless ( $pos >= 0 && $pos <= $#{$children} ) {
  7         25  
482 2         11 return undef;
483             }
484              
485 5         8 $self->{'curr_pos'} = $pos;
486 5         13 $self->{'curr_child'} = $self->get_child($pos);
487 5         13 return $self->{'curr_pos'};
488             }
489              
490             sub prev {
491 6     6 1 11 my $self = shift;
492 6         19 my $children = $self->{'curr_node'}->children;
493 6         15 _debug(__PACKAGE__, "::prev() children: ", $children, "\n");
494              
495             # If no current child, behave like last() per documented contract
496 6 100       14 unless ( defined $self->{'curr_pos'} ) {
497 2         5 return $self->last();
498             }
499              
500 4         6 my $pos = $self->{'curr_pos'} - 1;
501 4 100 66     10 unless ( $pos >= 0 && $pos <= $#{$children} ) {
  3         10  
502 1         3 return undef;
503             }
504              
505 3         3 $self->{'curr_pos'} = $pos;
506 3         7 $self->{'curr_child'} = $self->get_child($pos);
507 3         10 return $self->{'curr_pos'};
508             }
509              
510             sub last {
511 8     8 1 19 my $self = shift;
512 8         35 my $children = $self->{'curr_node'}->children;
513              
514 8 100 66     37 unless ( defined $children && @{$children} ) {
  8         34  
515 1         5 return undef;
516             }
517              
518 7         14 my $pos = $#{$children};
  7         14  
519 7         22 _debug(__PACKAGE__, "::last() children [", $pos, "]: ", $children, "\n");
520              
521 7         33 $self->{'curr_pos'} = $pos;
522 7         20 $self->{'curr_child'} = $self->get_child($pos);
523 7         28 return $self->{'curr_pos'};
524             }
525              
526             =head2 Tree::MultiNode::Handle::down
527              
528             down() moves the handle to point at the current child node. It fails
529             if there is no current child node. When down() is called, the current
530             child becomes invalid (undef).
531              
532             $handle->down();
533              
534             =cut
535              
536             sub down {
537 58     58 1 137 my $self = shift;
538 58         80 my $pos = shift;
539 58         90 my $node = $self->{'curr_node'};
540 58 50       113 return undef unless defined $node;
541 58         182 my $children = $node->children;
542 58         128 _debug(__PACKAGE__, "::down() children: ", $children, "\n");
543              
544 58 100       116 if ( defined $pos ) {
545 25 50       70 unless ( defined $self->position($pos) ) {
546 0         0 confess "Error, $pos was an invalid position.\n";
547             }
548             }
549              
550             # Prevent corrupting the handle when no child is selected
551 58 100       126 unless ( defined $self->{'curr_child'} ) {
552 3         12 return undef;
553             }
554              
555 55         85 $self->{'curr_pos'} = undef;
556 55         105 $self->{'curr_node'} = $self->{'curr_child'};
557 55         113 $self->{'curr_child'} = undef;
558 55         81 ++$self->{'curr_depth'};
559 55         104 _debug(__PACKAGE__, "::down() set to: ", $self->{'curr_node'}, "\n");
560              
561 55         119 return 1;
562             }
563              
564             =head2 Tree::MultiNode::Handle::up
565              
566             up() moves the handle to point at the parent of the current node. It fails
567             if there is no parent node. When up() is called, the current child becomes
568             invalid (undef).
569              
570             $handle->up();
571              
572             =cut
573              
574             sub up {
575 33     33 1 3421 my $self = shift;
576 33         60 my $node = $self->{'curr_node'};
577 33 50       84 return undef unless defined $node;
578 33         104 my $parent = $node->parent();
579              
580 33 100       92 unless ( defined $parent ) {
581 1         6 return undef;
582             }
583              
584 32         60 $self->{'curr_pos'} = undef;
585 32         74 $self->{'curr_node'} = $parent;
586 32         54 $self->{'curr_child'} = undef;
587 32         49 --$self->{'curr_depth'};
588              
589 32         89 return 1;
590             }
591              
592             =head2 Tree::MultiNode::Handle::top
593              
594             Resets the handle to point back at the top of the tree.
595             When top() is called, the current child becomes invalid (undef).
596              
597             $handle->top();
598              
599             =cut
600              
601             sub top {
602 8     8 1 29 my $self = shift;
603 8         18 my $tree = $self->{'tree'};
604              
605 8         17 $self->{'curr_pos'} = undef;
606 8         19 $self->{'curr_node'} = $tree->{'top'};
607 8         18 $self->{'curr_child'} = undef;
608 8         16 $self->{'curr_depth'} = 0;
609              
610 8         24 return 1;
611             }
612              
613             =head2 Tree::MultiNode::Handle::children
614              
615             This returns an array of Node objects that represents the children of the
616             current Node. Unlike Node::children(), the array Handle::children() is not
617             a reference to an array, but an array. Useful if you need to iterate through
618             the children of the current node.
619              
620             print "There are: ", scalar($handle->children()), " children\n";
621             foreach $child ($handle->children()) {
622             print $child->key(), " : ", $child->value(), "\n";
623             }
624              
625             =cut
626              
627             sub children {
628 20     20 1 1693 my $self = shift;
629 20         34 my $node = $self->{'curr_node'};
630 20 50       46 return undef unless defined $node;
631 20         60 my $children = $node->children;
632              
633 20         77 return @{$children};
  20         69  
634             }
635              
636             =head2 Tree::MultiNode::Handle::num_children
637              
638             Returns the number of children for the current node. This is more
639             efficient than Cchildren())> because it does not
640             copy the children array.
641              
642             my $count = $handle->num_children();
643              
644             =cut
645              
646             sub num_children {
647 33     33 1 52 my $self = shift;
648 33         124 return $self->{'curr_node'}->num_children();
649             }
650              
651             =head2 Tree::MultiNode::Handle::child_key_positions
652              
653             This function returns a hash table that consists of the
654             child keys as the hash keys, and the position in the child
655             array as the value. This allows for a quick and dirty way
656             of looking up the position of a given key in the child list.
657              
658             my %h = $handle->child_key_positions();
659              
660             =cut
661              
662             sub child_key_positions {
663 2     2 1 10 my $self = shift;
664 2         5 my $node = $self->{'curr_node'};
665              
666 2         7 return $node->child_key_positions();
667             }
668              
669             =head2 Tree::MultiNode::Handle::get_child_key
670              
671             Returns the key at the specified position, or from the corresponding child
672             node.
673              
674             my $key = $handle->get_child_key();
675              
676             =cut
677              
678             sub get_child_key {
679 29     29 1 402 my $self = shift;
680 29         45 my $pos = shift;
681 29 100       82 $pos = $self->{'curr_pos'} unless defined $pos;
682              
683 29         66 my $node = $self->get_child($pos);
684 29 50       94 return defined $node ? $node->key() : undef;
685             }
686              
687             =head2 Tree::MultiNode::Handle::get_child_value
688              
689             Returns the value at the specified position, or from the corresponding child
690             node.
691              
692             my $value = $handle->get_child_value();
693              
694             =cut
695              
696             sub get_child_value {
697 6     6 1 16 my $self = shift;
698 6         10 my $pos = shift;
699 6 100       19 $pos = defined $pos ? $pos : $self->{'curr_pos'};
700              
701 6         17 _debug(__PACKAGE__, "::get_child_value() pos is: ", $pos, "\n");
702 6         14 my $node = $self->get_child($pos);
703 6 50       22 return defined $node ? $node->value() : undef;
704             }
705              
706             =head2 Tree::MultiNode::Handle::kv_pairs
707              
708             Returns Tree::MultiNode::Node::child_kv_pairs() for the
709             current node for this handle.
710              
711             my %pairs = $handle->kv_pairs();
712              
713             =cut
714              
715             sub kv_pairs {
716 5     5 1 1281 my $self = shift;
717 5         12 my $node = $self->{'curr_node'};
718              
719 5         20 return $node->child_kv_pairs();
720             }
721              
722             =head2 Tree::MultiNode::Handle::remove_child
723              
724             Removes the child at the specified position, or at the current child
725             position if no position is given. Returns the key and value of the
726             removed child node.
727              
728             my ($key, $value) = $handle->remove_child(0);
729              
730             =cut
731              
732             sub remove_child {
733 9     9 1 356 my $self = shift;
734 9         12 my $pos = shift;
735 9 100       54 $pos = defined $pos ? $pos : $self->{'curr_pos'};
736              
737 9         21 _debug(__PACKAGE__, "::remove_child() pos is: ", $pos, "\n");
738              
739 9         22 my $children = $self->{'curr_node'}->children;
740              
741 9 50       22 unless ( defined $children ) {
742 0         0 return undef;
743             }
744              
745 9 100 33     44 unless ( defined $pos && $pos >= 0 && $pos <= $#{$children} ) {
  9   66     35  
746 1         1 my $num = $#{$children};
  1         2  
747 1         256 confess "Error, $pos is an invalid position [$num] $children.\n";
748             }
749              
750 8         11 my $node = splice( @{$children}, $pos, 1 );
  8         16  
751 8         16 $self->{'curr_node'}->{'children'} = $children;
752              
753             # Reset handle's child cursor to avoid stale references
754 8         14 $self->{'curr_pos'} = undef;
755 8         12 $self->{'curr_child'} = undef;
756              
757 8         21 return ( $node->key, $node->value );
758             }
759              
760             =head2 Tree::MultiNode::Handle::child_keys
761              
762             Returns the keys from the current node's children.
763             Returns undef if there is no current node.
764              
765             =cut
766              
767             sub child_keys {
768 5     5 1 695 my $self = shift;
769 5         12 my $node = $self->{'curr_node'};
770 5 50       17 return undef unless $node;
771 5         18 return $node->child_keys();
772             }
773              
774             =head2 Tree::MultiNode::Handle::child_values
775              
776             Returns the values from the current node's children.
777             Returns undef if there is no current node.
778              
779             =cut
780              
781             sub child_values {
782 0     0 1 0 my $self = shift;
783 0         0 my $node = $self->{'curr_node'};
784 0 0       0 return undef unless $node;
785 0         0 return $node->child_values();
786             }
787              
788             =head2 Tree::MultiNode::Handle::traverse
789              
790             $handle->traverse(sub {
791             my $h = pop;
792             printf "%sk: %s v: %s\n",(' ' x $handle->depth()),$h->get_data();
793             });
794              
795             Traverse takes a subroutine reference, and will visit each node of the tree,
796             starting with the node the handle currently points to, recursively down from the
797             current position of the handle. Each time the subroutine is called, it will be
798             passed a handle which points to the node to be visited. Any additional
799             arguments after the sub ref will be passed to the traverse function _before_
800             the handle is passed. This should allow you to pass constant arguments to the
801             sub ref.
802              
803             Modifying the node that the handle points to will cause traverse to work
804             from the new node forward.
805              
806             =cut
807              
808             sub traverse {
809 7     7 1 49 my ( $self, $subref, @args ) = @_;
810 7 50       27 confess "Error, invalid sub ref: $subref\n" unless 'CODE' eq ref($subref);
811              
812             # operate on a cloned handle
813 7         19 return Tree::MultiNode::Handle->new($self)->_traverseImpl( $subref, @args );
814             }
815              
816             sub _traverseImpl {
817 22     22   45 my ( $self, $subref, @args ) = @_;
818 22         75 $subref->( @args, $self );
819 22         7126 my $num_children = $self->num_children();
820 22         79 for ( my $i = 0; $i < $num_children; ++$i ) {
821 15         63 $self->down($i);
822 15         51 $self->_traverseImpl( $subref, @args );
823 15         65 $self->up();
824             }
825 22         47 return;
826             }
827              
828             =head2 Tree::MultiNode::Handle::otraverse
829              
830             Like traverse(), but designed for passing an object method. The first
831             argument after the handle should be the object, the second should be
832             the method name or code reference, followed by any additional arguments.
833             The handle is passed as the last argument to the method.
834              
835             This allows you to have the subref be a method on an object (and still
836             pass the object's 'self' to the method).
837              
838             $handle->otraverse( $obj, \&Some::Object::method, $const1, \%const2 );
839              
840             ...
841             sub method
842             {
843             my $handle = pop;
844             my $self = shift;
845             my $const1 = shift;
846             my $const2 = shift;
847             # do something
848             }
849             =cut
850              
851             sub otraverse {
852 1     1 1 19 my ( $self, $obj, $method, @args ) = @_;
853 1 50       11 confess "Error, invalid sub ref: $method\n" unless 'CODE' eq ref($method);
854              
855             # operate on a cloned handle
856 1         3 return Tree::MultiNode::Handle->new($self)->_otraverseImpl( $obj, $method, @args );
857             }
858              
859             sub _otraverseImpl {
860 4     4   8 my ( $self, $obj, $method, @args ) = @_;
861 4         11 $obj->$method( @args, $self );
862 4         8 my $num_children = $self->num_children();
863 4         10 for ( my $i = 0; $i < $num_children; ++$i ) {
864 3         5 $self->down($i);
865 3         11 $self->_otraverseImpl( $obj, $method, @args );
866 3         7 $self->up();
867             }
868 4         10 return;
869             }
870              
871             1;