File Coverage

blib/lib/Bio/Phylo/Forest/Node.pm
Criterion Covered Total %
statement 90 106 84.9
branch 22 32 68.7
condition 16 24 66.6
subroutine 24 25 96.0
pod 12 12 100.0
total 164 199 82.4


line stmt bran cond sub pod time code
1             package Bio::Phylo::Forest::Node;
2 33     33   64458 use strict;
  33         71  
  33         835  
3 33     33   9349 use Bio::Phylo::Forest::DrawNodeRole;
  33         86  
  33         267  
4 33     33   186 use base qw'Bio::Phylo::Forest::DrawNodeRole';
  33         58  
  33         2746  
5 33     33   203 use Bio::Phylo::Util::CONSTANT qw':objecttypes /looks_like/';
  33         69  
  33         7953  
6 33     33   222 use Bio::Phylo::Util::Exceptions 'throw';
  33         60  
  33         1348  
7 33     33   176 use Scalar::Util 'weaken';
  33         65  
  33         8758  
8              
9             # store type constant
10             my ( $TYPE_CONSTANT, $CONTAINER_CONSTANT ) = ( _NODE_, _TREE_ );
11              
12             {
13              
14             # @fields array necessary for object destruction
15             my @fields = \( my ( %branch_length, %parent, %tree, %rank ) );
16              
17             =head1 NAME
18              
19             Bio::Phylo::Forest::Node - Node in a phylogenetic tree
20              
21             =head1 SYNOPSIS
22              
23             # some way to get nodes:
24             use Bio::Phylo::IO;
25             my $string = '((A,B),C);';
26             my $forest = Bio::Phylo::IO->parse(
27             -format => 'newick',
28             -string => $string
29             );
30              
31             # prints 'Bio::Phylo::Forest'
32             print ref $forest;
33              
34             foreach my $tree ( @{ $forest->get_entities } ) {
35              
36             # prints 'Bio::Phylo::Forest::Tree'
37             print ref $tree;
38              
39             foreach my $node ( @{ $tree->get_entities } ) {
40              
41             # prints 'Bio::Phylo::Forest::Node'
42             print ref $node;
43              
44             # node has a parent, i.e. is not root
45             if ( $node->get_parent ) {
46             $node->set_branch_length(1);
47             }
48              
49             # node is root
50             else {
51             $node->set_branch_length(0);
52             }
53             }
54             }
55              
56             =head1 DESCRIPTION
57              
58             This module has the getters and setters that alter the state of a
59             node object. Useful behaviours (which are also available) are defined
60             in the L<Bio::Phylo::Forest::NodeRole> package.
61              
62             =head1 METHODS
63              
64             =cut
65              
66             my $set_raw_parent = sub {
67             my ( $self, $parent ) = @_;
68             my $id = $self->get_id;
69             $parent{$id} = $parent; # XXX here we modify parent
70             weaken $parent{$id} if $parent;
71             };
72             my $get_parent = sub {
73             my $self = shift;
74             return $parent{ $self->get_id };
75             };
76             my $get_children = sub { shift->get_entities };
77             my $get_branch_length = sub {
78             my $self = shift;
79             return $branch_length{ $self->get_id };
80             };
81             my $set_raw_child = sub {
82             my ( $self, $child, $i ) = @_;
83             $i = $self->last_index + 1 if not defined $i or $i == -1;
84             $self->insert_at_index( $child, $i ); # XXX here we modify children
85             };
86              
87             =over
88              
89             =item set_parent()
90              
91             Sets argument as invocant's parent.
92              
93             Type : Mutator
94             Title : set_parent
95             Usage : $node->set_parent($parent);
96             Function: Assigns a node's parent.
97             Returns : Modified object.
98             Args : If no argument is given, the current
99             parent is set to undefined. A valid
100             argument is Bio::Phylo::Forest::Node
101             object.
102              
103             =cut
104              
105             sub set_parent : Clonable {
106 811     811 1 1234 my ( $self, $parent ) = @_;
107 811 100 100     2223 if ( $parent and looks_like_object $parent, $TYPE_CONSTANT ) {
    50          
108 681         1167 $parent->set_child($self);
109             }
110             elsif ( not $parent ) {
111 129         274 $self->set_raw_parent;
112             }
113 810         1538 return $self;
114 33     33   210 }
  33         66  
  33         163  
115              
116             =item set_raw_parent()
117              
118             Sets argument as invocant's parent. This method does NO
119             sanity checks on the rest of the topology. Use with caution.
120              
121             Type : Mutator
122             Title : set_raw_parent
123             Usage : $node->set_raw_parent($parent);
124             Function: Assigns a node's parent.
125             Returns : Modified object.
126             Args : If no argument is given, the current
127             parent is set to undefined. A valid
128             argument is Bio::Phylo::Forest::Node
129             object.
130              
131             =cut
132              
133             sub set_raw_parent {
134 10966     10966 1 19817 $set_raw_parent->(@_)
135             }
136              
137             =item set_child()
138              
139             Sets argument as invocant's child.
140              
141             Type : Mutator
142             Title : set_child
143             Usage : $node->set_child($child);
144             Function: Assigns a new child to $node
145             Returns : Modified object.
146             Args : A valid argument consists of a
147             Bio::Phylo::Forest::Node object.
148              
149             =cut
150              
151             sub set_child {
152 11571     11571 1 18921 my ( $self, $child, $i ) = @_;
153              
154             # bad args?
155 11571 100 66     28877 if ( not $child or not looks_like_object $child, $TYPE_CONSTANT ) {
156 460         631 return;
157             }
158              
159             # maybe nothing to do?
160 11109 100 33     28538 if ( not $child
      66        
161             or $child->get_id == $self->get_id
162             or $child->is_child_of($self) )
163             {
164 312         482 return $self;
165             }
166              
167             # $child_parent is NEVER $self, see above
168 10797         19307 my $child_parent = $child->get_parent;
169              
170             # child is ancestor: this is obviously problematic, because
171             # now we're trying to set a node nearer to the root on the
172             # same lineage as the CHILD of a descendant. Because they're
173             # on the same lineage it's hard to see how this can be done
174             # sensibly. The decision here is to do:
175             # 1. we prune what is to become the parent (now the descendant)
176             # from its current parent
177             # 2. we set this pruned node (and its descendants) as a sibling
178             # of what is to become the child
179             # 3. we prune what is to become the child from its parent
180             # 4. we set that pruned child as the child of $self
181 10797 100       21444 if ( $child->is_ancestor_of($self) ) {
182              
183             # step 1.
184 1         3 my $parent_parent = $self->get_parent;
185 1         7 $parent_parent->prune_child($self);
186              
187             # step 2.
188 1         3 $self->set_raw_parent( $child_parent ); # XXX could be undef
189 1 50       3 if ($child_parent) {
190 0         0 $child_parent->set_raw_child( $self );
191             }
192             }
193              
194             # step 3.
195 10797 100       17352 if ($child_parent) {
196 245         540 $child_parent->prune_child($child);
197             }
198 10797         24359 $child->set_raw_parent( $self );
199              
200             # now do the insert, first make room by shifting later siblings right
201 10797         21004 my $children = $self->get_children;
202 10797 50       18983 if ( defined $i ) {
203 0         0 for ( my $j = $#{$children} ; $j >= 0 ; $j-- ) {
  0         0  
204 0         0 my $sibling = $children->[$j];
205 0         0 $self->set_raw_child( $sibling, $j + 1 );
206             }
207             }
208              
209             # no index was supplied, child becomes last daughter
210             else {
211 10797         12722 $i = scalar @{$children};
  10797         15118  
212             }
213              
214             # step 4.
215 10797         25375 $self->set_raw_child( $child, $i );
216 10797         21413 return $self;
217             }
218              
219             =item set_raw_child()
220              
221             Sets argument as invocant's child. This method does NO
222             sanity checks on the rest of the topology. Use with caution.
223              
224             Type : Mutator
225             Title : set_raw_child
226             Usage : $node->set_raw_child($child);
227             Function: Assigns a new child to $node
228             Returns : Modified object.
229             Args : A valid argument consists of a
230             Bio::Phylo::Forest::Node object.
231              
232             =cut
233              
234             sub set_raw_child {
235 10836     10836 1 18003 $set_raw_child->(@_);
236             }
237            
238             =item set_branch_length()
239              
240             Sets argument as invocant's branch length.
241              
242             Type : Mutator
243             Title : set_branch_length
244             Usage : $node->set_branch_length(0.423e+2);
245             Function: Assigns a node's branch length.
246             Returns : Modified object.
247             Args : If no argument is given, the
248             current branch length is set
249             to undefined. A valid argument
250             is a number in any of Perl's formats.
251              
252             =cut
253              
254             sub set_branch_length : Clonable {
255 80679     80679 1 119602 my ( $self, $bl ) = @_;
256 80679         117892 my $id = $self->get_id;
257 80679 100 100     173027 if ( defined $bl && looks_like_number $bl && !ref $bl ) {
    100 66        
    50 33        
      66        
258 80668         114674 $branch_length{$id} = $bl;
259 80668 50       128143 if ( $bl < 0 ) {
260 0         0 $self->get_logger->warn("Setting length < 0: $bl");
261             }
262             }
263             elsif ( defined $bl && ( !looks_like_number $bl || ref $bl ) ) {
264 1         8 throw 'BadNumber' => "Branch length \"$bl\" is a bad number";
265             }
266             elsif ( !defined $bl ) {
267 10         22 $branch_length{$id} = undef;
268             }
269 80678         154792 return $self;
270 33     33   17285 }
  33         70  
  33         117  
271              
272             =item set_tree()
273              
274             Sets what tree invocant belongs to
275              
276             Type : Mutator
277             Title : set_tree
278             Usage : $node->set_tree($tree);
279             Function: Sets what tree invocant belongs to
280             Returns : Invocant
281             Args : Bio::Phylo::Forest::Tree
282             Comments: This method is called automatically
283             when inserting or deleting nodes in
284             trees.
285              
286             =cut
287              
288             sub set_tree : Clonable {
289 11093     11093 1 15974 my ( $self, $tree ) = @_;
290 11093         19753 my $id = $self->get_id;
291 11093 100       18555 if ($tree) {
292 10821 50       24339 if ( looks_like_object $tree, $CONTAINER_CONSTANT ) {
293 10821         19524 $tree{$id} = $tree;
294 10821         23255 weaken $tree{$id};
295             }
296             else {
297 0         0 throw 'ObjectMismatch' => "$tree is not a tree";
298             }
299             }
300             else {
301 272         621 $tree{$id} = undef;
302             }
303 11093         30084 return $self;
304 33     33   8699 }
  33         64  
  33         114  
305              
306             =item set_rank()
307              
308             Sets the taxonomic rank of the node
309              
310             Type : Mutator
311             Title : set_rank
312             Usage : $node->set_rank('genus');
313             Function: Sets the taxonomic rank of the node
314             Returns : Invocant
315             Args : String
316             Comments: Free-form, but highly recommended to use same rank names as in Bio::Taxon
317              
318             =cut
319              
320            
321             sub set_rank : Clonable {
322 2     2 1 8 my ( $self, $rank ) = @_;
323 2         8 $rank{$self->get_id} = $rank;
324 2         7 return $self;
325 33     33   7168 }
  33         65  
  33         124  
326              
327             =item get_parent()
328              
329             Gets invocant's parent.
330              
331             Type : Accessor
332             Title : get_parent
333             Usage : my $parent = $node->get_parent;
334             Function: Retrieves a node's parent.
335             Returns : Bio::Phylo::Forest::Node
336             Args : NONE
337              
338             =cut
339              
340 445295     445295 1 558273 sub get_parent { return $get_parent->(shift) }
341              
342             =item get_branch_length()
343              
344             Gets invocant's branch length.
345              
346             Type : Accessor
347             Title : get_branch_length
348             Usage : my $branch_length = $node->get_branch_length;
349             Function: Retrieves a node's branch length.
350             Returns : FLOAT
351             Args : NONE
352             Comments: Test for "defined($node->get_branch_length)"
353             for zero-length (but defined) branches. Testing
354             "if ( $node->get_branch_length ) { ... }"
355             yields false for zero-but-defined branches!
356              
357             =cut
358              
359 84967     84967 1 118266 sub get_branch_length { return $get_branch_length->(shift) }
360              
361             =item get_children()
362              
363             Gets invocant's immediate children.
364              
365             Type : Query
366             Title : get_children
367             Usage : my @children = @{ $node->get_children };
368             Function: Returns an array reference of immediate
369             descendants, ordered from left to right.
370             Returns : Array reference of
371             Bio::Phylo::Forest::Node objects.
372             Args : NONE
373              
374             =cut
375              
376 113803     113803 1 155939 sub get_children { return $get_children->(shift) }
377            
378             =item get_tree()
379              
380             Returns the tree invocant belongs to
381              
382             Type : Query
383             Title : get_tree
384             Usage : my $tree = $node->get_tree;
385             Function: Returns the tree $node belongs to
386             Returns : Bio::Phylo::Forest::Tree
387             Args : NONE
388              
389             =cut
390              
391             sub get_tree {
392 129     129 1 186 my $self = shift;
393 129         220 my $id = $self->get_id;
394 129         393 return $tree{$id};
395             }
396              
397             =item get_rank()
398              
399             Gets the taxonomic rank of the node
400              
401             Type : Mutator
402             Title : get_rank
403             Usage : my $rank = $node->get_rank;
404             Function: Gets the taxonomic rank of the node
405             Returns : String
406             Args : NONE
407             Comments:
408              
409             =cut
410            
411 2     2 1 8 sub get_rank { $rank{shift->get_id} }
412              
413             =begin comment
414              
415             Type : Internal method
416             Title : _json_data
417             Usage : $node->_json_data;
418             Function: Populates a data structure to be serialized as JSON
419             Returns :
420             Args :
421              
422             =end comment
423              
424             =cut
425            
426             sub _json_data {
427 0     0     my $self = shift;
428 0           my %result = %{ $self->SUPER::_json_data };
  0            
429 0 0         $result{'length'} = $self->get_branch_length if defined $self->get_branch_length;
430 0 0         $result{'rank'} = $self->get_rank if $self->get_rank;
431 0           $result{'children'} = [ map { $_->_json_data } @{ $self->get_children } ];
  0            
  0            
432 0           return \%result;
433             }
434              
435             =begin comment
436              
437             Type : Internal method
438             Title : _cleanup
439             Usage : $trees->_cleanup;
440             Function: Called during object destruction, for cleanup of instance data
441             Returns :
442             Args :
443              
444             =end comment
445              
446             =cut
447              
448             sub _cleanup : Destructor {
449 10785     10785   12907 my $self = shift;
450 10785         17024 my $id = $self->get_id;
451 10785         16384 for my $field (@fields) {
452 43140         72910 delete $field->{$id};
453             }
454 33     33   12449 }
  33         65  
  33         120  
455            
456             }
457              
458             =back
459              
460             # podinherit_insert_token
461              
462             =head1 SEE ALSO
463              
464             There is a mailing list at L<https://groups.google.com/forum/#!forum/bio-phylo>
465             for any user or developer questions and discussions.
466              
467             =over
468              
469             =item L<Bio::Phylo::Forest::NodeRole>
470              
471             This object inherits from L<Bio::Phylo::Forest::NodeRole>, so methods
472             defined there are also applicable here.
473              
474             =item L<Bio::Phylo::Manual>
475              
476             Also see the manual: L<Bio::Phylo::Manual> and L<http://rutgervos.blogspot.com>.
477              
478             =back
479              
480             =head1 CITATION
481              
482             If you use Bio::Phylo in published research, please cite it:
483              
484             B<Rutger A Vos>, B<Jason Caravas>, B<Klaas Hartmann>, B<Mark A Jensen>
485             and B<Chase Miller>, 2011. Bio::Phylo - phyloinformatic analysis using Perl.
486             I<BMC Bioinformatics> B<12>:63.
487             L<http://dx.doi.org/10.1186/1471-2105-12-63>
488              
489             =cut
490              
491             1;