File Coverage

blib/lib/Bio/NEXUS.pm
Criterion Covered Total %
statement 357 483 73.9
branch 117 172 68.0
condition 57 83 68.6
subroutine 42 56 75.0
pod 35 35 100.0
total 608 829 73.3


line stmt bran cond sub pod time code
1             ######################################################
2             # NEXUS.pm
3             ######################################################
4             #
5             # $Id: NEXUS.pm,v 1.122 2012/02/10 13:28:28 astoltzfus Exp $
6             # $Revision: 1.122 $
7             #
8             #################### START POD DOCUMENTATION ##################
9              
10             =head1 NAME
11              
12             Bio::NEXUS - An object-oriented Perl Applications Programming Interface (API) for the NEXUS file format
13              
14             =head1 SYNOPSIS
15              
16             my $nexus =Bio::NEXUS->new($file);
17             # if $file is not provided, an empty Bio::NEXUS object will be created
18             $nexus->write($newfile);
19              
20             =head1 DESCRIPTION
21              
22             This is the base class for the Bio::NEXUS package, providing an object-oriented API to
23             the NEXUS file format of I, 1997. This module provides methods to
24             add/remove blocks, select blocks/trees/subtrees/OTUs/characters and so on. For a
25             tutorial illustrating how to use Bio::NEXUS, see L.
26              
27             =head1 FEEDBACK
28              
29             All feedback (bugs, feature enhancements, etc.) are all greatly appreciated.
30              
31             =head1 AUTHORS
32              
33             Chengzhi Liang (liangc@umbi.umd.edu)
34             Weigang Qiu (weigang@genectr.hunter.cuny.edu)
35             Peter Yang (pyang@rice.edu)
36             Thomas Hladish (tjhladish at yahoo)
37             Arlin Stoltzfus (arlin.stoltzfus@nist.gov)
38              
39             =head1 METHODS
40              
41             =cut
42              
43             package Bio::NEXUS;
44              
45 34     34   1361513 use strict;
  34         92  
  34         1787  
46             # use Data::Dumper; # XXX this is not used, might as well not load it!
47             # use Carp; # XXX this is not used, might as well not load it!
48              
49 34     34   22333 use Bio::NEXUS::Functions;
  34         288  
  34         8634  
50 34     34   25136 use Bio::NEXUS::AssumptionsBlock;
  34         124  
  34         1197  
51 34     34   32775 use Bio::NEXUS::CharactersBlock;
  34         145  
  34         5852  
52 34     34   26319 use Bio::NEXUS::TreesBlock;
  34         148  
  34         234  
53 34     34   29586 use Bio::NEXUS::HistoryBlock;
  34         107  
  34         471  
54 34     34   220 use Bio::NEXUS::Node;
  34         68  
  34         735  
55 34     34   25369 use Bio::NEXUS::TaxaBlock;
  34         100  
  34         1019  
56 34     34   39671 use Bio::NEXUS::SetsBlock;
  34         116  
  34         1292  
57 34     34   38108 use Bio::NEXUS::SpanBlock;
  34         122  
  34         2071  
58 34     34   34437 use Bio::NEXUS::UnalignedBlock;
  34         115  
  34         1249  
59 34     34   23464 use Bio::NEXUS::UnknownBlock;
  34         179  
  34         990  
60 34     34   22072 use Bio::NEXUS::DataBlock;
  34         97  
  34         1153  
61 34     34   24064 use Bio::NEXUS::DistancesBlock;
  34         116  
  34         1140  
62             # in progress
63 34     34   23429 use Bio::NEXUS::CodonsBlock;
  34         109  
  34         964  
64 34     34   27408 use Bio::NEXUS::NotesBlock;
  34         95  
  34         1046  
65              
66 34     34   208 use Bio::NEXUS::Util::Logger;
  34         69  
  34         773  
67 34     34   243 use Bio::NEXUS::Util::Exceptions 'throw';
  34         67  
  34         2512  
68              
69             #use Bio::NEXUS::CodonsBlock;
70             #use Bio::NEXUS::NotesBlock;
71              
72             # Version number is obtained cvs $Name tag (eg. release_1_05).
73             # ExtUtils::MakeMaker reads package global $VERSION
74              
75 34     34   194 use vars qw($VERSION $AUTOLOAD);
  34         69  
  34         223093  
76             $VERSION = do { my @r = ( q$Name: $ =~ /\d+/g ); ( $#r < 0 ) ? '0.78' : sprintf " %d." . "%02d" x $#r, @r; };
77              
78             # a logger is an object that conditionally prints messages,
79             # so we don't need to add print statements and then comment
80             # them out anymore. You can leave the logging in the code, and
81             # make it invisible by lowering the log level.
82             my $logger = Bio::NEXUS::Util::Logger->new; # XXX
83              
84             =head2 new
85              
86             Title : new
87             Usage : my $nexus = Bio::NEXUS->new($filename, $verbose);
88             Function: Creates a new Bio::NEXUS object
89             Returns : Bio::NEXUS object
90             Args : $filename, $verbose, or none
91              
92             =cut
93              
94             sub new {
95 83     83 1 122725 my ( $class, $filename, $verbose ) = @_;
96            
97             # XXX notify user conditionally
98 83         1238 $logger->info( "Constructor for $class called" );
99 83         226 my $self = {};
100 83         246 bless( $self, $class );
101 83 100       397 if ($filename) {
102 69         317 $self->read_file( $filename, $verbose );
103 69         499 $filename =~ s/\.nex$//;
104 69         348 $self->set_name($filename);
105             }
106 83         470 return $self;
107             }
108              
109              
110             =head2 get_bionexus_version
111              
112             Title : get_bionexus_version
113             Usage : Bio::NEXUS->get_bionexus_version();
114             Function: gets the package version
115             Returns : value of \$VERSION
116             Args : none
117              
118             =cut
119              
120 0     0 1 0 sub get_bionexus_version { return $VERSION; }
121              
122             =head2 read_file
123              
124             Title : read_file
125             Usage : Bio::NEXUS->read_file($filename, $verbose);
126             Function: Reads the contents of the NEXUS file and populate the data in the Bio::NEXUS object
127             Returns : None
128             Args : $filename, $verbose, or none
129              
130             =cut
131              
132             sub read_file {
133 69     69 1 155 my ( $self, $filename, $verbose ) = @_;
134 69 50       3058 if ( not -e $filename ) {
135            
136             # XXX refer to Bio::NEXUS::Util::Exceptions -
137             # exceptions are generally more useful for
138             # tracking down problems
139 0         0 throw 'FileError' => "$filename is not a valid filename";
140             }
141             $self->read( {
142 69         597 'format' => 'filename',
143             'param' => $filename,
144             'verbose' => $verbose,
145             } );
146             }
147              
148             =head2 read
149              
150             Title : read
151             Usage : Bio::NEXUS->read({format => 'string', 'param' => $buffer, 'verbose' => $verbose});
152             Usage : Bio::NEXUS->read({format => 'file', 'param' => $filename, 'verbose' => $verbose});
153             Function: Reads the contents of the NEXUS file and populate the data in the NEXUS object
154             Returns : None
155             Args : $filename, $verbose, or none
156              
157             =cut
158              
159             sub read {
160 87     87 1 5055 my ( $self, $args ) = @_;
161 87   50     345 $args->{'format'} ||= 'string';
162 87   50     319 $args->{'param'} ||= '';
163 87   50     595 my $verbose = $args->{'verbose'} || 0;
164 87         189 my $nexus_file;
165             my $filename;
166              
167 87 100       423 if ( lc $args->{'format'} eq 'string' ) {
168 18         39 $nexus_file = $args->{'param'};
169 18         36 $filename = 'INPUT';
170             }
171             else {
172 69         196 $filename = $args->{'param'};
173 69         483 $nexus_file = _slurp($filename);
174             }
175              
176             # Read entire file into scalar $nexus_file
177 87         654 $logger->info('Reading NEXUS file');
178 87         454 $self->{'filename'} = $filename;
179              
180 87         183 my $found_nexus_token = 0;
181 87         159 my $comment_level = 0;
182 87         164 my $quote_level = 0;
183 87         179 my $comment = '';
184 87         165 my $block_type = '';
185 87         204 my @command_level_strings = ();
186 87         201 my $command = '';
187 87         164 my $in_tree_string = 0;
188 87         152 my $prev_text_char = '';
189              
190 87         18888 for my $text_char ( split //, $nexus_file ) {
191              
192             # if we're at the beginning of a single-quoted string
193             # (We're also supporting double quoting, since double quotes don't seem
194             # to be used for a different meaning, and we need to support double
195             # quotes in output from programs like clustal. We will not, however,
196             # output double quotes.)
197              
198 91684 100       183113 $text_char = q{'} if $text_char eq q{"};
199              
200 91684 100 100     810113 if ( ( $text_char eq q{'} )
    100 100        
    100 66        
    100 100        
    100 100        
    50          
201             && $quote_level == 0
202             && $comment_level == 0
203             && $found_nexus_token )
204             {
205 212         350 $command .= $text_char;
206 212         263 $quote_level++;
207             }
208              
209             # if we're inside a single-quoted string
210             elsif ( $quote_level > 0 ) {
211 4856         5500 $command .= $text_char;
212              
213             #turn off the quote flag if we're ending the quoted string
214 4856 100       9349 if ( $text_char eq q{'} ) {
215 212 50       522 $quote_level =
216             ( $prev_text_char eq $text_char )
217             ? $quote_level + 1
218             : $quote_level - 1;
219             }
220              
221             }
222              
223             # if we're entering a (possibly nested) comment, or we're already in
224             # one, but we're not looking at bracketed bootstraps in the tree string
225             elsif ( ( $text_char eq '[' || $comment_level > 0 )
226             && $in_tree_string == 0 )
227             {
228 5434         6764 $comment .= $text_char;
229 5434 100       10161 $comment_level++ if $text_char eq '[';
230              
231             # if we see the end of a (possibly nested) comment
232 5434 100       10049 if ( $text_char eq ']' ) {
233 161         262 $comment_level--;
234              
235             # if we just closed found the last right bracket in the comment,
236             # then add the comment to the Bio::NEXUS obj
237 161 100 66     817 if ( $comment_level == 0 && @command_level_strings == 0 ) {
238 34         173 $self->add_comment($comment);
239 34         63 $comment = q{};
240             }
241             }
242             }
243              
244             # if we haven't found '#NEXUS' yet
245             elsif ( !$found_nexus_token ) {
246 526         941 $command .= $text_char;
247              
248             # if we've found the whole #NEXUS token that's supposed
249             # to start the file (though it may be broken by comments)
250 526 100       3151 if ( $command =~ /^\s*#NEXUS/i ) {
    100          
251 86         176 $found_nexus_token = 1;
252 86         261 $command = q{};
253             }
254              
255             # If the file starts with something else, then throw.
256             # This regex will match '#NEX' and '#NEXUS', but not '#NEXT'
257             elsif ( $command !~ /^\s*(?:#(?:N(?:E(?:X(?:U(?:S)?)?)?)?)?)?$/i ) {
258 1         9 throw 'BadFormat' => "'$filename' does not begin with the \n'#NEXUS' token; it does not appear to be a NEXUS file.\n";
259             }
260             }
261              
262             # if we're at the beginning of a block/command
263             elsif ( $command eq q{} ) {
264 2392 100       4484 if ( $comment ) {
265 25         67 push( @command_level_strings, $comment );
266 25         44 $comment = q{};
267             }
268 2392 100       4986 if ( $text_char ne "\n" ) {
269 1050         1861 $command .= $text_char;
270             }
271             }
272              
273             # if we're inside a block, but haven't gotten to the end of the command
274             elsif ( $command !~ /;$/ ) {
275 78264         97895 $command .= $text_char;
276 78264 100 100     500952 if ( ( $in_tree_string == 0 )
      66        
      100        
277             && ( $block_type eq 'trees' || $block_type eq 'history' )
278             && ( $text_char eq '=' ) )
279             {
280 88 100       575 $in_tree_string = 1 if ( $command =~ /tree\s.+=/i );
281             }
282 78176 100       159721 else { $in_tree_string = 0 if $text_char eq ';' }
283             }
284              
285 91683         124339 $prev_text_char = $text_char;
286              
287             # Only process if we might genuinely have reached the end
288             # of a command or block
289 91683 100 100     568940 if ( !$comment_level && !$quote_level && $text_char eq ';' ) {
      100        
290              
291             # if we've read in the entire begin block command
292 1050 100       13754 if ( $command =~ /begin\s+(.+)\s*;/i ) {
    100          
    50          
293 231         845 $block_type = lc $1;
294 231         2061 $logger->info("found 'begin' token for a $block_type block");
295 231         470 push( @command_level_strings, $command );
296 231         427 $command = q{};
297 231 50       908 if ($comment) {
298 0         0 push( @command_level_strings, $comment );
299 0         0 $comment = q{};
300             }
301             }
302              
303             # if we've found the end of the block
304             elsif ( $command =~ /^\s*end(?:block)?\s*;/i ) {
305 231         2278 $logger->info("found 'end' token");
306 231         448 $command = 'end';
307 231         461 push( @command_level_strings, $command );
308 231         399 $command = q{};
309 231 100       642 if ($comment) {
310 1         3 push( @command_level_strings, $comment );
311 1         3 $comment = q{};
312             }
313              
314             # Send the commands [and comments] off to be turned into a block
315 231         1130 my $block_obj =
316             $self->create_block( $block_type, \@command_level_strings,
317             $verbose );
318 222         896 $self->add_block($block_obj);
319 222         589 @command_level_strings = ();
320 222         741 $block_type = q{};
321              
322             }
323              
324             # if we've found the end of a command (but not an
325             # 'END BLOCK;' command, since we already asked that) remove the
326             # semicolon at the end, since the block parsers aren't expecting
327             # one, as well as surrounding white space. Two substitutions
328             # are faster than one, in this case.
329             elsif ( $command =~ s/\s*;\s*$// ) {
330 588         2263 $command =~ s/^\s*//;
331 588 100       1774 if ($comment) {
332 10         870 push( @command_level_strings, $comment );
333 10         25 $comment = q{};
334             }
335 588         1169 push( @command_level_strings, $command );
336 588         15430 $command = q{};
337             }
338             }
339             }
340              
341             # Create a taxa block if we didn't find one in the file
342 77 100       9275 if ( !$self->get_block('taxa') ) {
343 6         41 $logger->info("No taxa block found, will create one");
344 6         34 $self->set_taxablock;
345             }
346              
347 77         183 my $counter = scalar @{ $self->get_blocks() };
  77         243  
348 77         661 $logger->info("$counter blocks read. NEXUS read complete.");
349 77         414 return $self;
350             }
351              
352             =head2 create_block
353              
354             Title : create_block
355             Usage : my $block = Bio::NEXUS->create_block($blocktype,$block_string, $verbose);
356             Function: Creates a block object based on the input block type and block content as string
357             Returns : A block object (If Block type is 'Characters' then 'Bio::NEXUS::CharactersBlock' is returned
358             Args : $block_type (as string), $block_content (as string), verbose
359              
360             =cut
361              
362             sub create_block {
363 232     232 1 1548 my ( $self, $block_type, $commands, $verbose ) = @_;
364 232         1471 $logger->info("creating block $block_type");
365 232         432 my $block; # This will hold a block object, once one is constructed
366 232         683 my @args = ( $block_type, $commands, $verbose );
367              
368 232         2865 my %block_types = (
369             'assumptions' => "Bio::NEXUS::AssumptionsBlock",
370             'characters' => "Bio::NEXUS::CharactersBlock",
371             # in progress: codons
372             # 'codons' => "Bio::NEXUS::CodonsBlock",
373             'data' => "Bio::NEXUS::DataBlock",
374             'distances' => "Bio::NEXUS::DistancesBlock",
375             'history' => "Bio::NEXUS::HistoryBlock",
376             # in progress: notes
377             # 'notes' => "Bio::NEXUS::NotesBlock",
378             'sets' => "Bio::NEXUS::SetsBlock",
379             'span' => "Bio::NEXUS::SpanBlock",
380             'taxa' => "Bio::NEXUS::TaxaBlock",
381             'trees' => "Bio::NEXUS::TreesBlock",
382             'unaligned' => "Bio::NEXUS::UnalignedBlock"
383             );
384 232         521 my $class = $block_types{$block_type};
385 232 100       746 if ( defined( $class ) ) { $logger->info("class: $class"); }
  224         1563  
386              
387 232         445 my $taxlabels;
388 232 100       901 if ( defined $self->get_block('taxa') ) {
389 139         581 $taxlabels = $self->get_taxlabels();
390             }
391 231 100       1006 if ( $class ) {
392 223         2652 $block = $class->new( @args, $taxlabels );
393             }
394             else {
395 8         49 $logger->info("An UnknownBlock is being created for block_type: $block_type");
396 8         54 $block = Bio::NEXUS::UnknownBlock->new( @args );
397             }
398              
399 222 100 100     1482 if ( $block_type =~ m/taxa/i and my $title = $block->get_title() ) {
400 6         48 $self->set_name( $title );
401             }
402              
403             # Check to make sure that if a Taxa Block is defined,
404             # that everything is included in it
405 222         945 $self->_validate_taxa($block);
406              
407 222         1592 return $block;
408             }
409              
410             =begin comment
411              
412             Title : _validate_taxa
413             Usage :
414             Function:
415             Returns :
416             Args :
417              
418             =end comment
419              
420             =cut
421              
422             sub _validate_taxa {
423 222     222   393 my ( $self, $block ) = @_;
424 222         904 my $block_type = $block->get_type();
425 222         835 my $taxablock = $self->get_block('taxa');
426 222 100       797 return unless $taxablock;
427              
428 132         253 my @taxlabels = @{ $taxablock->get_taxlabels() };
  132         583  
429              
430             # Every taxon listed in the characters or trees blocks should be in the
431             # Taxa Block as well
432 132 100 100     1052 if ( lc $block_type eq 'characters' || lc $block_type eq 'trees' ) {
    100          
433 112         198 my @taxlabels = @{ $taxablock->get_taxlabels() };
  112         413  
434 112         229 my @block_taxa = @{ $block->get_taxlabels() };
  112         455  
435             LABEL:
436 112         365 for my $label (@block_taxa) {
437 372         468 my $match = 0;
438 372 50       529 next LABEL if grep { $label eq $_ } @taxlabels;
  2731         4824  
439 0         0 throw 'ObjectMismatch' => "Taxon <$label> in $block_type block is not in the TAXA Block";
440             }
441             }
442              
443             # And every set element should be in the Taxa Block
444             elsif ( lc $block_type eq 'sets' ) {
445              
446 1         2 my %taxsets = %{ $block->get_taxsets() };
  1         5  
447 1         4 for my $setname ( keys %taxsets ) {
448 5         6 my @elements = @{ $taxsets{$setname} };
  5         8  
449             ELEMENT:
450 5         8 for my $element (@elements) {
451 8 50       11 next ELEMENT if grep { $element eq $_ } @taxlabels;
  64         86  
452 0         0 throw 'ObjectMismatch' => "Element <$element> of set <$setname> is not in the TAXA Block";
453             }
454             }
455             }
456 132         406 return;
457             }
458              
459             =head2 clone
460              
461             Name : clone
462             Usage : my $newnexus = $nexus->clone();
463             Function: clone a NEXUS object; each block is also (shallow) cloned.
464             Returns : new Bio::NEXUS object
465             Args : none
466              
467             =cut
468              
469             sub clone {
470 3     3 1 8 my ($self) = @_;
471 3         9 my $class = ref($self);
472 3         7 my $newnexus = bless( { %{$self} }, $class );
  3         23  
473            
474             # clone blocks
475 3         7 my @newblocks;
476 3         6 for my $block ( @{ $self->get_blocks() } ) {
  3         10  
477 8         50 push @newblocks, $block->clone();
478             }
479 3         19 $newnexus->set_blocks( \@newblocks );
480 3         9 return $newnexus;
481             }
482              
483             =head2 set_name
484              
485             Title : set_name
486             Usage : Bio::NEXUS->set_name($name);
487             Function: Sets name for the NEXUS object (usually the filename).
488             Returns : Nothing
489             Args : $name (as string)
490              
491             =cut
492              
493             sub set_name {
494 75     75 1 185 my ( $self, $name ) = @_;
495 75         245 $self->{'name'} = $name;
496             }
497              
498             =head2 get_name
499              
500             Title : get_name
501             Usage : $name = Bio::NEXUS->get_name();
502             Function: Returns the name of the NEXUS object as string. (NEXUS filename).
503             Returns : NEXUS filename
504             Args : None
505              
506             =cut
507              
508             sub get_name {
509 0     0 1 0 my ($self) = @_;
510 0         0 return $self->{'name'};
511             }
512              
513             =head2 add_comment
514              
515             Name : add_comment
516             Usage : $nexus->add_comment($comment);
517             Function: add a block of comments.
518             Returns : none
519             Args : a string object
520              
521             =cut
522              
523             sub add_comment {
524 34     34 1 104 my ( $self, $comment ) = @_;
525 34         156 $self->add_block($comment);
526             }
527              
528             =head2 get_comments
529              
530             Name : get_comments
531             Usage : $nexus->get_comments();
532             Function: Retrieves all comments.
533             Returns : ref to an array of strings
534             Args : none
535              
536             =cut
537              
538             sub get_comments {
539 0     0 1 0 my ($self) = @_;
540 0         0 my @blocks_and_comments = @{ $self->get_blocks_and_comments() };
  0         0  
541 0         0 my @comments;
542 0         0 for my $block_or_comment (@blocks_and_comments) {
543 0 0       0 if ( _is_comment($block_or_comment) ) {
544 0         0 push( @comments, $block_or_comment );
545             }
546             }
547 0   0     0 return \@comments || [];
548             }
549              
550             =head2 get_filename
551              
552             Name : get_filename
553             Usage : $nexus->get_filename;
554             Function: get the NEXUS filename for this object.
555             Returns : A filename
556             Args : none
557              
558             =cut
559              
560             sub get_filename {
561 0     0 1 0 my ($self) = @_;
562 0         0 return $self->{'filename'};
563             }
564              
565             =head2 set_blocks
566              
567             Name : set_blocks
568             Usage : $nexus->set_blocks($blocks);
569             Function: set the blocks in this nexus file.
570             Returns : none
571             Args : an array of Block objects
572              
573             =cut
574              
575             sub set_blocks {
576 3     3 1 5 my ( $self, $blocks ) = @_;
577 3         8 $self->{'block_level'} = $blocks;
578             }
579              
580             =head2 add_block
581              
582             Name : add_block
583             Usage : $nexus->add_block($block_obj);
584             Function: add a block.
585             Returns : none
586             Args : a Bio::NEXUS::*Block object
587              
588             =cut
589              
590             sub add_block {
591 265     265 1 625 my ( $self, $block ) = @_;
592 265         395 push @{ $self->{'block_level'} }, $block;
  265         780  
593 265         534 return;
594             }
595              
596             =head2 remove_block
597              
598             Name : remove_block
599             Usage : $nexus->remove_block($blocktype, $title);
600             Function: remove a block
601             Returns : none
602             Args : block type and block name (strings)
603              
604             =cut
605              
606             sub remove_block {
607 2     2 1 1840 my ( $self, $blocktype, $title ) = @_;
608 2         7 my $items = $self->get_blocks_and_comments();
609 2         4 my $found_block = 0;
610 2         4 ITEM: for my $i ( 0 .. $#{ $items } ) {
  2         8  
611 5         8 my $item = $items->[$i];
612 5 50       15 next ITEM if _is_comment($item);
613 5 100       18 if ( $item->get_type() =~ m/$blocktype/i ) {
614            
615             # if either no title was specified, or the title matches
616 2 50 33     20 if ( !$title || $item->get_title =~ m/$title/i ) {
617 2         13 $logger->info("> found the block!");
618            
619             # the next statement removes a reference
620             # from a copy array - but will it remove the
621             # reference from the actual array of blocks?
622             # XXX yes -- RAV
623 2         4 splice( @{ $items }, $i, 1 );
  2         5  
624            
625             # sanity check
626 2         3 $logger->info('> blocks_and_comments.length: ' . scalar @{ $items } );
  2         14  
627 2         4 $logger->info('> self->get_blocks_and_comments.length: ' . scalar @{ $self->get_blocks_and_comments() } );
  2         7  
628            
629 2         5 $found_block = 1;
630             }
631             }
632 5         42 $self->{'block_level'} = $items;
633             }
634              
635 2 50       11 if ( not $found_block ) {
636 0         0 my $blockname = $blocktype;
637 0 0       0 if ( $title ) {
638 0         0 $blockname .= " ($title)"
639             }
640 0         0 $logger->warn("could not find a $blockname block");
641             }
642             }
643              
644             =head2 get_block
645              
646             Name : get_block
647             Usage : $nexus->get_block($blocktype, $blockname);
648             Function: Retrieves NEXUS block.
649             Returns : A Bio::NEXUS::*Block object
650             Args : none
651              
652             =cut
653              
654             sub get_block {
655 798     798 1 21543 my ( $self, $blocktype, $blockname ) = @_;
656              
657 798         1050 for my $block ( @{ $self->get_blocks($blocktype) } ) {
  798         2032  
658 602 50       1665 if ( $block->get_type() =~ m/$blocktype/i ) {
659 602 100       1313 if ( !$blockname ) {
    50          
660 597         3923 return $block;
661             }
662             elsif ( $block->get_title() =~ m/$blockname/i ) {
663 5         37 return $block;
664             }
665             }
666             }
667 195         701 return undef;
668             }
669              
670             =head2 get_blocks
671              
672             Name : get_blocks
673             Usage : $nexus->get_blocks($blocktype);
674             Function: Retrieves list of blocks of some type or all blocks.
675             Returns : Array of Bio::NEXUS::Block objects
676             Args : $blocktype or none
677              
678             =cut
679              
680             sub get_blocks {
681 919     919 1 5891 my ( $self, $blocktype ) = @_;
682              
683 919         1222 my @blocks;
684              
685 919         2218 for my $item ( @{ $self->get_blocks_and_comments() } ) {
  919         2199  
686              
687             # if it's actually a block object, and not a block-level comment
688 2124 100       5867 if ( !_is_comment($item) ) {
689 1768 100 100     7080 if (!$blocktype || $item->get_type() =~ /$blocktype/i ) {
690 1037         2931 push @blocks, $item;
691             }
692             }
693             }
694              
695 918         3244 return \@blocks;
696             }
697              
698             =head2 get_blocks_and_comments
699              
700             Name : get_blocks_and_comments
701             Usage : @blocks_and_comments = @{ $nexus->get_blocks_and_comments() };
702             Function: get all comments and blocks in the NEXUS object
703             Returns : array of strings and block objects
704             Args : none
705              
706             =cut
707              
708             sub get_blocks_and_comments {
709 925     925 1 1223 my ($self) = @_;
710 925   100     4239 return $self->{'block_level'} || [];
711             }
712              
713             =head2 get_weights
714              
715             Name : get_weights
716             Usage : $nexus->get_weights($charblockname);
717             Function: get all weights for a block.
718             Returns : the weights of alignments in a Characters Block
719             Args : an hash of weightset objects
720              
721             =cut
722              
723             sub get_weights {
724 0     0 1 0 my ( $self, $characters ) = @_;
725 0         0 my $blocks = $self->get_blocks('assumptions');
726 0         0 my %weights;
727 0         0 for my $block (@$blocks) {
728 0 0       0 if ( lc $block->get_link('characters') eq lc $characters ) {
729 0         0 push @{ $weights{ $block->get_title } },
  0         0  
730 0         0 @{ $block->get_assumptions };
731             }
732             }
733 0         0 return \%weights;
734             }
735              
736             =head2 get_taxlabels
737              
738             Name : get_taxlabels
739             Usage : $nexus->get_taxlabels();
740             Function: get the taxa labels of the NEXUS object (obtained from TAXA block).
741             Returns : an arrayreference of taxa labels.
742             Args : none
743              
744             =cut
745              
746             sub get_taxlabels {
747 154     154 1 340 my $self = shift;
748 154         513 return $self->get_block('taxa')->get_taxlabels();
749             }
750              
751             =head2 get_otus
752              
753             Name : get_otus
754             Usage : $nexus->get_otus();
755             Function: Retrieves list of OTUs
756             Returns : Array of OTU names or Bio::NEXUS::TaxUnit objects
757             Args : none
758              
759             =cut
760              
761             sub get_otus {
762 5     5 1 21 my $self = shift;
763              
764 5 50       17 if ( my $taxablock = $self->get_block('taxa') ) {
765 5         24 return $taxablock->get_taxlabels();
766             }
767 0 0       0 if ( my $charblock = $self->get_block('characters') ) {
768 0         0 return $charblock->get_otus();
769             }
770 0 0       0 if ( my $treesblock = $self->get_block('trees') ) {
771 0         0 return $treesblock->get_otus();
772             }
773 0         0 throw 'BadArgs' => 'no appropriate block exists to get the otus from';
774             }
775              
776             =head2 rename_otus
777              
778             Name : rename_otus
779             Usage : $nexus->rename_otus(\%translation);
780             Function: rename all OTUs
781             Returns : a new nexus object with new OTU names
782             Args : a ref to hash based on OTU name pairs
783              
784             =cut
785              
786             sub rename_otus {
787 2     2 1 5 my ( $self, $translation ) = @_;
788 2         7 my $nexus = $self->clone();
789 2         3 for my $block ( @{ $nexus->get_blocks() } ) {
  2         8  
790             # XXX duck-typing is probably okay, no? -- RAV
791             # if ( $block->get_type()
792             # =~ /^(?:characters|taxa|sets|span|history|trees)$/i )
793 6 50       56 if ( $block->can('rename_otus') ) {
794 6         37 $block->rename_otus($translation);
795             }
796             }
797 2         20 return $nexus;
798             }
799              
800             =head2 add_otu_clone
801              
802             Name : add_otu_clone
803             Usage : $nexus_object->add_otu_clone($original_otu_name, $copy_otu_name);
804             Function: creates a copy of a specified otu inside this Bio::NEXUS object
805             Returns : n/a
806             Args : $original_otu_name (string) - the name of the otu that will be cloned, $copy_otu_name (string) - the desired name for the new clone
807             Preconditions : $original_otu_name and $copy_otu_name are not equal, $original_otu_name is a valid otu name (existing otu)
808            
809             =cut
810              
811             sub add_otu_clone {
812 9     9 1 3672 my ( $self, $original_otu_name, $copy_otu_name ) = @_;
813 9         58 $logger->warn( "not fully implemented!" );
814            
815 9 50 33     46 if (! defined $original_otu_name || ! defined $copy_otu_name) {
816 0         0 throw 'BadArgs' => 'missing argument!';
817             }
818              
819 9 100       32 if ($original_otu_name eq $copy_otu_name) {
820 1         6 throw 'BadArgs' => 'otu names should be different';
821             }
822            
823 8 100       15 if ( grep {$_ eq $copy_otu_name} @{$self->get_taxlabels} ) {
  52         96  
  8         27  
824 1         5 throw 'BadArgs' => "duplicate otu name [$copy_otu_name] already exists";
825             }
826 7         62 $logger->debug("orig: $original_otu_name; copy: $copy_otu_name");
827            
828             # todo:
829             # a portion of the following code should be re-written as
830             # a stand-alone, utility method
831 7         14 my $contains_otu = "false";
832 7         13 foreach my $otu (@{ $self->get_taxlabels() }) {
  7         17  
833 24         137 $logger->debug("$otu");
834 24 100       69 if ($otu eq $original_otu_name) {
835 7         11 $contains_otu = "true";
836 7         15 last;
837             }
838             }
839              
840 7 50       26 if ($contains_otu eq "true") {
841             # otu cloning happens here:
842             # - cycle through all blocks and call add_otu_clone() method on each block
843 7         11 foreach my $block ( @{ $self->get_blocks() } ) {
  7         39  
844 26         89 $logger->debug( "> Block: " . $block->get_type() );
845 26         159 $block->add_otu_clone($original_otu_name, $copy_otu_name);
846             }
847             }
848             else {
849 0         0 throw 'BadArgs' => "the specified otu [$original_otu_name] does not exist";
850             }
851            
852             }
853              
854             =head2 select_blocks
855              
856             Name : select_blocks
857             Usage : $nexus->select_blocks(\@blocknames);
858             Function: select a subset of blocks
859             Returns : a new nexus object
860             Args : a ref to array of block names to be selected
861              
862             =cut
863              
864             sub select_blocks {
865 0     0 1 0 my ( $self, $blocknames ) = @_;
866 0         0 my $nexus = __PACKAGE__->new();
867 0         0 for my $blockname (@$blocknames) {
868 0         0 $nexus->add_block( $self->get_block($blockname) );
869             }
870 0         0 return $nexus;
871             }
872              
873             =head2 exclude_blocks
874              
875             Name : exclude_blocks
876             Usage : $nexus->exclude_blocks(\@blocknames);
877             Function: remove a subset of blocks
878             Returns : a new nexus object
879             Args : a ref to array of block names to be removed
880              
881             =cut
882              
883             sub exclude_blocks {
884 0     0 1 0 my ( $self, $blocknames ) = @_;
885 0         0 my $nexus = $self->clone();
886 0         0 for my $blockname (@$blocknames) {
887 0         0 $nexus->remove_block($blockname);
888             }
889 0         0 return $nexus;
890             }
891              
892             =head2 select_otus
893              
894             Name : select_otus
895             Usage : $nexus->select_otus(\@otunames);
896             Function: select a subset of OTUs
897             Returns : a new nexus object
898             Args : a ref to array of OTU names
899              
900             =cut
901              
902             sub select_otus {
903 1     1 1 3 my ( $self, $otunames ) = @_;
904 1         8 my $nexus = $self->clone();
905              
906 1         2 for my $block ( @{ $nexus->get_blocks() } ) {
  1         3  
907             # XXX duck-typing probably okay, no? -- RAV
908             #if ( $block->get_type() =~ /^(?:characters|taxa|sets|span|history)$/i )
909 2 50       29 if ( $block->can('select_otus') ) {
910 2         10 $block->select_otus($otunames);
911             }
912             }
913 1         5 return $nexus;
914             }
915              
916             =head2 exclude_otus
917              
918             Name : exclude_otus
919             Usage : $nexus->exclude_otus(\@otunames);
920             Function: remove a subset of OTUs
921             Returns : a new nexus object
922             Args : a ref to array of OTU names to be removed
923              
924             =cut
925              
926             sub exclude_otus {
927 1     1 1 9 my ( $self, $otus ) = @_;
928 1         2 my @OTUs;
929 1         2 for my $otu ( @{ $self->get_otus() } ) {
  1         5  
930 4         8 my $exclude = 0;
931 4         6 for my $name ( @{$otus} ) {
  4         8  
932 4 100 100     27 last if ( $otu eq $name ) && ( $exclude = 1 );
933             }
934 4 100       16 push( @OTUs, $otu ) unless ($exclude);
935             }
936 1         7 return $self->select_otus( \@OTUs );
937             }
938              
939             =head2 select_tree
940              
941             Name : select_tree
942             Usage : $nexus->select_tree($treename);
943             Function: select a tree
944             Returns : a new nexus object
945             Args : a tree name
946              
947             =cut
948              
949             sub select_tree {
950 0     0 1 0 my ( $self, $treename ) = @_;
951 0         0 my $nexus = $self->clone();
952 0         0 $nexus->get_block('trees')->select($treename);
953 0         0 return $nexus;
954             }
955              
956             =head2 select_subtree
957              
958             Name : select_subtree
959             Usage : $nexus->select_subtree($inodename);
960             Function: select a subtree
961             Returns : a new nexus object
962             Args : an internal node name for subtree to be selected
963              
964             =cut
965              
966             sub select_subtree {
967 0     0 1 0 my ( $self, $nodename, $treename ) = @_;
968 0 0       0 if ( not defined $nodename ) {
969 0         0 throw 'BadArgs' => 'Need to specify an internal node name for subtree';
970             }
971              
972 0         0 my $nexus = $self->clone();
973 0         0 my $treesblock = $nexus->get_block("trees");
974 0         0 $treesblock->select_subtree( $nodename, $treename );
975 0         0 my $OTUnames = $treesblock->get_taxlabels();
976 0         0 $nexus->get_block('taxa')->select_otus($OTUnames);
977              
978 0         0 for my $block ( @{ $nexus->get_blocks() } ) {
  0         0  
979             # XXX duck-typing probably okay, no? -- RAV
980             #if ( $block->get_type() =~ /^(?:characters|taxa|sets|span|history)$/i )
981 0 0       0 if ( $block->can('select_otus') ) {
982 0         0 $block->select_otus($OTUnames);
983             }
984             }
985              
986 0         0 return $nexus;
987             }
988              
989             =head2 exclude_subtree
990              
991             Name : exclude_subtree
992             Usage : $nexus->exclude_subtree($inodename);
993             Function: remove a subtree
994             Returns : a new nexus object
995             Args : an internal node for subtree to be removed
996              
997             =cut
998              
999             sub exclude_subtree {
1000 0     0 1 0 my ( $self, $nodename, $treename ) = @_;
1001 0 0       0 if ( not defined $nodename ) {
1002 0         0 throw 'BadArgs' => 'Need to specify an internal node name for subtree';
1003             }
1004              
1005 0         0 my $nexus = $self->clone();
1006 0         0 my $treesblock = $nexus->get_block('trees');
1007 0         0 $treesblock->exclude_subtree( $nodename, $treename );
1008 0         0 my $OTUnames = $treesblock->get_taxlabels();
1009              
1010 0         0 for my $block ( @{ $nexus->get_blocks() } ) {
  0         0  
1011             # XXX duck-typing probably okay, no? --RAV
1012             #if ( $block->get_type() =~ /^(?:characters|taxa|sets|span|history)$/i )
1013 0 0       0 if ( $block->can('select_otus') ) {
1014 0         0 $block->select_otus($OTUnames);
1015             }
1016             }
1017              
1018 0         0 return $nexus;
1019             }
1020              
1021             =head2 select_chars
1022              
1023             Name : select_chars
1024             Usage : $nexus->select_chars(\@columns);
1025             Function: select a subset of characters
1026             Returns : a new nexus object
1027             Args : a ref to array of character columns
1028              
1029             =cut
1030              
1031             sub select_chars {
1032 0     0 1 0 my ( $self, $columns, $title ) = @_;
1033 0         0 my @labels = ();
1034 0         0 my $nexus = $self->clone();
1035 0         0 my $block = $nexus->get_block( "characters", $title );
1036 0         0 $block->select_columns($columns);
1037              
1038             #
1039             # temp change by arlin
1040             # to do this right, we need to separate two systems, column numbers (index + 1)
1041             # and column labels. Default should be to select labels if they exist, and
1042             # otherwise to assigning old column numbers as new labels. An alternative
1043             # to the default would be to leave the new column labels unset (i.e., ignore
1044             # previous labels or numbers).
1045             # print &Dumper($columns);exit;
1046 0         0 for my $i ( 0 .. $#{ $columns } ) {
  0         0  
1047 0         0 $labels[$i] = $$columns[$i] + 1;
1048             }
1049              
1050             # use these to set labels
1051 0 0 0     0 if ( !$block->get_charlabels || @{ $block->get_charlabels } == 0 ) {
  0         0  
1052 0         0 $block->set_charlabels( \@labels );
1053             }
1054 0         0 $block = $nexus->get_block("assumptions");
1055 0 0       0 if ($block) {
1056 0         0 $block->select_assumptions($columns);
1057             }
1058              
1059 0         0 return $nexus;
1060             }
1061              
1062             =head2 exclude_chars
1063              
1064             Name : exclude_chars
1065             Usage : $nexus->exclude_chars($columns,block_type);
1066             Function: exclude specified columns from a block.
1067             Returns : new nexus object
1068             Args : column numbers to exclude as array reference, block_type as string
1069              
1070             =cut
1071              
1072             sub exclude_chars {
1073 0     0 1 0 my ( $self, $columns, $title ) = @_;
1074 0         0 my $block = $self->get_block( "characters", $title );
1075              
1076 0         0 my $len = $block->get_dimensions()->{'nchar'};
1077 0         0 print "$len\n";
1078 0         0 my @columns = ( -1, @{$columns}, $len );
  0         0  
1079 0         0 my @select = ();
1080 0         0 for my $i ( 0 .. $#columns ) {
1081 0         0 for ( my $j = $columns[$i] + 1; $j < $columns[ $i + 1 ]; $j++ ) {
1082 0         0 push @select, $j;
1083             }
1084             }
1085 0         0 print "@select\n";
1086 0         0 return $self->select_chars( \@select, $title );
1087             }
1088              
1089             =head2 reroot
1090              
1091             Name : reroot
1092             Usage : $nexus->reroot($outgroupname);
1093             Function: reroot the tree using the new outgroup
1094             Returns : a new nexus object
1095             Args : a OTU name as new outgroup
1096              
1097             =cut
1098              
1099             sub reroot {
1100 0     0 1 0 my ( $self, $outgroup, $root_position, $treename ) = @_;
1101 0         0 my $nexus = $self->clone();
1102 0         0 my $trees = $nexus->get_block('trees');
1103 0 0       0 if ( defined $treename ) {
1104 0         0 $trees->reroot_tree( $outgroup, $root_position, $treename );
1105             }
1106             else {
1107 0         0 $trees->reroot_all_trees( $outgroup, $root_position );
1108             }
1109 0         0 return $nexus;
1110             }
1111              
1112             =head2 equals
1113              
1114             Name : equals
1115             Usage : $nexus->equals($another);
1116             Function: compare if two Bio::NEXUS objects are equal
1117             Returns : boolean
1118             Args : a Bio::NEXUS object
1119              
1120             =cut
1121              
1122             sub equals {
1123 1     1 1 3 my ( $self, $nexus ) = @_;
1124 1         2 my @blocks1 = @{ $self->get_blocks() };
  1         5  
1125 1         3 my @blocks2 = @{ $nexus->get_blocks() };
  1         3  
1126 1 50       6 if ( @blocks1 != @blocks2 ) { return 0; }
  0         0  
1127 3   50     13 @blocks1 = sort {
      50        
1128 1         5 $a->get_type()
1129             . ( $a->get_title || '' ) cmp $b->get_type()
1130             . ( $b->get_title || '' )
1131             } @blocks1;
1132 3   50     9 @blocks2 = sort {
      50        
1133 1         3 $a->get_type()
1134             . ( $a->get_title || '' ) cmp $b->get_type()
1135             . ( $b->get_title || '' )
1136             } @blocks2;
1137              
1138 1         5 for ( my $i = 0; $i < @blocks1; $i++ ) {
1139 3 0 33     8 if ( ( !$blocks1[$i] ) && ( !$blocks2[$i] ) ) { next; }
  0         0  
1140 3 50       17 if ( !$blocks1[$i]->equals( $blocks2[$i] ) ) {
1141              
1142             # print &Dumper($blocks1[$i]);
1143             # print &Dumper($blocks2[$i]);
1144 0         0 return 0;
1145             }
1146             }
1147 1         13 return 1;
1148             }
1149              
1150             =head2 write
1151              
1152             Name : write
1153             Usage : $nexus->write($filename, $verbose);
1154             Function: Writes to NEXUS file from stored NEXUS data
1155             Returns : none
1156             Args : file name (string) for output to file or '-' or 'STDOUT' for standard output
1157              
1158             =cut
1159              
1160             sub write {
1161 2     2 1 2453 my ( $self, $filename, $verbose ) = @_;
1162 2         3 my $fh;
1163              
1164 2 100 33     14 if ( ref($filename) eq "GLOB" ) {
    50          
1165 1         3 $fh = $filename;
1166             }
1167             elsif ( $filename eq "-" || $filename eq \*STDOUT ) {
1168 0         0 $fh = \*STDOUT;
1169             }
1170             else {
1171 1 50       148 open( $fh, ">$filename" ) || throw 'FileError' => $!;
1172             }
1173              
1174 2         48 print $fh "#NEXUS\n\n";
1175              
1176 2         3 my @blocks_and_comments = @{ $self->get_blocks_and_comments() };
  2         5  
1177              
1178             # First, print any comments that are at the top level
1179 2         7 for ( my $i = 0; $i < @blocks_and_comments; $i++ ) {
1180 4 100       11 if ( _is_comment( $blocks_and_comments[$i] ) ) {
1181 2         7 print $fh "$blocks_and_comments[$i]\n\n";
1182 2         3 shift @blocks_and_comments;
1183 2         3 $i--;
1184 2         5 next;
1185             }
1186             else {
1187 2         3 last;
1188             }
1189             }
1190              
1191             # Then print the TAXA Block
1192 2         8 $self->set_taxablock;
1193 2         7 $self->get_block('taxa')->_write( $fh, $verbose );
1194 2         5 print $fh "\n";
1195              
1196             # And print whatever else there is
1197 2         3 for my $block_or_comment (@blocks_and_comments) {
1198 6 50       14 if ( _is_comment($block_or_comment) ) {
1199 0         0 print $fh "$block_or_comment\n\n";
1200 0         0 next;
1201             }
1202 6         16 my $type = $block_or_comment->get_type();
1203 6 100       17 if ( lc $type eq 'taxa' ) { next; }
  2         4  
1204 4         20 $block_or_comment->_write($fh);
1205 4         9 print $fh "\n";
1206             }
1207            
1208             # if $fh is STDOUT, don't close it!
1209 2 50       138 close($fh) unless ($fh == \*STDOUT);
1210             }
1211              
1212             =head2 set_taxablock
1213              
1214             Name : set_taxablock
1215             Usage : $nexus->set_taxablock();
1216             Function: Sets taxablock if taxablock is not already defined in the nexus object
1217             Returns : none
1218             Args : none
1219              
1220             =cut
1221              
1222             sub set_taxablock {
1223 9     9 1 25 my $self = shift;
1224 9 100       33 if ( not defined $self->get_block('taxa') ) {
1225 7         14 for my $block ( @{ $self->get_blocks } ) {
  7         22  
1226 7         28 my $block_type = lc $block->get_type();
1227 7 50 66     41 if ( $block_type eq 'characters' || $block_type eq 'trees' ) {
1228 7         50 my $taxlabels = $block->get_taxlabels();
1229 7 100 33     118 if ( ( not defined $taxlabels ) or ( not @$taxlabels ) ) {
1230 1 50       5 if ( $block_type eq 'trees' ) {
1231 1         6 $block->set_taxlabels(
1232             $block->get_tree()->get_node_names() );
1233             }
1234             else {
1235 0         0 $block->set_taxlabels(
1236             $block->get_otuset->get_otu_names() );
1237             }
1238 1         4 $taxlabels = $block->get_taxlabels();
1239             }
1240 7         79 my $taxa_block = new Bio::NEXUS::TaxaBlock('taxa');
1241 7         80 $taxa_block->set_taxlabels($taxlabels);
1242 7         23 $self->add_block($taxa_block);
1243 7         19 return;
1244             }
1245             }
1246             }
1247             }
1248              
1249             sub AUTOLOAD {
1250 0 0   0     return if $AUTOLOAD =~ /DESTROY$/;
1251 0           my $package_name = __PACKAGE__ . '::';
1252              
1253             # The following methods are deprecated and are temporarily supported
1254             # via a warning and a redirection
1255 0           my %synonym_for =
1256             ( "${package_name}is_comment" => 'Bio::NEXUS::Functions::_is_comment',
1257             );
1258              
1259 0 0         if ( defined $synonym_for{$AUTOLOAD} ) {
1260 0           $logger->warn("$AUTOLOAD() is deprecated; use $synonym_for{$AUTOLOAD}() instead");
1261 0           goto &{ $synonym_for{$AUTOLOAD} };
  0            
1262             }
1263             else {
1264 0           throw 'UnknownMethod' => "Unknown method $AUTOLOAD called";
1265             }
1266             }
1267              
1268             1;