File Coverage

blib/lib/Forest/Tree.pm
Criterion Covered Total %
statement 61 69 88.4
branch 27 34 79.4
condition 8 11 72.7
subroutine 14 18 77.7
pod 13 14 92.8
total 123 146 84.2


line stmt bran cond sub pod time code
1             package Forest::Tree;
2 15     15   2266806 use Moose;
  15         18241708  
  15         150  
3              
4 15     15   153646 use Scalar::Util 'reftype', 'refaddr';
  15         43  
  15         1597  
5 15     15   95 use List::Util 'sum', 'max';
  15         39  
  15         23627  
6              
7             our $VERSION = '0.10';
8             our $AUTHORITY = 'cpan:STEVAN';
9              
10             extends qw(Forest::Tree::Pure);
11              
12             #has '+node' => ( is => 'rw' );
13             has 'node' => (
14             traits => [qw(StorableClone)],
15             is => 'rw',
16             isa => 'Item',
17             );
18              
19             sub set_node {
20 0     0 1 0 my ( $self, $new ) = @_;
21 0         0 $self->node($new);
22 0         0 $self;
23             }
24              
25             has 'parent' => (
26             traits => [qw(NoClone)],
27             reader => 'parent',
28             writer => '_set_parent',
29             predicate => 'has_parent',
30             clearer => 'clear_parent',
31             isa => 'Maybe[Forest::Tree]',
32             weak_ref => 1,
33             handles => {
34             'add_sibling' => 'add_child',
35             'get_sibling_at' => 'get_child_at',
36             'insert_sibling_at' => 'insert_child_at',
37             },
38             );
39              
40             #has '+children' => (
41             # is => 'rw',
42             has 'children' => (
43             traits => [qw(Array Clone)],
44             is => 'rw',
45             isa => 'ArrayRef[Forest::Tree]',
46             lazy => 1,
47             default => sub { [] },
48             handles => {
49             get_child_at => 'get',
50             child_count => 'count',
51             },
52             trigger => sub {
53             my ($self, $children) = @_;
54             foreach my $child (@$children) {
55             $child->_set_parent($self);
56             $self->clear_height if $self->has_height;
57             $self->clear_size if $self->has_size;
58             }
59             }
60             );
61              
62             after 'clear_size' => sub {
63             my $self = shift;
64             $self->parent->clear_size
65             if $self->has_parent && $self->parent->has_size;
66             };
67              
68             after 'clear_height' => sub {
69             my $self = shift;
70             $self->parent->clear_height
71             if $self->has_parent && $self->parent->has_height;
72             };
73              
74             ## informational
75 76     76 1 27716 sub is_root { !(shift)->has_parent }
76              
77             ## depth
78 118   100 118 1 13755 sub depth { ((shift)->parent || return -1)->depth + 1 }
79              
80             ## child management
81              
82             sub add_child {
83 61     61 1 5382 my ($self, $child) = @_;
84 61 100 100     708 (blessed($child) && $child->isa(ref $self))
    100          
85             || confess "Child parameter must be a " . ref($self) . " not (" . (defined $child ? $child : 'undef') . ")";
86 57         2772 $child->_set_parent($self);
87 57 100       3200 $self->clear_height if $self->has_height;
88 57 100       2622 $self->clear_size if $self->has_size;
89 57         202 push @{ $self->children } => $child;
  57         2425  
90 57         1368 $self;
91             }
92              
93             sub replace {
94 1     1 1 2 my ( $self, $replacement ) = @_;
95              
96 1 50       5 confess "Can't replace root" if $self->is_root;
97              
98 1         34 $self->parent->set_child_at( $self->get_index_in_siblings, $replacement );
99              
100 1         4 return $replacement;
101             }
102              
103             sub add_children {
104 6     6 1 18 my ($self, @children) = @_;
105 6         26 $self->add_child($_) for @children;
106 6         22 return $self;
107             }
108              
109             sub set_child_at {
110 2     2 1 5 my ( $self, $index, $child ) = @_;
111              
112 2 0 33     21 (blessed($child) && $child->isa(ref $self))
    50          
113             || confess "Child parameter must be a " . ref($self) . " not (" . (defined $child ? $child : 'undef') . ")";
114              
115 2 100       81 $self->clear_height if $self->has_height;
116 2 100       86 $self->clear_size if $self->has_size;
117              
118 2         70 my $children = $self->children;
119              
120 2         75 $children->[$index]->clear_parent;
121              
122 2         3 $children->[$index] = $child;
123 2         74 $child->_set_parent($self);
124              
125 2         5 $self;
126             }
127              
128             sub insert_child_at {
129 7     7 1 4642 my ($self, $index, $child) = @_;
130 7 50 66     102 (blessed($child) && $child->isa(ref $self))
    100          
131             || confess "Child parameter must be a " . ref($self) . " not (" . (defined $child ? $child : 'undef') . ")";
132 3         143 $child->_set_parent($self);
133 3 100       156 $self->clear_height if $self->has_height;
134 3 100       134 $self->clear_size if $self->has_size;
135 3         13 splice @{ $self->children }, $index, 0, $child;
  3         114  
136 3         12 $self;
137             }
138              
139             sub remove_child_at {
140 2     2 1 5 my ($self, $index) = @_;
141 2 100       113 $self->clear_height if $self->has_height;
142 2 100       86 $self->clear_size if $self->has_size;
143 2         9 my $child = splice @{ $self->children }, $index, 1;
  2         71  
144 2         79 $child->clear_parent;
145 2         11 $child;
146             }
147              
148             ##siblings
149              
150             sub siblings {
151 2     2 1 8 my $self = shift;
152 2 50       87 return [] unless $self->has_parent;
153 2         4 [ grep { $self->uid ne $_->uid } @{ $self->parent->children } ];
  3         118  
  2         78  
154             }
155              
156             sub get_index_in_siblings {
157 23     23 1 37 my ($self) = @_;
158 23 50       50 return -1 if $self->is_root;
159              
160 23         756 $self->parent->get_child_index($self);
161             }
162              
163             ## cloning
164              
165 0     0 0   sub clone_and_detach { shift->clone(@_) }
166              
167             sub to_pure_tree {
168 0     0 1   my $self = shift;
169              
170 0           $self->reconstruct_with_class("Forest::Tree::Pure");
171             }
172              
173             sub to_mutable_tree {
174 0     0 1   my $self = shift;
175              
176 0           return $self;
177             }
178              
179             __PACKAGE__->meta->make_immutable;
180              
181 15     15   128 no Moose; 1;
  15         31  
  15         109  
182              
183             __END__
184              
185             =pod
186              
187             =head1 NAME
188              
189             Forest::Tree - An n-ary tree
190              
191             =head1 SYNOPSIS
192              
193             use Forest::Tree;
194              
195             my $t = Forest::Tree->new(
196             node => 1,
197             children => [
198             Forest::Tree->new(
199             node => 1.1,
200             children => [
201             Forest::Tree->new(node => 1.1.1),
202             Forest::Tree->new(node => 1.1.2),
203             Forest::Tree->new(node => 1.1.3),
204             ]
205             ),
206             Forest::Tree->new(node => 1.2),
207             Forest::Tree->new(
208             node => 1.3,
209             children => [
210             Forest::Tree->new(node => 1.3.1),
211             Forest::Tree->new(node => 1.3.2),
212             ]
213             ),
214             ]
215             );
216              
217             $t->traverse(sub {
218             my $t = shift;
219             print((' ' x $t->depth) . ($t->node || '\undef') . "\n");
220             });
221              
222             =head1 DESCRIPTION
223              
224             This module is a basic n-ary tree, it provides most of the functionality
225             of Tree::Simple, whatever is missing will be added eventually.
226              
227             This class inherits from L<Forest::Tree::Pure>>, but all shared methods and
228             attributes are documented in both classes.
229              
230             =head1 ATTRIBUTES
231              
232             =over 4
233              
234             =item I<node>
235              
236             =item I<uid>
237              
238             =item I<parent>
239              
240             =over 4
241              
242             =item B<parent>
243              
244             =item B<_set_parent>
245              
246             =item B<has_parent>
247              
248             =item B<clear_parent>
249              
250             =back
251              
252             =item I<children>
253              
254             =over 4
255              
256             =item B<get_child_at ($index)>
257              
258             Return the child at this position. (zero-base index)
259              
260             =item B<child_count>
261              
262             Returns the number of children this tree has
263              
264             =back
265              
266             =item I<size>
267              
268             =over 4
269              
270             =item B<size>
271              
272             =item B<has_size>
273              
274             =item B<clear_size>
275              
276             =back
277              
278             =item I<height>
279              
280             =over 4
281              
282             =item B<height>
283              
284             =item B<has_height>
285              
286             =item B<clear_height>
287              
288             =back
289              
290             =back
291              
292             =head1 METHODS
293              
294             =over 4
295              
296             =item B<is_root>
297              
298             True if the current tree has no parent
299              
300             =item B<is_leaf>
301              
302             True if the current tree has no children
303              
304             =item B<depth>
305              
306             Return the depth of this tree. Root has a depth of -1
307              
308             =item B<add_child ($child)>
309              
310             =item B<add_children (@children)>
311              
312             Add a new child. The $child must be a C<Forest::Tree>
313              
314             =item B<insert_child_at ($index, $child)>
315              
316             Insert a child at this position. (zero-base index)
317              
318             =item B<remove_child_at ($index)>
319              
320             Remove the child at this position. (zero-base index)
321              
322             =item B<traverse (\&func)>
323              
324             Takes a reference to a subroutine and traverses the tree applying this subroutine to
325             every descendant.
326              
327             =item B<siblings>
328              
329             Returns an array reference of all siblings (not including us)
330              
331             =item B<to_pure_tree>
332              
333             Invokes C<reconstruct_with_class> with L<Forest::Tree::Pure>.
334              
335             =item B<to_mutable_tree>
336              
337             Returns the invocant (without cloning).
338              
339             =item B<clone>
340              
341             See L<Forest::Tree::Pure/clone>.
342              
343             This variant will B<not> clone the parent, but return a clone of the subtree
344             that is detached.
345              
346             =item B<get_index_in_siblings>
347              
348             Returns the index of the tree in the list of children.
349              
350             Equivalent to calling C<$tree->parent->get_child_index($tree)>.
351              
352             Returns -1 if the node has no parent (the root node).
353              
354             =back
355              
356             =head1 BUGS
357              
358             All complex software has bugs lurking in it, and this module is no
359             exception. If you find a bug please either email me, or add the bug
360             to cpan-RT.
361              
362             =head1 AUTHOR
363              
364             Stevan Little E<lt>stevan.little@iinteractive.comE<gt>
365              
366             =head1 COPYRIGHT AND LICENSE
367              
368             Copyright 2008-2014 Infinity Interactive, Inc.
369              
370             L<http://www.iinteractive.com>
371              
372             This library is free software; you can redistribute it and/or modify
373             it under the same terms as Perl itself.
374              
375             =cut