File Coverage

blib/lib/Bio/Phylo/ListableRole.pm
Criterion Covered Total %
statement 123 182 67.5
branch 51 80 63.7
condition 33 42 78.5
subroutine 16 19 84.2
pod 12 12 100.0
total 235 335 70.1


line stmt bran cond sub pod time code
1             package Bio::Phylo::ListableRole;
2 51     51   284 use strict;
  51         92  
  51         1188  
3 51     51   218 use warnings;
  51         88  
  51         1035  
4 51     51   1266 use Bio::Phylo::Util::MOP;
  51         96  
  51         337  
5 51     51   234 use base 'Bio::Phylo::NeXML::Writable';
  51         87  
  51         17346  
6 51     51   342 use Scalar::Util qw'blessed';
  51         117  
  51         2596  
7 51     51   288 use Bio::Phylo::Util::Exceptions 'throw';
  51         96  
  51         1875  
8 51     51   260 use Bio::Phylo::Util::CONSTANT qw':all';
  51         97  
  51         85911  
9              
10             {
11             my $logger = __PACKAGE__->get_logger;
12             my ( $DATUM, $NODE, $MATRIX, $TREE ) =
13             ( _DATUM_, _NODE_, _MATRIX_, _TREE_ );
14              
15             =head1 NAME
16              
17             Bio::Phylo::ListableRole - Extra functionality for things that are lists
18              
19             =head1 SYNOPSIS
20              
21             No direct usage, parent class. Methods documented here
22             are available for all objects that inherit from it.
23              
24             =head1 DESCRIPTION
25              
26             A listable object is an object that contains multiple smaller objects of the
27             same type. For example: a tree contains nodes, so it's a listable object.
28              
29             This class contains methods that are useful for all listable objects: Matrices
30             (i.e. sets of matrix objects), individual Matrix objects, Datum objects (i.e.
31             character state sequences), Taxa, Forest, Tree and Node objects.
32              
33             =head1 METHODS
34              
35             =head2 ARRAY METHODS
36              
37             =over
38              
39             =item prune_entities()
40              
41             Prunes the container's contents specified by an array reference of indices.
42              
43             Type : Mutator
44             Title : prune_entities
45             Usage : $list->prune_entities([9,7,7,6]);
46             Function: Prunes a subset of contents
47             Returns : A Bio::Phylo::Listable object.
48             Args : An array reference of indices
49              
50             =cut
51              
52             sub prune_entities {
53 0     0 1 0 my ( $self, @indices ) = @_;
54 0         0 my %indices = map { $_ => 1 } @indices;
  0         0  
55 0         0 my $last_index = $self->last_index;
56 0         0 my @keep;
57 0         0 for my $i ( 0 .. $last_index ) {
58 0 0       0 push @keep, $i if not exists $indices{$i};
59             }
60 0         0 return $self->keep_entities( \@keep );
61             }
62              
63             =item get_index_of()
64              
65             Returns the index of the argument in the list,
66             or undef if the list doesn't contain the argument
67              
68             Type : Accessor
69             Title : get_index_of
70             Usage : my $i = $listable->get_index_of($obj)
71             Function: Returns the index of the argument in the list,
72             or undef if the list doesn't contain the argument
73             Returns : An index or undef
74             Args : A contained object
75              
76             =cut
77              
78             sub get_index_of {
79 68     68 1 110 my ( $self, $obj ) = @_;
80 68         107 my $id = $obj->get_id;
81 68         91 my $i = 0;
82 68         80 for my $ent ( @{ $self->get_entities } ) {
  68         115  
83 148 100       229 return $i if $ent->get_id == $id;
84 80         100 $i++;
85             }
86 0         0 return;
87             }
88              
89             =item get_by_index()
90              
91             Gets element at index from container.
92              
93             Type : Accessor
94             Title : get_by_index
95             Usage : my $contained_obj = $obj->get_by_index($i);
96             Function: Retrieves the i'th entity
97             from a listable object.
98             Returns : An entity stored by a listable
99             object (or array ref for slices).
100             Args : An index or range. This works
101             the way you dereference any perl
102             array including through slices,
103             i.e. $obj->get_by_index(0 .. 10)>
104             $obj->get_by_index(0, -1)
105             and so on.
106             Comments: Throws if out-of-bounds
107              
108             =cut
109              
110             sub get_by_index {
111 123     123 1 4190 my $self = shift;
112 123         233 my $entities = $self->get_entities;
113 123         222 my @range = @_;
114 123 50       233 if ( scalar @range > 1 ) {
115 0         0 my @returnvalue;
116 0         0 eval { @returnvalue = @{$entities}[@range] };
  0         0  
  0         0  
117 0 0       0 if ($@) {
118 0         0 throw 'OutOfBounds' => 'index out of bounds';
119             }
120 0         0 return \@returnvalue;
121             }
122             else {
123 123         147 my $returnvalue;
124 123         150 eval { $returnvalue = $entities->[ $range[0] ] };
  123         195  
125 123 50       225 if ($@) {
126 0         0 throw 'OutOfBounds' => 'index out of bounds';
127             }
128 123         342 return $returnvalue;
129             }
130             }
131              
132             =item get_by_regular_expression()
133              
134             Gets elements that match regular expression from container.
135              
136             Type : Accessor
137             Title : get_by_regular_expression
138             Usage : my @objects = @{
139             $obj->get_by_regular_expression(
140             -value => $method,
141             -match => $re
142             ) };
143             Function: Retrieves the data in the
144             current Bio::Phylo::Listable
145             object whose $method output
146             matches $re
147             Returns : A list of Bio::Phylo::* objects.
148             Args : -value => any of the string
149             datum props (e.g. 'get_type')
150             -match => a compiled regular
151             expression (e.g. qr/^[D|R]NA$/)
152              
153             =cut
154              
155             sub get_by_regular_expression {
156 17     17 1 2152 my $self = shift;
157 17         70 my %o = looks_like_hash @_;
158 17         33 my @matches;
159 17         29 for my $e ( @{ $self->get_entities } ) {
  17         44  
160 175 100 66     447 if ( $o{-match} && looks_like_instance( $o{-match}, 'Regexp' ) ) {
161 174 100 100     369 if ( $e->get( $o{-value} )
162             && $e->get( $o{-value} ) =~ $o{-match} )
163             {
164 13         37 push @matches, $e;
165             }
166             }
167             else {
168 1         3 throw 'BadArgs' => 'need a regular expression to evaluate';
169             }
170             }
171 15         67 return \@matches;
172             }
173              
174             =item get_by_value()
175              
176             Gets elements that meet numerical rule from container.
177              
178             Type : Accessor
179             Title : get_by_value
180             Usage : my @objects = @{ $obj->get_by_value(
181             -value => $method,
182             -ge => $number
183             ) };
184             Function: Iterates through all objects
185             contained by $obj and returns
186             those for which the output of
187             $method (e.g. get_tree_length)
188             is less than (-lt), less than
189             or equal to (-le), equal to
190             (-eq), greater than or equal to
191             (-ge), or greater than (-gt) $number.
192             Returns : A reference to an array of objects
193             Args : -value => any of the numerical
194             obj data (e.g. tree length)
195             -lt => less than
196             -le => less than or equals
197             -eq => equals
198             -ge => greater than or equals
199             -gt => greater than
200              
201             =cut
202              
203             sub get_by_value {
204 28     28 1 63 my $self = shift;
205 28         77 my %o = looks_like_hash @_;
206 28         56 my @results;
207 28         41 for my $e ( @{ $self->get_entities } ) {
  28         73  
208 116 100       274 if ( $o{-eq} ) {
209 22 100 100     78 if ( $e->get( $o{-value} )
210             && $e->get( $o{-value} ) == $o{-eq} )
211             {
212 3         12 push @results, $e;
213             }
214             }
215 115 100       259 if ( $o{-le} ) {
216 22 100 100     82 if ( $e->get( $o{-value} )
217             && $e->get( $o{-value} ) <= $o{-le} )
218             {
219 5         17 push @results, $e;
220             }
221             }
222 114 100       272 if ( $o{-lt} ) {
223 24 100 100     100 if ( $e->get( $o{-value} )
224             && $e->get( $o{-value} ) < $o{-lt} )
225             {
226 6         18 push @results, $e;
227             }
228             }
229 113 100       274 if ( $o{-ge} ) {
230 24 100 100     136 if ( $e->get( $o{-value} )
231             && $e->get( $o{-value} ) >= $o{-ge} )
232             {
233 18         65 push @results, $e;
234             }
235             }
236 112 100       314 if ( $o{-gt} ) {
237 24 100 100     60 if ( $e->get( $o{-value} )
238             && $e->get( $o{-value} ) > $o{-gt} )
239             {
240 17         45 push @results, $e;
241             }
242             }
243             }
244 23         123 return \@results;
245             }
246              
247             =item get_by_name()
248              
249             Gets first element that has argument name
250              
251             Type : Accessor
252             Title : get_by_name
253             Usage : my $found = $obj->get_by_name('foo');
254             Function: Retrieves the first contained object
255             in the current Bio::Phylo::Listable
256             object whose name is 'foo'
257             Returns : A Bio::Phylo::* object.
258             Args : A name (string)
259              
260             =cut
261              
262             sub get_by_name {
263 173     173 1 3388 my ( $self, $name ) = @_;
264 173 50 33     649 if ( not defined $name or ref $name ) {
265 0         0 throw 'BadString' => "Can't search on name '$name'";
266             }
267 173         240 for my $obj ( @{ $self->get_entities } ) {
  173         407  
268 608         1215 my $obj_name = $obj->get_name;
269 608 100 100     1718 if ( $obj_name and $name eq $obj_name ) {
270 173         551 return $obj;
271             }
272             }
273 0         0 return;
274             }
275              
276             =back
277              
278             =head2 VISITOR METHODS
279              
280             =over
281              
282             =item visit()
283              
284             Iterates over objects contained by container, executes argument
285             code reference on each.
286              
287             Type : Visitor predicate
288             Title : visit
289             Usage : $obj->visit(
290             sub{ print $_[0]->get_name, "\n" }
291             );
292             Function: Implements visitor pattern
293             using code reference.
294             Returns : The container, possibly modified.
295             Args : a CODE reference.
296              
297             =cut
298              
299             sub visit {
300 193     193 1 539 my ( $self, $code ) = @_;
301 193 50       484 if ( looks_like_instance( $code, 'CODE' ) ) {
302 193         439 for ( @{ $self->get_entities } ) {
  193         514  
303 1109         3186 $code->($_);
304             }
305             }
306             else {
307 0         0 throw 'BadArgs' => "\"$code\" is not a CODE reference!";
308             }
309 193         686 return $self;
310             }
311              
312             =back
313              
314             =head2 TESTS
315              
316             =over
317              
318             =item contains()
319              
320             Tests whether the container object contains the argument object.
321              
322             Type : Test
323             Title : contains
324             Usage : if ( $obj->contains( $other_obj ) ) {
325             # do something
326             }
327             Function: Tests whether the container object
328             contains the argument object
329             Returns : BOOLEAN
330             Args : A Bio::Phylo::* object
331              
332             =cut
333              
334             sub contains {
335 20064     20064 1 30186 my ( $self, $obj ) = @_;
336 20064 50       45052 if ( blessed $obj ) {
337 20064         34211 my $id = $obj->get_id;
338 20064         27175 for my $ent ( @{ $self->get_entities } ) {
  20064         37689  
339 1132085 50       1725086 next if not $ent;
340 1132085 100       1694169 return 1 if $ent->get_id == $id;
341             }
342 10         51 return 0;
343             }
344             else {
345 0         0 for my $ent ( @{ $self->get_entities } ) {
  0         0  
346 0 0       0 next if not $ent;
347 0 0       0 return 1 if $ent eq $obj;
348             }
349             }
350             }
351              
352             =item can_contain()
353              
354             Tests if argument can be inserted in container.
355              
356             Type : Test
357             Title : can_contain
358             Usage : &do_something if $listable->can_contain( $obj );
359             Function: Tests if $obj can be inserted in $listable
360             Returns : BOOL
361             Args : An $obj to test
362              
363             =cut
364              
365             sub can_contain {
366 20630     20630 1 34786 my ( $self, @obj ) = @_;
367 20630         30181 for my $obj (@obj) {
368 20935         25910 my ( $self_type, $obj_container );
369 20935         26267 eval {
370 20935         43320 $self_type = $self->_type;
371 20935         42676 $obj_container = $obj->_container;
372             };
373 20935 100 100     67182 if ( $@ or $self_type != $obj_container ) {
374 5 100       20 if ( not $@ ) {
375 1         8 $logger->info(" $self $self_type != $obj $obj_container");
376             }
377             else {
378 4         22 $logger->info($@);
379             }
380 5         50 return 0;
381             }
382             }
383 20625         50580 return 1;
384             }
385              
386             =back
387              
388             =head2 UTILITY METHODS
389              
390             =over
391              
392             =item cross_reference()
393              
394             The cross_reference method links node and datum objects to the taxa they apply
395             to. After crossreferencing a matrix with a taxa object, every datum object has
396             a reference to a taxon object stored in its C<$datum-E<gt>get_taxon> field, and
397             every taxon object has a list of references to datum objects stored in its
398             C<$taxon-E<gt>get_data> field.
399              
400             Type : Generic method
401             Title : cross_reference
402             Usage : $obj->cross_reference($taxa);
403             Function: Crossreferences the entities
404             in the container with names
405             in $taxa
406             Returns : string
407             Args : A Bio::Phylo::Taxa object
408             Comments:
409              
410             =cut
411              
412             sub cross_reference {
413 5     5 1 32 my ( $self, $taxa ) = @_;
414 5         15 my ( $selfref, $taxref ) = ( ref $self, ref $taxa );
415 5 100       23 if ( looks_like_implementor( $taxa, 'get_entities' ) ) {
416 4         16 my $ents = $self->get_entities;
417 4 100 66     19 if ( $ents && @{$ents} ) {
  4         16  
418 3         6 foreach ( @{$ents} ) {
  3         8  
419 4 100 66     12 if ( looks_like_implementor( $_, 'get_name' )
420             && looks_like_implementor( $_, 'set_taxon' ) )
421             {
422 2         4 my $tax = $taxa->get_entities;
423 2 50 33     6 if ( $tax && @{$tax} ) {
  2         5  
424 2         3 foreach my $taxon ( @{$tax} ) {
  2         4  
425 4 50 33     9 if ( not $taxon->get_name or not $_->get_name )
426             {
427 4         10 next;
428             }
429 0 0       0 if ( $taxon->get_name eq $_->get_name ) {
430 0         0 $_->set_taxon($taxon);
431 0 0       0 if ( $_->_type == $DATUM ) {
432 0         0 $taxon->set_data($_);
433             }
434 0 0       0 if ( $_->_type == $NODE ) {
435 0         0 $taxon->set_nodes($_);
436             }
437             }
438             }
439             }
440             }
441             else {
442 2         11 throw 'ObjectMismatch' =>
443             "$selfref can't link to $taxref";
444             }
445             }
446             }
447 2 100       10 if ( $self->_type == $TREE ) {
    50          
448 1         14 $self->_get_container->set_taxa($taxa);
449             }
450             elsif ( $self->_type == $MATRIX ) {
451 1         13 $self->set_taxa($taxa);
452             }
453 2         12 return $self;
454             }
455             else {
456 1         7 throw 'ObjectMismatch' => "$taxref does not contain taxa";
457             }
458             }
459              
460             =item alphabetize()
461              
462             Sorts the contents alphabetically by their name.
463              
464             Type : Generic method
465             Title : alphabetize
466             Usage : $obj->alphabetize;
467             Function: Sorts the contents alphabetically by their name.
468             Returns : $self
469             Args : None
470             Comments:
471              
472             =cut
473            
474             sub alphabetize {
475 0     0 1   my $self = shift;
476 0           my @sorted = map { $_->[0] }
477 0           sort { $_->[1] cmp $_->[1] }
478 0           map { [ $_, $_->get_internal_name ] }
479 0           @{ $self->get_entities };
  0            
480 0           $self->clear;
481 0           $self->insert($_) for @sorted;
482 0           return $self;
483             }
484            
485             =back
486              
487             =head2 SETS MANAGEMENT
488              
489             Many Bio::Phylo objects are segmented, i.e. they contain one or more subparts
490             of the same type. For example, a matrix contains multiple rows; each row
491             contains multiple cells; a tree contains nodes, and so on. (Segmented objects
492             all inherit from Bio::Phylo::Listable, i.e. the class whose documentation you're
493             reading here.) In many cases it is useful to be able to define subsets of the
494             contents of segmented objects, for example sets of taxon objects inside a taxa
495             block. The Bio::Phylo::Listable object allows this through a number of methods
496             (add_set, remove_set, add_to_set, remove_from_set etc.). Those methods delegate
497             the actual management of the set contents to the L<Bio::Phylo::Set> object.
498             Consult the documentation for L<Bio::Phylo::Set> for a code sample.
499              
500             =over
501              
502             =item sets_to_xml()
503              
504             Returns string representation of sets
505              
506             Type : Accessor
507             Title : sets_to_xml
508             Usage : my $str = $obj->sets_to_xml;
509             Function: Gets xml string
510             Returns : Scalar
511             Args : None
512              
513             =cut
514              
515             sub sets_to_xml {
516 0     0 1   my $self = shift;
517 0           my $xml = '';
518 0 0         if ( $self->can('get_sets') ) {
519 0           for my $set ( @{ $self->get_sets } ) {
  0            
520 0           my %contents;
521 0           for my $ent ( @{ $self->get_entities } ) {
  0            
522 0 0         if ( $self->is_in_set($ent,$set) ) {
523 0           my $tag = $ent->get_tag;
524 0 0         $contents{$tag} = [] if not $contents{$tag};
525 0           push @{ $contents{$tag} }, $ent->get_xml_id;
  0            
526             }
527             }
528 0           for my $key ( keys %contents ) {
529 0           my @ids = @{ $contents{$key} };
  0            
530 0           $contents{$key} = join ' ', @ids;
531             }
532 0           $set->set_attributes(%contents);
533 0           $xml .= "\n" . $set->to_xml;
534             }
535             }
536 0           return $xml;
537             }
538              
539             =back
540              
541             =cut
542              
543             # podinherit_insert_token
544              
545             =head1 SEE ALSO
546              
547             There is a mailing list at L<https://groups.google.com/forum/#!forum/bio-phylo>
548             for any user or developer questions and discussions.
549              
550             Also see the manual: L<Bio::Phylo::Manual> and L<http://rutgervos.blogspot.com>.
551              
552             =head2 Objects inheriting from Bio::Phylo::Listable
553              
554             =over
555              
556             =item L<Bio::Phylo::Forest>
557              
558             Iterate over a set of trees.
559              
560             =item L<Bio::Phylo::Forest::Tree>
561              
562             Iterate over nodes in a tree.
563              
564             =item L<Bio::Phylo::Forest::Node>
565              
566             Iterate of children of a node.
567              
568             =item L<Bio::Phylo::Matrices>
569              
570             Iterate over a set of matrices.
571              
572             =item L<Bio::Phylo::Matrices::Matrix>
573              
574             Iterate over the datum objects in a matrix.
575              
576             =item L<Bio::Phylo::Matrices::Datum>
577              
578             Iterate over the characters in a datum.
579              
580             =item L<Bio::Phylo::Taxa>
581              
582             Iterate over a set of taxa.
583              
584             =back
585              
586             =head2 Superclasses
587              
588             =over
589              
590             =item L<Bio::Phylo::NeXML::Writable>
591              
592             This object inherits from L<Bio::Phylo::NeXML::Writable>, so methods
593             defined there are also applicable here.
594              
595             =back
596              
597             =head1 CITATION
598              
599             If you use Bio::Phylo in published research, please cite it:
600              
601             B<Rutger A Vos>, B<Jason Caravas>, B<Klaas Hartmann>, B<Mark A Jensen>
602             and B<Chase Miller>, 2011. Bio::Phylo - phyloinformatic analysis using Perl.
603             I<BMC Bioinformatics> B<12>:63.
604             L<http://dx.doi.org/10.1186/1471-2105-12-63>
605              
606             =cut
607              
608             }
609             1;