File Coverage

blib/lib/Tree/Base.pm
Criterion Covered Total %
statement 93 97 95.8
branch 19 28 67.8
condition 6 8 75.0
subroutine 24 26 92.3
pod 15 15 100.0
total 157 174 90.2


line stmt bran cond sub pod time code
1             package Tree::Base;
2             $VERSION = v0.10.2;
3              
4 2     2   22039 use warnings;
  2         5  
  2         75  
5 2     2   10 use strict;
  2         4  
  2         69  
6 2     2   20 use Carp;
  2         4  
  2         172  
7              
8 2     2   13 use Scalar::Util ();
  2         11  
  2         2313  
9              
10             =head1 NAME
11              
12             Tree::Base - a base class for trees
13              
14             =head1 SYNOPSIS
15              
16             package MyTree;
17             use base 'Tree::Base';
18              
19             sub blah {shift->{blah}}
20              
21              
22             use MyTree;
23             my $tree = MyTree->new(blah => ...);
24             my $child = $tree->create_child(blah => ...);
25             $child->create_child(blah => ...);
26              
27             =cut
28              
29             =head2 new
30              
31             my $tree = Tree::Base->new(%data);
32              
33             =cut
34              
35             sub new {
36 10     10 1 38 my $package = shift;
37 10   66     36 my $class = ref($package) || $package;
38 10         23 my $self = {@_};
39 10         20 bless($self, $class);
40             # TODO parent
41 10 50       35 die "todo" if($self->{parent});
42 10         19 return($self);
43             } # new ################################################################
44              
45             =head2 create_child
46              
47             my $child = $tree->create_child(%data);
48              
49             =cut
50              
51             sub create_child {
52 7     7 1 1563 my $self = shift;
53 7         17 my $child = $self->new(@_);
54              
55 7         21 return($self->add_child($child));
56             } # create_child #######################################################
57              
58             =head2 add_child
59              
60             $tree->add_child($child);
61              
62             =cut
63              
64             sub add_child {
65 8     8 1 16 my $self = shift;
66 8         10 my $child = shift;
67              
68 8 50       24 croak("cannot add rooted child") if($child->{parent});
69              
70 8         16 $child->{parent} = $self;
71 8         18 my $root = $self->root;
72 8         15 $child->{root} = $root;
73 8         31 Scalar::Util::weaken($child->{parent});
74 8         20 Scalar::Util::weaken($child->{root});
75              
76 8         20 foreach my $child ($child->children) {
77 2     2   6 $child->rmap(sub {Scalar::Util::weaken(shift->{root} = $root); ()})
  2         6  
78 1         8 }
79              
80 8   100     119 my $kids = $self->{children} ||= [];
81 8         15 push(@$kids, $child);
82              
83 8         23 return($child);
84             } # add_child ##########################################################
85              
86             =head2 parent
87              
88             undef if the node is the root.
89              
90             my $parent = $tree->parent;
91              
92             =head2 children
93              
94             my @children = $tree->children;
95              
96             =cut
97              
98             sub children {
99 33     33 1 41 my $self = shift;
100 33 100       95 return($self->{children} ? @{$self->{children}} : ());
  17         54  
101             } # children ###########################################################
102              
103             =head2 child
104              
105             Get the child with index $i.
106              
107             my $child = $toc->child($i);
108              
109             =cut
110              
111             sub child {
112 4     4 1 10 my $self = shift;
113 4         10 my ($i) = @_;
114 4 50       17 (1 == @_) or croak "wrong number of arguments";
115              
116 4         10 my @children = $self->children;
117 4 100       192 $children[$i] or croak "no child at index $i";
118 3         13 return($children[$i]);
119             } # end subroutine child definition
120             ########################################################################
121              
122             =head2 root
123              
124             The root node ($tree if $tree is the root.)
125              
126             my $root = $tree->root;
127              
128             =cut
129              
130             sub root {
131 16     16 1 29 my $self = shift;
132 16 100       65 return(exists($self->{parent}) ? $self->{root} : $self);
133             } # root ###############################################################
134              
135             =head2 is_root
136              
137             True if this is the root node.
138              
139             $tree->is_root;
140              
141             =cut
142              
143 6     6 1 26 sub is_root { return(! exists(shift->{parent})) }
144             ########################################################################
145              
146             =head2 descendants
147              
148             Recursive children.
149              
150             my @descendants = $toc->descendants;
151              
152             =cut
153              
154             sub descendants {
155 1     1 1 3 my $self = shift;
156              
157 1     1   3 return map({$_->rmap(sub {shift})} $self->children);
  1         7  
  1         4  
158             } # descendants ########################################################
159              
160             =head2 older_siblings
161              
162             Nodes before this, at the same level.
163              
164             my @nodes = $tree->older_siblings;
165              
166             =cut
167              
168             sub older_siblings {
169 3     3 1 588 my $self = shift;
170              
171 3 50       9 $self->is_root and return();
172 3         9 my @siblings = $self->parent->children;
173              
174 3 100       12 while(my $s = pop(@siblings)) {($s == $self) and last;}
  7         22  
175              
176 3         17 return(@siblings);
177             } # older_siblings #####################################################
178              
179              
180             =head2 younger_siblings
181              
182             Nodes after this, at the same level.
183              
184             my @nodes = $tree->younger_siblings;
185              
186             =cut
187              
188             sub younger_siblings {
189 2     2 1 4 my $self = shift;
190              
191 2 50       6 $self->is_root and return();
192 2         5 my @siblings = $self->parent->children;
193              
194 2 100       8 while(my $s = shift(@siblings)) {($s == $self) and last;}
  5         19  
195              
196 2         12 return(@siblings);
197             } # younger_siblings ###################################################
198              
199              
200             =head2 next_sibling
201              
202             Returns the next sibling or undef.
203              
204             $younger = $toc->next_sibling;
205              
206             =cut
207              
208             sub next_sibling {
209 1     1 1 3 my $self = shift;
210              
211 1 50       3 my @younger = $self->younger_siblings or return();
212 1         4 return($younger[0]);
213             } # next_sibling #######################################################
214              
215             =head2 prev_sibling
216              
217             Returns the previous sibling or undef.
218              
219             $older = $tree->prev_sibling;
220              
221             =cut
222              
223             sub prev_sibling {
224 1     1 1 2 my $self = shift;
225              
226 1 50       3 my @older = $self->older_siblings or return();
227 1         4 return($older[-1]);
228             } # prev_sibling #######################################################
229              
230             =head2 ancestors
231              
232             Returns all of the node's ancestors (from parent upward.)
233              
234             my @ancestors = $tree->ancestors;
235              
236             =cut
237              
238             sub ancestors {
239 1     1 1 2 my $self = shift;
240 1         2 my $node = $self;
241 1         2 my @ancestors;
242 1         3 while(my $parent = $node->parent) {
243 2         3 push(@ancestors, $parent);
244 2         7 $node = $parent;
245             }
246 1         6 return(@ancestors);
247             } # ancestors ##########################################################
248              
249             =head2 rmap
250              
251             my @ans = $tree->rmap(sub {...});
252              
253             =cut
254              
255             sub rmap {
256 14     14 1 362 my $self = shift;
257 14         20 my ($subref, $knob) = @_;
258 14   66     45 $knob ||= Tree::Base::Knob->new;
259              
260 14         13 my @ans; for ($self) { @ans = $subref->($self, $knob); }
  14         23  
  14         31  
261              
262 14 50       93 $knob->{pruned} and return(@ans);
263              
264 14         26 foreach my $child ($self->children) {
265 10         22 push(@ans, $child->rmap($subref, $knob));
266 10 50       32 $knob->{stopped} and last;
267             }
268 14         58 return(@ans);
269             } # rmap ###############################################################
270              
271 11     11 1 53 sub parent { shift->{parent} }
272              
273             sub DESTROY {
274 10     10   521 my $self = shift;
275 10         55 delete($self->{children});
276             }
277              
278 2     2   155 BEGIN {
279             package Tree::Base::Knob;
280 4     4   23 sub new {return bless({}, 'Tree::Base::Knob')};
281 0     0     sub prune {shift->{pruned} = 1; return()}
  0            
282 0     0     sub stop {shift->{stopped} = 1; return()}
  0            
283             } # Tree::Base::Knob
284             ########################################################################
285              
286             =head1 See Also
287              
288             You may prefer the JavaStyleAccessors of Tree::Simple or one of the
289             other tree modules mentioned in its fine manual. I wanted a tree with
290             lower-cased accessors, fewer methods, a root() which returned undef, and
291             no need to worry about circular references.
292              
293             This module was partially based on the tree functionality of dotReader's
294             dtRdr::TOC object.
295              
296             =head1 AUTHOR
297              
298             Eric Wilhelm @
299              
300             http://scratchcomputing.com/
301              
302             =head1 BUGS
303              
304             If you found this module on CPAN, please report any bugs or feature
305             requests through the web interface at L. I will be
306             notified, and then you'll automatically be notified of progress on your
307             bug as I make changes.
308              
309             If you pulled this development version from my /svn/, please contact me
310             directly.
311              
312             =head1 COPYRIGHT
313              
314             Copyright (C) 2006-2009 Eric L. Wilhelm, All Rights Reserved.
315              
316             =head1 NO WARRANTY
317              
318             Absolutely, positively NO WARRANTY, neither express or implied, is
319             offered with this software. You use this software at your own risk. In
320             case of loss, no person or entity owes you anything whatsoever. You
321             have been warned.
322              
323             =head1 LICENSE
324              
325             This program is free software; you can redistribute it and/or modify it
326             under the same terms as Perl itself.
327              
328             =cut
329              
330             # vi:ts=2:sw=2:et:sta
331             1;