File Coverage

blib/lib/Bio/Phylo/Project.pm
Criterion Covered Total %
statement 56 162 34.5
branch 6 30 20.0
condition 0 6 0.0
subroutine 15 25 60.0
pod 13 13 100.0
total 90 236 38.1


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