File Coverage

blib/lib/Bio/Phylo/Forest/Node.pm
Criterion Covered Total %
statement 93 109 85.3
branch 22 32 68.7
condition 16 24 66.6
subroutine 25 26 96.1
pod 12 12 100.0
total 168 203 82.7


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