File Coverage

blib/lib/Bio/NEXUS/AssumptionsBlock.pm
Criterion Covered Total %
statement 169 199 84.9
branch 53 74 71.6
condition 11 19 57.8
subroutine 23 24 95.8
pod 10 10 100.0
total 266 326 81.6


line stmt bran cond sub pod time code
1             ######################################################
2             # AssumptionsBlock.pm
3             ######################################################
4             # Author: Chengzhi Liang, Weigang Qiu, Eugene Melamud, Peter Yang, Thomas Hladish
5             # $Id: AssumptionsBlock.pm,v 1.51 2012/02/07 21:38:09 astoltzfus Exp $
6              
7             #################### START POD DOCUMENTATION ##################
8              
9             =head1 NAME
10              
11             Bio::NEXUS::AssumptionsBlock - Represents ASSUMPTIONS block of a NEXUS file
12              
13             =head1 SYNOPSIS
14              
15             if ( $type =~ /assumptions/i ) {
16             $block_object = new Bio::NEXUS::AssumptionsBlock($block_type, $block, $verbose);
17             }
18              
19             =head1 DESCRIPTION
20              
21             If a NEXUS block is an assumptions block, this module parses the block and stores the assumptions data. Currently this only works with SOAP weight data, but we hope to extend its functionality.
22              
23             =head1 FEEDBACK
24              
25             All feedback (bugs, feature enhancements, etc.) are greatly appreciated.
26              
27             =head1 AUTHORS
28              
29             Chengzhi Liang (liangc@umbi.umd.edu)
30             Weigang Qiu (weigang@genectr.hunter.cuny.edu)
31             Eugene Melamud (melamud@carb.nist.gov)
32             Peter Yang (pyang@rice.edu)
33             Thomas Hladish (tjhladish at yahoo)
34              
35             =head1 VERSION
36              
37             $Revision: 1.51 $
38              
39             =head1 METHODS
40              
41             =cut
42              
43             package Bio::NEXUS::AssumptionsBlock;
44              
45 34     34   195 use strict;
  34         66  
  34         1474  
46 34     34   1470 use Bio::NEXUS::Functions;
  34         94  
  34         7039  
47 34     34   27576 use Bio::NEXUS::Block;
  34         112  
  34         1147  
48 34     34   24809 use Bio::NEXUS::WeightSet;
  34         100  
  34         1149  
49 34     34   214 use Bio::NEXUS::Util::Logger;
  34         69  
  34         800  
50 34     34   172 use Bio::NEXUS::Util::Exceptions 'throw';
  34         71  
  34         1992  
51 34     34   178 use vars qw(@ISA $AUTOLOAD $VERSION);
  34         73  
  34         2252  
52 34     34   198 use Bio::NEXUS; $VERSION = $Bio::NEXUS::VERSION;
  34         67  
  34         99343  
53              
54             @ISA = qw(Bio::NEXUS::Block);
55             my $logger = Bio::NEXUS::Util::Logger->new();
56              
57             =head2 new
58              
59             Title : new
60             Usage : block_object = new Bio::NEXUS::AssumptionsBlock($block_type, $commands, $verbose );
61             Function: Creates a new Bio::NEXUS::AssumptionsBlock object
62             Returns : Bio::NEXUS::AssumptionsBlock object
63             Args : type (string), the commands/comments to parse (array ref), and a verbose flag (0 or 1; optional)
64              
65             =cut
66              
67             sub new {
68 14     14 1 1299 my ( $class, $type, $commands, $verbose ) = @_;
69 14 100       59 if ( not $type ) {
70 2         14 ( $type = lc $class ) =~ s/Bio::NEXUS::(.+)Block/$1/i;
71             }
72 14         82 my $self = {
73             'type' => $type,
74             'assumptions' => [],
75             'options' => undef
76             };
77 14         47 bless $self, $class;
78 14 100 66     160 if ( ( defined $commands ) and @$commands ) {
79 12         116 $self->_parse_block( $commands, $verbose );
80             }
81 13         46 return $self;
82             }
83              
84             =begin comment
85              
86             Title : _parse_wtset
87             Usage : $self->_parse_wtset($buffer); (private)
88             Function: Processes the buffer containing weights data
89             Returns : name and array of weights
90             Args : the buffer to parse (string)
91             Method : Creates a Bio::NEXUS::WeightSet object and sets the name and list of weight values.
92             Adds the newly created WeightSet object to the set of assumptions
93             this block contains.
94              
95             =end comment
96              
97             =cut
98              
99             sub _parse_wtset {
100 7     7   20 my ( $self, $buffer ) = @_;
101 7         38 my ( $name, $weights ) = split /=/, $buffer;
102 7         44 $name =~ s/(\(.*\))//;
103 7         38 my $flags = $1;
104 7         19 my ( $type, $tokens );
105 7 50       68 $type = ( $flags =~ /vector/i ) ? 'VECTOR' : 'STANDARD';
106 7 100       35 $tokens = ( $flags =~ /notokens/i ) ? 0 : 1;
107 7         59 $name =~ s/^\s*(\S+)\s*$/$1/;
108 7         51 $weights =~ s/^\s*(\S+.*\S+)\s*$/$1/s;
109 7         17 my @weights;
110 7 100       27 if ( $tokens ) {
111 6         694 @weights = split /\s*/, $weights;
112             }
113             else {
114 1         5 @weights = split //, $weights;
115             }
116 7         47 my $is_weightset = 1;
117 7         85 my $new_weightset = Bio::NEXUS::WeightSet->new(
118             $name,
119             \@weights,
120             $is_weightset,
121             $tokens,
122             $type
123             );
124 7         42 $self->add_weightset($new_weightset);
125 7         33 return ( $name, \@weights, $is_weightset, $tokens, $type );
126             }
127              
128             =begin comment
129              
130             Title : _parse_options
131             Usage : ...
132             Function: parses the $buffer and populates the 'options' data structure; see options command in the assumptions block (Maddison p 611)
133             Returns : n/a
134             Args : $buffer (string) - the option command and its subcommands
135             Method : extracts the options and their values from the buffer.
136             Creates a hash from those data, and adds it to the Bio::NEXUS::AssumptionsBlock object.
137              
138             =end comment
139              
140             =cut
141              
142             sub _parse_options {
143 4     4   10 my ( $self, $buffer ) = @_;
144 4         23 my @mix = split( /\s+/, $buffer );
145 4         10 for my $word ( @mix ) {
146 11         39 my ( $command, $value ) = $word =~ m/^(.+?)=(.+)$/;
147 11 100       26 next if !defined $command;
148              
149             # check if the value should be converted to a 'preferred synonym'
150 5         11 $command = lc $command;
151 5         8 $value = lc $value;
152 5 50 33     194 if ( $value eq 'irrev.up' || $value eq 'irrev.dn' ) { $value = 'irrev' }
  0         0  
153 5 50 33     37 if ( $value eq 'dollo.up' || $value eq 'dollo.dn' ) { $value = 'dollo' }
  0         0  
154 5         28 $self->{'options'}->{$command} = $value;
155             }
156 4         21 $self->_validate_options($self->{'options'});
157             }
158              
159              
160             =begin comment
161              
162             Title : _validate_options
163             Usage : _validate_options($options);
164             Function: checks if the options passed conform to the Nexus file standard
165             Returns : n/a
166             Args : $options (hashref) - hash containing option-value pairs
167              
168             =end comment
169              
170             =cut
171              
172             sub _validate_options {
173 11     11   17 my ( $self, $opts ) = @_;
174 11         19 my $is_valid = 1;
175 11 100       30 if ( defined $opts ) {
176 10         15 for my $option ( keys %{ $opts } ) {
  10         40  
177 18         23 my $is_ok = 1;
178 18         32 my $value = $$opts{$option};
179 18 100       54 if ($option eq 'deftype') {
    100          
    100          
180 8 100       47 if ($value !~ m/^(unord|ord|irrev|irrev\.up|irrev\.dn|dollo|dollo\.up|dollo\.dn)$/i) {
181 2         4 $is_valid = 0;
182 2         3 $is_ok = 0;
183             }
184             }
185             elsif ($option eq 'polytcount') {
186 1 50       7 if ($value !~ m/^(maxsteps|minsteps)$/i) {
187 0         0 $is_valid = 0;
188 0         0 $is_ok = 0;
189             }
190             }
191             elsif ($option eq 'gapmode') {
192 5 100       23 if ($value !~ m/^(missing|newstate)$/i) {
193 1         3 $is_valid = 0;
194 1         3 $is_ok = 0;
195             }
196             }
197             # the option is not in the Nexus file standard
198             else {
199 4         8 $is_valid = 0;
200 4         36 $logger->info("Unknown option $option");
201             }
202 18 100       63 if ( $is_ok == 0 ) {
203 3         19 $logger->info("Unknown value ($value) for $option");
204             }
205             }
206             }
207             else {
208 1         10 $logger->warn("Missing argument 'options'");
209 1         7 return 0;
210             }
211 10         36 return $is_valid;
212             }
213              
214              
215             =head2 get_option
216              
217             Title : get_option
218             Usage : $val = $assump_block->get_option($option_type);
219             Function: Returns the value of the specified option
220             Returns : $value (string)
221             Args : $option_type (string); nexus standard permits: deftype, polytcount, gapmode
222              
223             =cut
224              
225             sub get_option {
226 9     9 1 2658 my ( $self, $option ) = @_;
227              
228 9 50       27 return undef if not defined $option;
229 9         77 $option = lc $option;
230 9 100       89 if ( $option =~ qr/^(?:deftype|polytcount|gapmode)$/ ) {
231 7 100       21 if ( defined $self->{'options'}->{$option} ) {
232 5         30 return $self->{'options'}->{$option};
233             }
234             else {
235 2         11 return undef;
236             }
237             }
238             else {
239 2 100       13 if ( defined $self->{'options'}->{$option} ) {
240 1         14 return $self->{'options'}->{$option};
241             }
242             else {
243 1         4 return undef;
244             }
245             }
246             }
247              
248             =head2 set_option
249              
250             Title : set_option
251             Usage : $assumption_block->set_option($option, $value)
252             Function: Updates/sets a particular option (DefType, PolyTCount, GapMode, etc.)
253             Returns : n/a
254             Args : $option (string) , $value (string)
255              
256             =cut
257              
258             sub set_option {
259 4     4 1 38 my ( $self, $option, $value ) = @_;
260 4 50 33     23 if ( defined $option && defined $value ) {
261 4         7 $option = lc $option;
262 4         9 $value = lc $value;
263 4         15 $self->{'options'}->{$option} = $value;
264             # validate the input
265 4         10 my $data = {$option => $value};
266 4         12 $self->_validate_options($data);
267             }
268             else {
269 0         0 $logger->warn("Missing argument(s)");
270             }
271             }
272              
273             =head2 get_all_options
274              
275             Title : get_all_options
276             Usage : $hash_ref = $assumption_block->get_all_options();
277             Function: Retrieve all the options stored in the block
278             Returns : a hash reference (key-value pair), where each 'key' is an option (subcommand) and the 'value' is the corresponding value
279             Args : none
280              
281             =cut
282              
283             sub get_all_options {
284             # note: this method returns a copy of
285             # the 'options' hash, rather thatn a
286             # reference to the original. Why?
287             # By passing a reference to the actual
288             # data structure you give the user
289             # direct access to it. And ...
290             # direct access to the objects
291             # bypasses the validation and correction
292             # which are a major part of the various
293             # 'set_' methods - not a good thing.
294 3     3 1 13 my ($self) = @_;
295              
296 3 50       10 if ( defined $self->{'options'} ) {
297 3         5 my %options;
298 3         4 for my $key ( keys %{ $self->{'options'} } ) {
  3         14  
299 9         18 my $value = $self->{'options'}->{$key};
300 9 50       21 if ( defined $value ) {
301 9         22 $options{$key} = $value;
302             }
303             }
304 3         14 $self->_validate_options(\%options);
305 3         12 return \%options;
306             }
307             else {
308 0         0 return undef;
309             }
310             }
311              
312             =head2 set_all_options
313              
314             Title : set_all_options
315             Usage : $assumption_block->set_all_options($options);
316             Function: Updates/sets options (of this assumptions block) and their values
317             Returns : n/a
318             Args : $options (hashref) {'option' => 'value', ... }
319              
320             =cut
321              
322             sub set_all_options {
323 2     2 1 824 my ( $self, $options ) = @_;
324 2 50       7 if ( defined $options ) {
325 2         3 for my $key ( keys %{$options} ) {
  2         8  
326 6         10 my $value = $$options{$key};
327 6         24 $self->{'options'}->{ lc $key } = lc $value;
328             }
329             }
330             else {
331 0         0 $logger->warn("Missing argument(s)");
332             }
333             }
334              
335             =head2 add_weightset
336              
337             Title : add_weightset
338             Usage : $block->add_weightset(weightset);
339             Function: add a weightset to this assumption block
340             Returns : none
341             Args : WeightSet object
342              
343             =cut
344              
345             sub add_weightset {
346 7     7 1 21 my ( $self, $weight ) = @_;
347 7         12 push @{ $self->{'assumptions'} }, $weight;
  7         24  
348             }
349              
350             =head2 get_assumptions
351              
352             Title : get_assumptions
353             Usage : $block->get_assumptions();
354             Function: Gets the list of assumptions (Bio::NEXUS::WeightSet objects) and returns it
355             Returns : ref to array of Bio::NEXUS::WeightSet objects
356             Args : none
357              
358             =cut
359              
360 14 50   14 1 3755 sub get_assumptions { shift->{'assumptions'} || [] }
361              
362             =head2 select_assumptions
363              
364             Title : select_assumptions
365             Usage : $block->select_assumptions($columns);
366             Function: select assumptions (Bio::NEXUS::WeightSet objects) for a set of characters (columns)
367             Returns : none
368             Args : column numbers for the set of characters to be selected
369              
370             =cut
371              
372             sub select_assumptions {
373 0     0 1 0 my ( $self, $columns ) = @_;
374 0 0       0 if ( !$self->get_assumptions() ) { return; }
  0         0  
375 0         0 my @assump = @{ $self->get_assumptions() };
  0         0  
376 0         0 for my $assump (@assump) {
377 0         0 $assump->select_weights($columns);
378             }
379             }
380              
381             =head2 add_otu_clone
382              
383             Title : add_otu_clone
384             Usage : ...
385             Function: ...
386             Returns : ...
387             Args : ...
388              
389             =cut
390              
391             sub add_otu_clone {
392 1     1 1 3 my ( $self, $original_otu_name, $copy_otu_name ) = @_;
393 1         8 $logger->warn("Bio::NEXUS::AssumptionsBlock::add_otu_clone() method not fully implemented");
394              
395             }
396              
397             =head2 equals
398              
399             Name : equals
400             Usage : $assump->equals($another);
401             Function: compare if two Bio::NEXUS::AssumptionsBlock objects are equal
402             Returns : boolean
403             Args : a Bio::NEXUS::AssumptionsBlock object
404              
405             =cut
406              
407             sub equals {
408 3     3 1 11 my ( $self, $block ) = @_;
409 3 100       19 if ( ! $self->SUPER::equals($block) ) {
410 1         6 return 0;
411             }
412 2         4 my @weightset1 = @{ $self->get_assumptions() };
  2         6  
413 2         3 my @weightset2 = @{ $block->get_assumptions() };
  2         5  
414 2 50       6 if ( @weightset1 != @weightset2 ) {
415 0         0 return 0;
416             }
417             # XXX Schwartzian transforms
418             @weightset1 =
419 0         0 map { $_->[0] }
  0         0  
420 0         0 sort { $a->[1] cmp $b->[1] }
421 2         5 map { [ $_, $_->get_name() ] } @weightset1;
422 0         0 @weightset2 =
423 0         0 map { $_->[0] }
424 0         0 sort { $a->[1] cmp $b->[1] }
425 2         5 map { [ $_, $_->get_name() ] } @weightset2;
426 2         13 for my $i ( 0 .. $#weightset1 ) {
427 0 0       0 if ( !$weightset1[$i]->equals( $weightset2[$i] ) ) {
428 0         0 return 0;
429             }
430             }
431 2         10 return 1;
432             }
433              
434             =begin comment
435              
436             Name : _write_options
437             Usage : $assump->_write_options($filehandle, $verbose);
438             Function: Writes 'options' command
439             Returns : none
440             Args : $fh - (filehandle) output target; if undefined, STDOUT will be used
441              
442             =end comment
443              
444             =cut
445              
446             sub _write_options {
447 4     4   9 my ( $self, $fh, $verbose ) = @_;
448 4   50     9 $fh ||= \*STDOUT;
449 4         7 my $return_val = "";
450 4         6 for my $option ( keys %{ $self->{'options'} } ) {
  4         13  
451 5         10 my $value = $self->{'options'}->{$option};
452 5 100 100     25 if ( defined $value && ( $value ne "" ) ) {
453 3         11 $return_val .= " " . $option . "=" . $value;
454             }
455             }
456 4 100       15 if ( $return_val ne "" ) {
457 2         5 $return_val = "Options" . $return_val . ";";
458 2         14 print $fh $return_val, "\n";
459             }
460             }
461              
462             =begin comment
463              
464             Name : _write
465             Usage : $assump->_write($filehandle, $verbose);
466             Function: Writes NEXUS block from stored data
467             Returns : none
468             Args : none
469              
470             =end comment
471              
472             =cut
473              
474             sub _write {
475 4     4   988 my ( $self, $fh, $verbose ) = @_;
476 4   100     26 $fh ||= \*STDOUT;
477              
478 4         37 $self->SUPER::_write($fh);
479 4         20 $self->_write_options($fh);
480 4         7 for my $assumption ( @{ $self->get_assumptions() } ) {
  4         12  
481 2 50       9 if ( $assumption->is_wt() ) {
482 2         2 my @wt = @{ $assumption->get_weights() };
  2         8  
483 2         6 my $delimiter = ' ';
484 2         4 my $format = '(STANDARD TOKENS)'; ## This is the NEXUS default
485 2 50       9 if ( !$assumption->_is_tokens() ) {
486 0         0 $delimiter = '';
487 0         0 $format =~ s/TOKENS/NOTOKENS/;
488             }
489 2 50       8 if ( $assumption->_is_vector() ) {
490 2         10 $format =~ s/STANDARD/VECTOR/;
491             }
492 2         26 my @wtstring = join $delimiter, @wt;
493 2         10 print $fh "\tWTSET ", $assumption->get_name(), " $format = \n\t";
494 2         30 print $fh @wtstring, ";\n";
495             }
496             }
497 4 50       9 for my $comm ( @{ $self->{'unknown'} || [] } ) {
  4         24  
498 0         0 print $fh "\t$comm;\n";
499             }
500 4         19 print $fh "END;\n";
501             }
502              
503             sub AUTOLOAD {
504 1 50   1   5 return if $AUTOLOAD =~ /DESTROY$/;
505 1         3 my $package_name = __PACKAGE__ . '::';
506              
507             # The following methods are deprecated and are temporarily supported
508             # via a warning and a redirection
509 1         5 my %synonym_for =
510             ( "${package_name}parse_weightset" => "${package_name}_parse_wtset", );
511              
512 1 50       6 if ( defined $synonym_for{$AUTOLOAD} ) {
513 0         0 $logger->warn("$AUTOLOAD() is deprecated; use $synonym_for{$AUTOLOAD}() instead");
514 0         0 goto &{ $synonym_for{$AUTOLOAD} };
  0         0  
515             }
516             else {
517 1         7 throw 'UnknownMethod' => "ERROR: Unknown method $AUTOLOAD called";
518             }
519 0           return;
520             }
521              
522             1;