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   47264 use strict;
  33         71  
  33         913  
3 33     33   9484 use Bio::Phylo::Forest::DrawNodeRole;
  33         94  
  33         297  
4 33     33   213 use base qw'Bio::Phylo::Forest::DrawNodeRole';
  33         69  
  33         3067  
5 33     33   214 use Bio::Phylo::Util::CONSTANT qw':objecttypes /looks_like/';
  33         71  
  33         8665  
6 33     33   223 use Bio::Phylo::Util::Exceptions 'throw';
  33         67  
  33         1434  
7 33     33   209 use Scalar::Util 'weaken';
  33         80  
  33         9574  
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 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 805     805 1 1245 my ( $self, $parent ) = @_;
107 805 100 100     2260 if ( $parent and looks_like_object $parent, $TYPE_CONSTANT ) {
    50          
108 677         1283 $parent->set_child($self);
109             }
110             elsif ( not $parent ) {
111 127         271 $self->set_raw_parent;
112             }
113 804         1635 return $self;
114 33     33   221 }
  33         68  
  33         174  
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 8470     8470 1 15828 $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 9077     9077 1 14855 my ( $self, $child, $i ) = @_;
153              
154             # bad args?
155 9077 100 66     23235 if ( not $child or not looks_like_object $child, $TYPE_CONSTANT ) {
156 460         710 return;
157             }
158              
159             # maybe nothing to do?
160 8615 100 33     23989 if ( not $child
      66        
161             or $child->get_id == $self->get_id
162             or $child->is_child_of($self) )
163             {
164 312         533 return $self;
165             }
166              
167             # $child_parent is NEVER $self, see above
168 8303         15443 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 8303 100       16864 if ( $child->is_ancestor_of($self) ) {
182              
183             # step 1.
184 1         4 my $parent_parent = $self->get_parent;
185 1         12 $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       5 if ($child_parent) {
190 0         0 $child_parent->set_raw_child( $self );
191             }
192             }
193              
194             # step 3.
195 8303 100       15390 if ($child_parent) {
196 241         616 $child_parent->prune_child($child);
197             }
198 8303         19645 $child->set_raw_parent( $self );
199              
200             # now do the insert, first make room by shifting later siblings right
201 8303         14703 my $children = $self->get_children;
202 8303 50       15801 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 8303         9669 $i = scalar @{$children};
  8303         11697  
212             }
213              
214             # step 4.
215 8303         19739 $self->set_raw_child( $child, $i );
216 8303         16212 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 8342     8342 1 14314 $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 50925     50925 1 75373 my ( $self, $bl ) = @_;
256 50925         77255 my $id = $self->get_id;
257 50925 100 100     109480 if ( defined $bl && looks_like_number $bl && !ref $bl ) {
    100 66        
    50 33        
      66        
258 50914         72912 $branch_length{$id} = $bl;
259 50914 50       82095 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         5 throw 'BadNumber' => "Branch length \"$bl\" is a bad number";
265             }
266             elsif ( !defined $bl ) {
267 10         25 $branch_length{$id} = undef;
268             }
269 50924         100101 return $self;
270 33     33   18461 }
  33         111  
  33         132  
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 8554     8554 1 12792 my ( $self, $tree ) = @_;
290 8554         15113 my $id = $self->get_id;
291 8554 100       15649 if ($tree) {
292 8286 50       18091 if ( looks_like_object $tree, $CONTAINER_CONSTANT ) {
293 8286         15040 $tree{$id} = $tree;
294 8286         19136 weaken $tree{$id};
295             }
296             else {
297 0         0 throw 'ObjectMismatch' => "$tree is not a tree";
298             }
299             }
300             else {
301 268         652 $tree{$id} = undef;
302             }
303 8554         24010 return $self;
304 33     33   9489 }
  33         76  
  33         135  
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 4 my ( $self, $rank ) = @_;
323 2         7 $rank{$self->get_id} = $rank;
324 2         4 return $self;
325 33     33   7704 }
  33         75  
  33         122  
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 714998     714998 1 902298 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 55974     55974 1 79222 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 154835     154835 1 213342 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 127     127 1 182 my $self = shift;
393 127         234 my $id = $self->get_id;
394 127         405 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 5 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 8250     8250   10766 my $self = shift;
450 8250         13152 my $id = $self->get_id;
451 8250         12466 for my $field (@fields) {
452 33000         58092 delete $field->{$id};
453             }
454 33     33   13275 }
  33         71  
  33         136  
455            
456             }
457              
458             =back
459              
460             # podinherit_insert_token
461              
462             =head1 SEE ALSO
463              
464             There is a mailing list at L
465             for any user or developer questions and discussions.
466              
467             =over
468              
469             =item L
470              
471             This object inherits from L, so methods
472             defined there are also applicable here.
473              
474             =item L
475              
476             Also see the manual: L and L.
477              
478             =back
479              
480             =head1 CITATION
481              
482             If you use Bio::Phylo in published research, please cite it:
483              
484             B, B, B, B
485             and B, 2011. Bio::Phylo - phyloinformatic analysis using Perl.
486             I B<12>:63.
487             L
488              
489             =cut
490              
491             1;