File Coverage

blib/lib/Bio/NEXUS/UnalignedBlock.pm
Criterion Covered Total %
statement 91 163 55.8
branch 19 54 35.1
condition 1 12 8.3
subroutine 13 24 54.1
pod 11 11 100.0
total 135 264 51.1


line stmt bran cond sub pod time code
1             #######################################################################
2             # UnalignedBlock.pm
3             #######################################################################
4             #
5             # thanks to Tom Hladish for the original version
6             #
7             # $Id: UnalignedBlock.pm,v 1.25 2012/02/10 13:28:28 astoltzfus Exp $
8              
9             #################### START POD DOCUMENTATION ##########################
10              
11             =head1 NAME
12              
13             Bio::NEXUS::UnalignedBlock - Represents an UNALIGNED block of a NEXUS file
14              
15             =head1 SYNOPSIS
16              
17             if ( $type =~ /unaligned/i ) {
18             $block_object = new Bio::NEXUS::UnalignedBlock($type, $block, $verbose);
19             }
20              
21             =head1 DESCRIPTION
22              
23             This is a class representing an unaligned block in NEXUS file
24              
25             =head1 FEEDBACK
26              
27             All feedback (bugs, feature enhancements, etc.) is greatly appreciated.
28              
29             =head1 AUTHORS
30              
31             Thomas Hladish (tjhladish at yahoo)
32              
33             =head1 VERSION
34              
35             $Id: UnalignedBlock.pm,v 1.25 2012/02/10 13:28:28 astoltzfus Exp $
36              
37             =head1 METHODS
38              
39             =cut
40              
41             package Bio::NEXUS::UnalignedBlock;
42              
43 34     34   446 use strict;
  34         71  
  34         1754  
44             #use Data::Dumper; # XXX this is not used, might as well not import it!
45             #use Carp;# XXX this is not used, might as well not import it!
46 34     34   202 use Bio::NEXUS::Functions;
  34         74  
  34         9251  
47 34     34   226 use Bio::NEXUS::TaxUnitSet;
  34         76  
  34         825  
48 34     34   263 use Bio::NEXUS::Matrix;
  34         72  
  34         738  
49 34     34   177 use Bio::NEXUS::Util::Exceptions;
  34         85  
  34         8638  
50 34     34   304 use vars qw(@ISA $VERSION $AUTOLOAD);
  34         82  
  34         2939  
51 34     34   193 use Bio::NEXUS; $VERSION = $Bio::NEXUS::VERSION;
  34         85  
  34         102432  
52             @ISA = qw(Bio::NEXUS::Matrix);
53             my $logger = Bio::NEXUS::Util::Logger->new();
54              
55             =head2 new
56              
57             Title : new
58             Usage : block_object = new Bio::NEXUS::UnalignedBlock($block_type, $commands, $verbose, $taxlabels);
59             Function: Creates a new Bio::NEXUS::UnalignedBlock object
60             Returns : Bio::NEXUS::UnalignedBlock object
61             Args : type (string), the commands/comments to parse (array ref), and a verbose flag (0 or 1)
62              
63             =cut
64              
65             sub new {
66 2     2 1 6 my ( $class, $type, $commands, $verbose, $taxa ) = @_;
67 2 50       9 unless ($type) { ( $type = lc $class ) =~ s/Bio::NEXUS::(.+)Block/$1/i; }
  0         0  
68 2         10 my $self = { type => $type };
69 2         6 bless $self, $class;
70 2         26 $self->set_taxlabels($taxa);
71 2         18 $self->{'otuset'} = new Bio::NEXUS::TaxUnitSet();
72 2 50 33     43 $self->_parse_block( $commands, $verbose )
73             if ( ( defined $commands ) and @$commands );
74 1         3 return $self;
75             }
76              
77             =begin comment
78              
79             Title : _parse_format
80             Usage : $format = $self->_parse_format($buffer); (private)
81             Function: Extracts format values from line and stores values in a hash
82             Returns : hash of formats
83             Args : buffer (string)
84             Methods : Separates formats by whitespace and creates hash containing
85             key = format name and value = format value.
86              
87             =end comment
88              
89             =cut
90              
91             sub _parse_format {
92 1     1   3 my ( $self, $string ) = @_;
93              
94 1         3 my %format = ();
95              
96 1         428 while ( $string =~ s/(\S+\s*=\s*[\"|\'][^\"\']+[\"|\'])// ) {
97 0         0 my ( $name, $symbol ) = split /\s*=\s*/, $1;
98 0         0 $format{ lc $name } = $symbol;
99             }
100 1         12 while ( $string =~ s/(\S+\s*=\s*\S+)// ) {
101 1         9 my ( $name, $symbol ) = split /\s*=\s*/, $1;
102 1         6 $format{ lc $name } = lc $symbol;
103             }
104 1         5 for my $other ( split /\s+/, $string ) {
105 0 0       0 if ($other) { $format{ lc $other } = 1; }
  0         0  
106             }
107 1         5 return \%format;
108             }
109              
110             =begin comment
111              
112             Title : _parse_matrix
113             Usage : $self->_parse_matrix($buffer); (private)
114             Function: Processes buffer containing matrix data
115             Returns : none
116             Args : buffer (string)
117             Method : parse according to if name is quoted string or single word,
118             if each state is single character or multi-character (use token keyword)
119              
120             =end comment
121              
122             =cut
123              
124             sub _parse_matrix {
125 1     1   3 my ( $self, $matrix, $verbose ) = @_;
126              
127 1         2 my @taxa;
128 1         2 my ( $name, $seq ) = ();
129              
130             # Build an array of hashrefs, where each hash has "name" and "seq" values
131             # corresponding to the name and sequence found in each row of the matrix
132 1         10 for my $row ( split /\n|\r/, $matrix ) {
133 4 50       12 if ( $row =~ /^\s*$/ ) { next; }
  0         0  
134            
135             #for quoted taxon name
136 4 50       10 if ( $row =~ /^\s*[\"|\']([^\"\']+)[\"|\']\s*([^\[]*)(\[.*\]\s*)*/ ) {
137 0         0 ( $name, $seq ) = ( $1, $2 );
138 0         0 $name =~ s/\s+/_/g;
139 0 0       0 if ( !$self->find_taxon($name) ) {
140 0         0 Bio::NEXUS::Util::Exceptions::BadArgs->throw(
141             'error' => "Undefined Taxon: $name"
142             );
143             }
144             }
145             else {
146              
147             # for one-word non-quoted taxon name
148 4         12 $row =~ /^\s*(\S+)(\s*)([^\[]*)(\[.*\]\s*)*/;
149 4 50       11 if ( $self->find_taxon($1) ) {
150 4         8 $name = $1;
151 4         7 $seq = $3;
152             #print Dumper $seq;
153             }
154             else {
155 0 0       0 print "taxon name $1 not found\n" if $verbose;
156 0         0 $seq = $1 . $2 . $3;
157             }
158             }
159             #print "> row: $row\n";
160             #print "> name: $name\n";
161             #print "> seq: $seq\n";
162            
163 4         6 my $newtaxon = 1;
164 4         5 for my $taxon (@taxa) {
165 6 50       18 if ( $taxon->{'name'} eq $name ) {
166 0         0 $taxon->{'seq'} .= ' ' . $seq;
167 0         0 $newtaxon = 0;
168             }
169             }
170 4 50       13 if ($newtaxon) {
171 4         16 push @taxa, { name => $name, seq => $seq };
172             }
173             }
174             #print '> @taxa: ';
175             # split each character
176 1         2 my @otus;
177             #print Dumper \@taxa;
178 1         3 for my $taxon (@taxa) {
179 4         8 $seq = $taxon->{'seq'};
180 4         17 $seq =~ s/^\s*(.*\S)\s*$/$1/;
181              
182 4         6 my @seq;
183 4         16 while ( $seq =~ s/([^\(]+)|\(([^\(]+)\)// ) { # for +-(+ -)+-
184 4 50       11 if ($1) { # for +-
    0          
185             ### The following 4 commented lines of code are implemented in CharactersBlock.pm; they allow data tokens to be space-delimited.
186             ### Unaligned blocks do not include the tokens or continuous formats according the Maddison et al. We
187             ### may decide that we don't want to restrict unaligned data to DNA/RNA/AA the way Maddison et al have.
188             # if ($self->get_format->{'tokens'} || lc $self->get_format->{'datatype'} eq 'continuous') { #LINE 1
189             # push @seq, split /\s+/, $1; #LINE 2
190             # } else { #LINE 3
191 4         57 push @seq, split /\s*/, $1;
192              
193             # } #LINE4
194             }
195             elsif ($2) {
196 0         0 push @seq, [ split /,\s*|\s+/, $2 ]; # for (+ -)
197             }
198             }
199              
200 4         18 push @otus, Bio::NEXUS::TaxUnit->new( $taxon->{'name'}, \@seq );
201             }
202            
203 1         8 my $otuset = $self->get_otuset();
204 1         6 $otuset->set_otus( \@otus );
205 1         5 $self->set_taxlabels( $otuset->get_otu_names() );
206 1         8 return \@otus;
207             }
208              
209             =head2 find_taxon
210              
211             Title : find_taxon
212             Usage : my $is_taxon_present = $self->find_taxon($taxon_name);
213             Function: Finds whether the input taxon name is present in the taxon label.
214             Returns : 0 (not present) or 1 (if present).
215             Args : taxon label (as string)
216              
217             =cut
218              
219             sub find_taxon {
220 7     7 1 17 my ( $self, $name ) = @_;
221 7 50       8 if ( @{ $self->get_taxlabels || [] } == 0 ) { return 1; }
  7 50       22  
  0         0  
222 7         9 for my $taxon ( @{ $self->get_taxlabels() } ) {
  7         20  
223 21 100       48 if ( lc $taxon eq lc $name ) { return 1; }
  5         18  
224             }
225 2         12 return 0;
226             }
227              
228             =head2 set_format
229              
230             Title : set_format
231             Usage : $block->set_format(\%format);
232             Function: set the format of the characters
233             Returns : none
234             Args : hash of format values
235              
236             =cut
237              
238             sub set_format {
239 0     0 1 0 my ( $self, $format ) = @_;
240 0         0 $self->{'format'} = $format;
241             }
242              
243             =head2 get_format
244              
245             Title : get_format
246             Usage : $block->get_format();
247             Function: Returns the format of the characters
248             Returns : hash of format values
249             Args : none
250              
251             =cut
252              
253 0 0   0 1 0 sub get_format { shift->{'format'} || {} }
254              
255             =head2 set_otuset
256              
257             Title : set_otuset
258             Usage : $block->set_otuset($otuset);
259             Function: Set the otus
260             Returns : none
261             Args : TaxUnitSet object
262              
263             =cut
264              
265             sub set_otuset {
266 0     0 1 0 my ( $self, $otuset ) = @_;
267 0         0 $self->{'otuset'} = $otuset;
268 0         0 $self->set_taxlabels( $otuset->get_otu_names() );
269             }
270              
271             =head2 set_charstatelabels
272              
273             Title : set_charstatelabels
274             Usage : $block->set_charstatelabels($labels);
275             Function: Set the character names and states
276             Returns : none
277             Args : array of character states
278              
279             =cut
280              
281             sub set_charstatelabels {
282 0     0 1 0 my ( $self, $charstatelabels ) = @_;
283 0         0 $self->get_otuset->set_charstatelabels($charstatelabels);
284             }
285              
286             =head2 get_charstatelabels
287              
288             Title : get_charstatelabels
289             Usage : $set->get_charstatelabels();
290             Function: Returns an array of character states
291             Returns : character states
292             Args : none
293              
294             =cut
295              
296             sub get_charstatelabels {
297 0     0 1 0 my ($self) = @_;
298 0         0 return $self->get_otuset->get_charstatelabels();
299             }
300              
301             =head2 get_ntax
302              
303             Title : get_ntax
304             Usage : $block->get_ntax();
305             Function: Returns the number of taxa of the block
306             Returns : # taxa
307             Args : none
308              
309             =cut
310              
311             sub get_ntax {
312 0     0 1 0 my $self = shift;
313 0         0 return $self->get_otuset()->get_ntax();
314             }
315              
316             =head2 rename_otus
317              
318             Title : rename_otus
319             Usage : $block->rename_otus(\%translation);
320             Function: Renames all the OTUs to something else
321             Returns : none
322             Args : hash containing translation
323              
324             =cut
325              
326             sub rename_otus {
327 0     0 1 0 my ( $self, $translation ) = @_;
328 0         0 $self->get_otuset()->rename_otus($translation);
329             }
330              
331             =head2 add_otu_clone
332              
333             Title : add_otu_clone
334             Usage : ...
335             Function: ...
336             Returns : ...
337             Args : ...
338              
339             =cut
340              
341             sub add_otu_clone {
342 1     1 1 4 my ( $self, $original_otu_name, $copy_otu_name ) = @_;
343             # print "Warning: Bio::NEXUS::UnalignedBlock::add_otu_clone() method not fully implemented\n";
344            
345 1 50       5 if ($self->find_taxon($copy_otu_name)) {
346 0         0 print "Error: an OTU with that name [$copy_otu_name] already exists.\n";
347             }
348             else {
349 1         12 $self->add_taxlabel($copy_otu_name);
350             }
351            
352 1         4 my @otu_set = ();
353 1 50       7 if (defined $self->{'otuset'}->{'otus'}) {
354 1         2 @otu_set = @{ $self->{'otuset'}->{'otus'} };
  1         4  
355             }
356 1         3 foreach my $otu (@otu_set) {
357 4 50       10 if (defined $otu) {
358 4 100       14 if ($otu->get_name() eq $original_otu_name) {
359 1         7 my $otu_clone = $otu->clone();
360 1         6 $otu_clone->set_name($copy_otu_name);
361 1         11 $self->{'otuset'}->add_otu($otu_clone);
362             }
363             }
364             }
365            
366             }
367              
368             =head2 equals
369              
370             Name : equals
371             Usage : $block->equals($another);
372             Function: compare if two Bio::NEXUS::UnalignedBlock objects are equal
373             Returns : boolean
374             Args : a Bio::NEXUS::CharactersBlock object
375              
376             =cut
377              
378             sub equals {
379 0     0 1 0 my ( $self, $block ) = @_;
380 0 0       0 if ( !Bio::NEXUS::Block::equals( $self, $block ) ) { return 0; }
  0         0  
381 0         0 return $self->get_otuset()->equals( $block->get_otuset() );
382             }
383              
384             =begin comment
385              
386             Name : _write
387             Usage : $block->_write();
388             Function: Writes NEXUS block containing unaligned data
389             Returns : none
390             Args : file name (string)
391              
392             =end comment
393              
394             =cut
395              
396             sub _write {
397 0     0   0 my ( $self, $fh, $verbose ) = @_;
398 0   0     0 $fh ||= \*STDOUT;
399              
400 0         0 Bio::NEXUS::Block::_write( $self, $fh );
401 0         0 $self->_write_matrix_info( $fh, $verbose );
402 0         0 $self->_write_matrix( $fh, $verbose );
403 0         0 print $fh "END;\n";
404 0         0 return;
405             }
406              
407             =begin comment
408              
409             Name : _write_matrix_info
410             Usage : $self->_write_matrix_info($file_handle,$verbose);
411             Function: Writes UnalignedBlock info (all the block content except the matrix data) into the filehandle
412             Returns : none
413             Args : $file_handle and $verbose
414              
415             =end comment
416              
417             =cut
418              
419             sub _write_matrix_info {
420 0     0   0 my ( $self, $fh, $verbose ) = @_;
421 0   0     0 $fh ||= \*STDOUT;
422              
423 0         0 my $ntax = $self->get_ntax();
424 0         0 print $fh "\tDIMENSIONS ntax=$ntax;\n";
425              
426 0         0 my %formats = %{ $self->get_format() };
  0         0  
427 0 0       0 if ( scalar keys %formats ) {
428 0         0 print $fh "\tFORMAT ";
429 0 0       0 if ( defined $formats{'datatype'} ) {
430 0         0 print $fh " datatype=$formats{'datatype'}";
431             }
432              
433 0         0 for my $format ( keys %formats ) {
434 0 0 0     0 if ( !$formats{$format} || $format =~ /datatype/i ) { next; }
  0 0       0  
435             elsif ( $formats{$format} eq '1' ) {
436 0         0 print $fh " $format";
437             }
438             else {
439 0         0 print $fh " $format=$formats{$format}";
440             }
441             }
442 0         0 print $fh ";\n";
443             }
444 0         0 return;
445             }
446              
447             =begin comment
448              
449             Name : _write_matrix
450             Usage : $self->_write_matrix($file_handle,$verbose);
451             Function: Writes UnalignedBlock matrix( The data stored in the matrix command) into the filehandle
452             Returns : none
453             Args : $file_handle and $verbose
454              
455             =end comment
456              
457             =cut
458              
459             sub _write_matrix {
460 0     0   0 my ( $self, $fh, $verbose ) = @_;
461 0   0     0 $fh ||= \*STDOUT;
462              
463 0         0 my @otus = @{ $self->get_otuset()->get_otus() };
  0         0  
464 0         0 print $fh "\tMATRIX\n";
465 0         0 for my $otu (@otus) {
466 0         0 my $seq = $otu->get_seq_string();
467 0         0 print $fh "\t", $otu->get_name(), "\t", $seq, "\n";
468             }
469 0         0 print $fh "\t;\n";
470 0         0 return;
471             }
472              
473             sub AUTOLOAD {
474 1 50   1   4 return if $AUTOLOAD =~ /DESTROY$/;
475 1         3 my $package_name = __PACKAGE__ . '::';
476              
477             # The following methods are deprecated and are temporarily supported
478             # via a warning and a redirection
479 1         7 my %synonym_for = (
480             "${package_name}set_charstates" => "${package_name}set_charstatelabels",
481             "${package_name}get_charstates" => "${package_name}get_charstatelabels",
482             );
483              
484 1 50       4 if ( defined $synonym_for{$AUTOLOAD} ) {
485 0         0 $logger->warn( "$AUTOLOAD() is deprecated; use $synonym_for{$AUTOLOAD}() instead" );
486 0         0 goto &{ $synonym_for{$AUTOLOAD} };
  0         0  
487             }
488             else {
489 1         7 Bio::NEXUS::Util::Exceptions::UnknownMethod->throw(
490             'error' => "ERROR: Unknown method $AUTOLOAD called"
491             );
492             }
493             }
494              
495             1;