File Coverage

blib/lib/Bio/NEXUS/NHXCmd.pm
Criterion Covered Total %
statement 112 132 84.8
branch 38 54 70.3
condition 10 21 47.6
subroutine 19 21 90.4
pod 12 12 100.0
total 191 240 79.5


line stmt bran cond sub pod time code
1              
2             ######################################################
3             # NHXCmd.pm
4             ######################################################
5             # Author:
6             # $Id: NHXCmd.pm,v 1.9 2007/09/21 23:09:09 rvos Exp $
7              
8             #################### START POD DOCUMENTATION ##################
9              
10             =head1 NAME
11              
12             Bio::NEXUS::NHXCmd - Provides functions for manipulating nodes in trees
13              
14             =head1 SYNOPSIS
15              
16             new Bio::NEXUS::NHXCmd;
17              
18             =head1 DESCRIPTION
19              
20             Provides a few useful functions for nodes.
21              
22             =head1 FEEDBACK
23              
24             All feedback (bugs, feature enhancements, etc.) are all greatly appreciated. There are no mailing lists at this time for the Bio::NEXUS::Node module, so send all relevant contributions to Dr. Weigang Qiu (weigang@genectr.hunter.cuny.edu).
25              
26             =head1 AUTHORS
27              
28             Mikhail Bezruchko (bezruchk@umbi.umd.edu), Vivek Gopalan
29              
30             =head1 CONTRIBUTORS
31              
32              
33             =head1 METHODS
34              
35             =cut
36              
37             package Bio::NEXUS::NHXCmd;
38              
39 34     34   179 use strict;
  34         71  
  34         1128  
40              
41             #use Bio::NEXUS::Functions;
42             #use Data::Dumper; # XXX this is not used, might as well not import it!
43             #use Carp;# XXX this is not used, might as well not import it!
44 34     34   197 use Bio::NEXUS::Util::Exceptions;
  34         73  
  34         1423  
45 34     34   187 use vars '$VERSION';
  34         65  
  34         4749  
46 34     34   204 use Bio::NEXUS; $VERSION = $Bio::NEXUS::VERSION;
  34         53  
  34         1842  
47              
48             sub BEGIN {
49             eval {
50 34         166 require warnings;
51 34         76531 1;
52             }
53 34 50   34   254 or do {
54 34     34   165 no strict 'refs';
  34         103  
  34         2146  
55 0         0 *warnings::import = *warnings::unimport = sub { };
  0         0  
56 0         0 $INC{'warnings.pm'} = '';
57             };
58             }
59              
60             =head2 new
61              
62             Title : new
63             Usage : $nhx_cmd = new Bio::NEXUS::NHXCmd($comment_string);
64             Function: Creates a new Bio::NEXUS::NHXCmd object
65             Returns : Bio::NEXUS::NHXCmd object
66             Args : $comment_string - a string representation of the comment (w/o brackets)
67              
68             =cut
69              
70             sub new {
71 140     140 1 386 my ( $class, $command_str ) = @_;
72 140         376 my $self = { '_tag_data' => undef };
73 140         347 bless $self, $class;
74 140 100 100     488 if ( defined $command_str and $self->_is_nhx_command($command_str) ) {
75 54         121 $self->_parse_nhx_command($command_str);
76             }
77 140         537 return $self;
78             }
79              
80             =head2 to_string
81              
82             Title : to_string
83             Usage : $comment_str = $nhx_obj->to_string
84             Function: Returns a string representation of the NHX command
85             Returns : String
86             Args : None
87              
88             =cut
89              
90             sub to_string {
91 143     143 1 188 my ($self) = @_;
92 143         185 my $result = "&&NHX";
93 143 100       344 if ( not defined $self->{_tag_data} ) {
94 42         59 $result = undef;
95 42         144 return $result;
96             }
97             else {
98 101         119 for my $tag ( sort keys %{ $self->{_tag_data} } ) {
  101         405  
99              
100             #print $tag;
101              
102 294 50       553 if ( defined $tag ) {
103 294         542 my @values = $self->get_values($tag);
104              
105 294         526 for my $value (@values) {
106 295 50       535 next unless defined $value;
107 295         1023 $result .= ":$tag=$value";
108             }
109             }
110             }
111 101         709 return $result;
112             }
113             }
114              
115             =head2 equals
116              
117             Title : equals
118             Usage : $nhx_one->equals($nhx_two);
119             Function: compares two NHX objects for equality
120             Returns : 1 if the two objects contain the same date; 0 if they don't
121             Args : $nhx_two - a Bio::NEXUS::NHXCmd object
122              
123             =cut
124              
125             sub equals {
126 18     18 1 35 my ( $self, $other ) = @_;
127              
128 18         47 my @self_tags = $self->get_tags();
129 18         45 my @other_tags = $other->get_tags();
130              
131 18 100       46 if ( scalar @self_tags != scalar @other_tags ) {
132 1         5 return 0;
133             }
134             else {
135 17         30 for my $tag (@self_tags) {
136 46 100       92 if ( !$other->contains_tag($tag) ) {
137 1         5 return 0;
138             }
139              
140 45         104 my @self_values = sort $self->get_values($tag);
141 45         101 my @other_values = sort $other->get_values($tag);
142              
143 45 50       126 if ( scalar @self_values != scalar @other_values ) {
144 0         0 return 0;
145             }
146              
147 45         113 for ( my $i = 0; $i < scalar @self_values; $i++ ) {
148 45 100       212 if ( $self_values[$i] ne $other_values[$i] ) {
149 2         12 return 0;
150             }
151             }
152             }
153 14         70 return 1;
154             }
155 0         0 return 0;
156             }
157              
158             =head2 clone
159              
160             Title : clone
161             Usage : $new_obj = $original->clone();
162             Function: Creates a "deep copy" of a Bio::NEXUS::NHXCmd
163             Returns : A "deep copy" of a Bio::NEXUS::NHXCmd
164             Args : None
165              
166             =cut
167              
168             sub clone {
169 8     8 1 13 my ($self) = @_;
170 8         15 my $class = ref($self);
171              
172             #return bless( { %{$self} }, $class );
173 8         12 my $data;
174 8         23 $data->{_tag_data} = _deep_copy( $self->{_tag_data} );
175 8         13 return bless( { %{$data} }, $class );
  8         61  
176              
177             } # end of sub
178              
179             #### ?????????? Has to be added to the Bio::NEXUS::Functions package for deep copying data structures
180             ## reference : http://www.stonehenge.com/merlyn/UnixReview/col30.html
181             ####
182              
183             sub _deep_copy {
184 28     28   42 my $this = shift;
185 28 100       92 if ( not ref $this ) {
    100          
    50          
186 10         69 $this;
187             }
188             elsif ( ref $this eq "ARRAY" ) {
189 10         28 [ map _deep_copy($_), @$this ];
190             }
191             elsif ( ref $this eq "HASH" ) {
192 8         31 +{ map { $_ => _deep_copy( $this->{$_} ) } keys %$this };
  10         30  
193             }
194 0         0 else { die "what type is $_?" }
195             }
196              
197             =begin comment
198              
199             Title : _parse_nhx_command
200             Usage : N/A
201             Function: A utility (private) function that parses an NHX comment of the tree node
202             Returns : Hash of arrays, where each key is an NHX tag (i.e. 'B'), and the value is an array of values associated with that tag
203             Args : $comment - a string containing the comment
204              
205             =end comment
206              
207             =cut
208              
209             sub _parse_nhx_command {
210 54     54   89 my ( $self, $command_str ) = @_;
211 54         625 my @command = split( //, $command_str );
212 54         136 my $word = "";
213 54         78 my @words = ();
214              
215             #
216             # 1. Split the NHX command into words (tag+value combo)
217             #
218 54         66 my $open_quote = 0;
219 54         85 for my $char (@command) {
220              
221             # try converting all dbl-quotes to sngl-quotes
222 1993 50 33     8578 if ( !$open_quote && $char =~ /("|')/ ) {
223 0         0 $open_quote = 1;
224 0         0 next;
225             }
226              
227 1993 50 33     8817 if ( $open_quote && $char =~ /("|')/ ) {
    100 66        
228 0         0 $open_quote = 0;
229 0         0 next;
230             }
231              
232             =begin comment
233              
234             warn "invalid whitespace ! - check your file\n" if ( ( !$open_quote ) && $char =~ /\s/ ); #whitespace_guardian
235              
236             =end comment
237              
238             =cut
239              
240             # The main part
241             elsif ( !$open_quote && $char eq ':' ) {
242              
243             # start of a new tag; add the previous word to the array, reset $word
244 158         227 push( @words, $word );
245 158         251 $word = ":";
246             }
247              
248             else {
249 1835         2499 $word .= $char;
250             }
251              
252             }
253              
254             # This is a broken solution - works, but should be re-written
255 54         95 push( @words, $word );
256              
257             #
258             # 2. Split each word into a _tag_ and a _value_
259             #
260 54         77 for my $word (@words) {
261 212         727 my ( $tag, $value ) = $word =~ m/^:(.*?)=(.*$)/;
262 212 100       487 next if not defined $tag;
263 158         163 push( @{ $self->{'_tag_data'}->{$tag} }, $value );
  158         1056  
264             }
265              
266             } # end of sub
267              
268             =begin comment
269              
270             Title : _is_nhx_command
271             Usage : $foo = _is_nhx_command($command)
272             Function: A utility (private) function that checks if a given string appears to be an NHX command
273             Returns : 1 if the string is an NHX command, 0 if not
274             Args : $comment - string representation of the comment/command
275              
276             =end comment
277              
278             =cut
279              
280             sub _is_nhx_command {
281 96     96   153 my ( $self, $comment ) = @_;
282 96         513 return $comment =~ m/^\s*&&NHX/i;
283             }
284              
285             =head2 contains_tag
286              
287             Title : contains_tag
288             Usage : $nhx_obj->_contains_tag($tag_name)
289             Function: Checks if a given tag exists
290             Returns : 1 if the tax exists, 0 if it doesn't
291             Args : $tag_name - a string representation of a tag
292              
293             =cut
294              
295             sub contains_tag {
296 583     583 1 745 my ( $self, $tag_name ) = @_;
297 583         2301 return defined( $self->{'_tag_data'}->{$tag_name} );
298             }
299              
300             =head2 get_tags
301              
302             Title : get_tags
303             Usage : $nhx_obj->get_tags();
304             Function: Reads and returns an array of tags
305             Returns : An array of tags
306             Args : None
307              
308             =cut
309              
310             sub get_tags {
311 37     37 1 46 my ($self) = @_;
312 37         43 return sort keys %{ $self->{_tag_data} };
  37         178  
313             }
314              
315             =head2 get_values
316              
317             Title : get_values
318             Usage : $nhx_obj->get_values($tag_name);
319             Function: Returns the list of values associated with the given tag ($tag_name)
320             Returns : Array of values
321             Args : $tag_name - a string representation of the tag
322              
323             =cut
324              
325             sub get_values {
326 459     459 1 605 my ( $self, $tag_name ) = @_;
327 459 50       957 if ( not defined $tag_name ) {
328 0         0 Bio::NEXUS::Util::Exceptions::BadArgs->throw(
329             'error' => "Required argument tag_name not defined"
330             );
331             }
332 459 50       887 if ( $self->contains_tag($tag_name) ) {
333 459         511 return @{ $self->{_tag_data}->{$tag_name} };
  459         1536  
334             }
335             else {
336 0         0 return undef;
337             }
338             }
339              
340             =head2 set_tag
341              
342             Title : set_tag
343             Usage : nhx_obj->set_tag($tag_name, $tag_reference);
344             Function: Updates the list of values associated with a given tag
345             Returns : Nothing
346             Args : $tag_name - a string, $tag_reference - an array-reference
347              
348             =cut
349              
350             sub set_tag {
351 89     89 1 141 my ( $self, $tag_name, $tag_values ) = @_;
352 89 50 33     241 if ( not defined $tag_name || not defined $tag_values ) {
353 0         0 Bio::NEXUS::Util::Exceptions::BadArgs->throw(
354             'error' => "Required arguments tag_name and/or tag_values are not defined"
355             );
356             }
357 89 50       209 if ( not ref $tag_values eq 'ARRAY' ) {
358 0         0 Bio::NEXUS::Util::Exceptions::BadArgs->throw(
359             'error' => "tag_values is not an array reference"
360             );
361             }
362              
363             #croak "no such tag: $tag_name\n" unless $self->contains_tag($tag_name);
364              
365 89         365 $self->{'_tag_data'}->{$tag_name} = $tag_values;
366              
367             }
368              
369             =head2 check_tag_value_present
370              
371             Title : check_tag_value
372             Usage : $boolean = nhx_obj->check_tag_value($tag_name, $value);
373             Function: check whether a particular value is present in a tag
374             Returns : 0 or 1 [ true or false]
375             Args : $tag_name - a string, $value - scalar (string or number)
376              
377             =cut
378              
379             sub check_tag_value_present {
380 4     4 1 8 my ( $self, $tag_name, $tag_value ) = @_;
381 4 50 33     15 if ( not defined $tag_name || not defined $tag_value ) {
382 0         0 Bio::NEXUS::Util::Exceptions::BadArgs->throw(
383             'error' => "tag_name or tag_value is not defined"
384             );
385             }
386              
387             #croak "no such tag: $tag_name\n" unless $self->contains_tag($tag_name);
388 4         7 my $present = 0;
389 4         13 for my $value ( $self->get_values($tag_name) ) {
390 7 50       16 next unless defined $value;
391 7 100       20 if ( $value eq $tag_value ) {
392 3         7 $present = 1;
393 3         4 last;
394             }
395             }
396 4         15 return $present;
397             }
398              
399             =head2 add_tag_value
400              
401             Title : add_tag_value
402             Usage : $nhx_obj->add_tag_value($tag_name, $tag_value);
403             Function: Adds a new tag/value set to the $nhx_obj;
404             Returns : 0 if not added or 1 if added [false or true]
405             Args : $tag_name - a string, $tag_value - a string
406              
407             =cut
408              
409             sub add_tag_value {
410 3     3 1 6 my ( $self, $tag_name, $tag_value ) = @_;
411 3 50 33     12 if ( not defined $tag_name || not defined $tag_value ) {
412 0         0 Bio::NEXUS::Util::Exceptions::BadArgs->throw(
413             'error' => "tag_name or tag_value is not defined"
414             );
415             }
416              
417             #croak "no such tag: $tag_name\n" unless $self->contains_tag($tag_name);
418              
419 3         10 my $is_value_present =
420             $self->check_tag_value_present( $tag_name, $tag_value );
421 3 100       9 push @{ $self->{_tag_data}->{$tag_name} }, $tag_value
  1         4  
422             unless $is_value_present;
423 3 100       14 return $is_value_present ? 0 : 1;
424              
425             }
426              
427             =head2 delete_tag
428              
429             Title : delete_tag
430             Usage : $nhx_obj->delete_tag($tag_name);
431             Function: Removes a given tag (and the associated valus) from the $nhx_obj
432             Returns : Nothing
433             Args : $tag_name - a string representation of the tag
434              
435             =cut
436              
437             sub delete_tag {
438 0     0 1   my ( $self, $tag_name ) = @_;
439              
440 0 0         delete $self->{_tag_data}->{$tag_name} if defined $tag_name;
441              
442             }
443              
444             =head2 delete_all_tags
445              
446             Title : delete_all_tags
447             Usage : $nhx_obj->delete_all_tags();
448             Function: Removes all tags from $nhx_obj
449             Returns : Nothing
450             Args : None
451              
452             =cut
453              
454             sub delete_all_tags {
455 0     0 1   my ($self) = @_;
456              
457 0           $self->{'_tag_data'} = undef;
458             }
459              
460             1;
461