File Coverage

blib/lib/Bio/Phylo/Project.pm
Criterion Covered Total %
statement 53 159 33.3
branch 6 30 20.0
condition 0 6 0.0
subroutine 14 24 58.3
pod 13 13 100.0
total 86 232 37.0


line stmt bran cond sub pod time code
1             package Bio::Phylo::Project;
2 13     13   78 use strict;
  13         25  
  13         372  
3 13     13   65 use base 'Bio::Phylo::Listable';
  13         20  
  13         3356  
4 13     13   76 use Bio::Phylo::Util::CONSTANT qw':all';
  13         23  
  13         3342  
5 13     13   78 use Bio::Phylo::Util::Exceptions 'throw';
  13         30  
  13         454  
6 13     13   61 use Bio::Phylo::Util::Logger;
  13         23  
  13         417  
7 13     13   64 use Bio::Phylo::IO 'parse';
  13         20  
  13         411  
8 13     13   70 use Bio::Phylo::Factory;
  13         22  
  13         76  
9             my $fac = Bio::Phylo::Factory->new;
10             my $logger = Bio::Phylo::Util::Logger->new;
11              
12             {
13              
14             =head1 NAME
15              
16             Bio::Phylo::Project - Container for related data
17              
18             =head1 SYNOPSIS
19              
20             use Bio::Phylo::Factory;
21             my $fac = Bio::Phylo::Factory->new;
22             my $proj = $fac->create_project;
23             my $taxa = $fac->create_taxa;
24             $proj->insert($taxa);
25             $proj->insert($fac->create_matrix->set_taxa($taxa));
26             $proj->insert($fac->create_forest->set_taxa($taxa));
27             print $proj->to_xml;
28              
29             =head1 DESCRIPTION
30              
31             The project module is used to collect taxa blocks, tree blocks and
32             matrices.
33              
34             =head1 METHODS
35              
36             =head2 MUTATORS
37              
38             =over
39              
40             =item merge()
41              
42             Project constructor.
43              
44             Type : Constructor
45             Title : merge
46             Usage : my $project = Bio::Phylo::Project->merge( @projects )
47             Function: Populates a Bio::Phylo::Project object from a list of projects
48             Returns : A Bio::Phylo::Project object.
49             Args : A list of Bio::Phylo::Project objects to be merged
50              
51             =cut
52              
53             sub merge {
54 0     0 1 0 my $class = shift;
55 0         0 my $self = $class->SUPER::new;
56 0         0 my @taxa = map { @{ $_->get_items(_TAXA_) } } @_;
  0         0  
  0         0  
57 0         0 my $taxa = $fac->create_taxa->merge_by_name(@taxa);
58 0         0 my $forest = $fac->create_forest( '-taxa' => $taxa );
59 0         0 $forest->insert($_) for map { @{ $_->get_items(_TREE_) } } @_;
  0         0  
  0         0  
60 0         0 $self->insert($taxa);
61 0         0 $self->insert($forest);
62 0         0 $self->insert($_) for map { $_->set_taxa($taxa) } map { @{ $_->get_items(_MATRIX_) } } @_;
  0         0  
  0         0  
  0         0  
63 0         0 return $self;
64             }
65              
66             =item set_datasource()
67              
68             Project constructor.
69              
70             Type : Constructor
71             Title : set_datasource
72             Usage : $project->set_datasource( -file => $file, -format => 'nexus' )
73             Function: Populates a Bio::Phylo::Project object from a data source
74             Returns : A Bio::Phylo::Project object.
75             Args : Arguments as must be passed to Bio::Phylo::IO::parse
76              
77             =cut
78              
79             sub set_datasource {
80 0     0 1 0 my $self = shift;
81 0         0 return parse( '-project' => $self, @_ );
82             }
83              
84             =item reset_xml_ids()
85              
86             Resets all xml ids to default values
87              
88             Type : Mutator
89             Title : reset_xml_ids
90             Usage : $project->reset_xml_ids
91             Function: Resets all xml ids to default values
92             Returns : A Bio::Phylo::Project object.
93             Args : None
94              
95             =cut
96              
97             sub reset_xml_ids {
98 0     0 1 0 my $self = shift;
99 0 0       0 if ( UNIVERSAL::can($self,'set_xml_id') ) {
100 0         0 my $xml_id = $self->get_tag;
101 0         0 my $obj_id = sprintf("%x",$self->get_id);
102 0         0 $xml_id =~ s/^(.).+(.)$/$1$2$obj_id/;
103 0         0 $self->set_xml_id($xml_id);
104             }
105 0 0       0 if ( UNIVERSAL::can($self,'get_entities') ) {
106 0         0 reset_xml_ids($_) for @{ $self->get_entities };
  0         0  
107             }
108 0         0 return $self;
109             }
110              
111             =back
112              
113             =head2 ACCESSORS
114              
115             =over
116              
117             =cut
118              
119             my $TYPE = _PROJECT_;
120             my $TAXA = _TAXA_;
121             my $FOREST = _FOREST_;
122             my $MATRIX = _MATRIX_;
123             my $get_object = sub {
124             my ( $self, $CONSTANT ) = @_;
125             my @result;
126             for my $ent ( @{ $self->get_entities } ) {
127             if ( $ent->_type == $CONSTANT ) {
128             push @result, $ent;
129             }
130             }
131             return \@result;
132             };
133              
134             =item get_taxa()
135              
136             Getter for taxa objects
137              
138             Type : Accessor
139             Title : get_taxa
140             Usage : my $taxa = $proj->get_taxa;
141             Function: Getter for taxa objects
142             Returns : An array reference of taxa objects
143             Args : NONE.
144              
145             =cut
146              
147             sub get_taxa {
148 3     3 1 7 my $self = shift;
149 3         9 return $get_object->( $self, $TAXA );
150             }
151              
152             =item get_forests()
153              
154             Getter for forest objects
155              
156             Type : Accessor
157             Title : get_forests
158             Usage : my $forest = $proj->get_forests;
159             Function: Getter for forest objects
160             Returns : An array reference of forest objects
161             Args : NONE.
162              
163             =cut
164              
165             sub get_forests {
166 3     3 1 5 my $self = shift;
167 3         9 return $get_object->( $self, $FOREST );
168             }
169              
170             =item get_matrices()
171              
172             Getter for matrix objects
173              
174             Type : Accessor
175             Title : get_matrices
176             Usage : my $matrix = $proj->get_matrices;
177             Function: Getter for matrix objects
178             Returns : An array reference of matrix objects
179             Args : NONE.
180              
181             =cut
182              
183             sub get_matrices {
184 4     4 1 7 my $self = shift;
185 4         11 return $get_object->( $self, $MATRIX );
186             }
187              
188             =item get_items()
189              
190             Gets all items of the specified type, recursively. This method can be used
191             to get things like all the trees in all the forest objects as one flat list
192             (or, indeed, all nodes, all taxon objects, etc.)
193              
194             Type : Accessor
195             Title : get_items
196             Usage : my @nodes = @{ $proj->get_items(_NODE_) };
197             Function: Getter for items of specified type
198             Returns : An array reference of objects
199             Args : A type constant as defined in Bio::Phylo::Util::CONSTANT
200              
201             =cut
202              
203             sub _item_finder {
204 108     108   175 my ( $item, $const, $array ) = @_;
205 108 50       244 if ( UNIVERSAL::can($item,'_type') ) {
206 108 100       187 if ( $item->_type == $const ) {
    100          
207 55         73 push @{ $array }, $item;
  55         139  
208             }
209             elsif ( UNIVERSAL::can($item,'get_entities') ) {
210 37         52 _item_finder( $_, $const, $array ) for @{ $item->get_entities };
  37         71  
211             }
212             }
213             }
214            
215             sub get_items {
216 19     19 1 65 my ( $self, $const ) = @_;
217 19 50       98 if ( $const !~ /^\d+/ ) {
218 0         0 throw 'BadArgs' => 'Constant must be an integer';
219             }
220 19         40 my $result = [];
221 19         66 _item_finder( $self, $const, $result );
222 19         119 return $result;
223             }
224              
225             =item get_document()
226              
227             Type : Serializer
228             Title : doc
229             Usage : $proj->get_document()
230             Function: Creates a DOM Document object, containing the
231             present state of the project by default
232             Returns : a Document object
233             Args : a DOM factory object
234             Optional: pass 1 to obtain a document node without
235             content
236              
237             =cut
238              
239             sub get_document {
240 0     0 1 0 my $self = shift;
241 0         0 my $dom = $_[0];
242 0         0 my @args = @_;
243              
244             # handle dom factory object...
245 0 0 0     0 if ( looks_like_instance( $dom, 'SCALAR' )
246             && $dom->_type == _DOMCREATOR_ )
247             {
248 0         0 splice( @args, 0, 1 );
249             }
250             else {
251 0         0 $dom = $Bio::Phylo::NeXML::DOM::DOM;
252 0 0       0 unless ($dom) {
253 0         0 throw 'BadArgs' => 'DOM factory object not provided';
254             }
255             }
256             ### # make sure argument handling works here...
257 0         0 my $empty = shift @args;
258 0         0 my $doc = $dom->create_document();
259 0         0 my $root;
260 0 0       0 unless ($empty) {
261 0         0 $root = $self->to_dom($dom);
262 0         0 $doc->set_root($root);
263             }
264 0         0 return $doc;
265             }
266              
267             =item get_attributes()
268              
269             Retrieves attributes for the element.
270              
271             Type : Accessor
272             Title : get_attributes
273             Usage : my %attrs = %{ $obj->get_attributes };
274             Function: Gets the xml attributes for the object;
275             Returns : A hash reference
276             Args : None.
277             Comments: throws ObjectMismatch if no linked taxa object
278             can be found
279              
280             =cut
281              
282             sub get_attributes {
283 0     0 1 0 my $self = shift;
284 0         0 my $class = ref($self);
285 0         0 my $version = $class->VERSION;
286 0         0 my %defaults = (
287             'version' => _NEXML_VERSION_,
288             'generator' => "$class v.$version",
289             'xmlns' => _NS_NEXML_,
290             'xsi:schemaLocation' => _NS_NEXML_ . ' '
291             . _NS_NEXML_
292             . '/nexml.xsd',
293             );
294 0         0 my %attrs = ( %defaults, %{ $self->SUPER::get_attributes } );
  0         0  
295 0         0 return \%attrs;
296             }
297              
298             =item is_identifiable()
299              
300             By default, all XMLWritable objects are identifiable when serialized,
301             i.e. they have a unique id attribute. However, in some cases a serialized
302             object may not have an id attribute (governed by the nexml schema). This
303             method indicates whether that is the case.
304              
305             Type : Test
306             Title : is_identifiable
307             Usage : if ( $obj->is_identifiable ) { ... }
308             Function: Indicates whether IDs are generated
309             Returns : BOOLEAN
310             Args : NONE
311              
312             =cut
313              
314 0     0 1 0 sub is_identifiable { 0 }
315              
316             =back
317              
318             =head2 SERIALIZERS
319              
320             =over
321              
322             =item to_xml()
323              
324             Serializes invocant to XML.
325              
326             Type : XML serializer
327             Title : to_xml
328             Usage : my $xml = $obj->to_xml;
329             Function: Serializes $obj to xml
330             Returns : An xml string
331             Args : Same arguments as can be passed to individual contained objects
332              
333             =cut
334              
335             sub _add_project_metadata {
336 0     0   0 my $self = shift;
337 0         0 $self->set_namespaces( 'dc' => _NS_DC_ );
338 0 0       0 if ( my $user = $ENV{'USER'} ) {
339 0         0 $logger->debug("adding user metadata '${user}'");
340 0         0 $self->add_meta(
341             $fac->create_meta( '-triple' => { 'dc:creator' => $user } ) );
342             }
343 0         0 eval { require DateTime };
  0         0  
344 0 0       0 if ( not $@ ) {
345 0         0 my $now = DateTime->now();
346 0         0 $logger->debug("adding timestamp metadata '${now}'");
347 0         0 $self->add_meta(
348             $fac->create_meta( '-triple' => { 'dc:date' => $now } ) );
349             }
350             else {
351 0         0 undef($@);
352             }
353 0 0       0 if ( my $desc = $self->get_desc ) {
354 0         0 $logger->debug("adding description metadata '${desc}'");
355 0         0 $self->add_meta(
356             $fac->create_meta( '-triple' => { 'dc:description' => $desc } )
357             );
358             }
359             }
360              
361             sub to_xml {
362 0     0 1 0 my $self = shift;
363 0         0 my %args;
364 0 0       0 if ( @_ ) {
365 0         0 %args = @_;
366 0 0       0 $self->reset_xml_ids if $args{'-reset'};
367             }
368              
369             # creating opening tags
370 0         0 $self->_add_project_metadata;
371 0         0 my $xml = $self->get_xml_tag;
372 0         0 $logger->debug("created opening structure ${xml}");
373              
374             # processing contents
375 0         0 my @linked = ( @{ $self->get_forests }, @{ $self->get_matrices } );
  0         0  
  0         0  
376 0         0 $logger->debug("fetched linked objects @linked");
377              
378             # writing out taxa blocks and linked objects
379 0         0 my %taxa = map { $_->get_id => $_ } @{ $self->get_taxa },
  0         0  
380 0         0 map { $_->make_taxa } @linked;
  0         0  
381 0         0 for ( values %taxa, @linked ) {
382 0         0 $logger->debug("writing $_ to xml");
383 0         0 $xml .= $_->to_xml(%args);
384             }
385 0         0 $xml .= '</' . $self->get_tag . '>';
386              
387             # done creating xml strings
388 0         0 $logger->debug($xml);
389             #eval { require XML::Twig };
390             #if ( not $@ ) {
391             # my $twig = XML::Twig->new( 'pretty_print' => 'indented' );
392             # eval { $twig->parse($xml) };
393             # if ($@) {
394             # throw 'API' => "Couldn't build xml: " . $@ . "\n\n$xml";
395             # }
396             # else {
397             # return $twig->sprint;
398             # }
399             #}
400             #else {
401             # undef $@;
402             # return $xml;
403             #}
404 0         0 return $xml;
405             }
406              
407             =item to_nexus()
408              
409             Serializes invocant to NEXUS.
410              
411             Type : NEXUS serializer
412             Title : to_nexus
413             Usage : my $nexus = $obj->to_nexus;
414             Function: Serializes $obj to nexus
415             Returns : An nexus string
416             Args : Same arguments as can be passed to individual contained objects
417              
418             =cut
419              
420             my $write_notes = sub {
421             my ( $self, @taxa ) = @_;
422             my $nexus = 'BEGIN NOTES;' . "\n";
423             my $version = $self->VERSION;
424             my $class = ref $self;
425             my $time = localtime();
426             $nexus .= "[! Notes block written by $class $version on $time ]\n";
427             for my $taxa ( @taxa ) {
428             my $name = $taxa->get_nexus_name;
429             my ( $i, $j ) = ( 1, 0 );
430             for my $taxon ( @{ $taxa->get_entities } ) {
431             if ( my $link = $taxon->get_link ) {
432             if ( $link =~ m|/phylows/| ) {
433            
434             # link has no query string, append one
435             if ( $link !~ /\?/ ) {
436             $link .= '?';
437             }
438            
439             # link has a format statement, replace format
440             if ( $link =~ /\?.*format=/ ) {
441             $link =~ s/(\?.*format=)\s+/$1nexus/;
442             }
443            
444             # append format statement
445             else {
446             $link .= '&' if $link !~ /\?$/ && $link !~ /&$/;
447             $link .= 'format=nexus';
448             }
449             }
450             $nexus .= "\tSUT TAXA = $name TAXON = $i NAME = hyperlink STRING = '$link';\n";
451             $nexus .= "\tHYPERLINK TAXA = $name TAXON = $j URL = '$link';\n";
452             }
453             $i++;
454             $j++;
455             }
456             }
457             $nexus .= 'END;' . "\n";
458             };
459              
460             sub to_nexus {
461 2     2 1 5 my $self = shift;
462 2         5 my $nexus = "#NEXUS\n";
463 2         4 my @linked = ( @{ $self->get_forests }, @{ $self->get_matrices } );
  2         6  
  2         12  
464 2         6 my %taxa = map { $_->get_id => $_ } @{ $self->get_taxa },
  2         5  
465 2         5 map { $_->make_taxa } @linked;
  1         4  
466 2         7 for ( values %taxa, @linked ) {
467 3         14 $nexus .= $_->to_nexus(@_);
468             }
469 2         8 $nexus .= $write_notes->($self,values %taxa);
470 2         13 return $nexus;
471             }
472              
473             =item to_dom()
474              
475             Type : Serializer
476             Title : to_dom
477             Usage : $node->to_dom
478             Function: Generates a DOM subtree from the invocant
479             and its contained objects
480             Returns : an XML::LibXML::Element object
481             Args : a DOM factory object
482              
483             =cut
484              
485             sub to_dom {
486 0     0 1 0 my ( $self, $dom ) = @_;
487 0   0     0 $dom ||= Bio::Phylo::NeXML::DOM->get_dom;
488 0 0       0 unless ( looks_like_object $dom, _DOMCREATOR_ ) {
489 0         0 throw 'BadArgs' => 'DOM factory object not provided';
490             }
491 0         0 my $elt = $self->get_dom_elt($dom);
492 0         0 my @linked = ( @{ $self->get_forests }, @{ $self->get_matrices } );
  0         0  
  0         0  
493 0         0 my %taxa = map { $_->get_id => $_ } @{ $self->get_taxa },
  0         0  
494 0         0 map { $_->make_taxa } @linked;
  0         0  
495 0         0 for ( values %taxa, @linked ) {
496 0         0 $elt->set_child( $_->to_dom( $dom, @_ ) );
497             }
498 0         0 return $elt;
499             }
500 114     114   254 sub _type { $TYPE }
501 0     0     sub _tag { 'nex:nexml' }
502              
503             =back
504              
505             =cut
506              
507             # podinherit_insert_token
508              
509             =head1 SEE ALSO
510              
511             There is a mailing list at L<https://groups.google.com/forum/#!forum/bio-phylo>
512             for any user or developer questions and discussions.
513              
514             =over
515              
516             =item L<Bio::Phylo::Listable>
517              
518             The L<Bio::Phylo::Project> object inherits from the L<Bio::Phylo::Listable>
519             object. Look there for more methods applicable to the project object.
520              
521             =item L<Bio::Phylo::Manual>
522              
523             Also see the manual: L<Bio::Phylo::Manual> and L<http://rutgervos.blogspot.com>.
524              
525             =back
526              
527             =head1 CITATION
528              
529             If you use Bio::Phylo in published research, please cite it:
530              
531             B<Rutger A Vos>, B<Jason Caravas>, B<Klaas Hartmann>, B<Mark A Jensen>
532             and B<Chase Miller>, 2011. Bio::Phylo - phyloinformatic analysis using Perl.
533             I<BMC Bioinformatics> B<12>:63.
534             L<http://dx.doi.org/10.1186/1471-2105-12-63>
535              
536             =cut
537              
538             }
539              
540             1