File Coverage

blib/lib/Bio/Phylo/Matrices/DatumRole.pm
Criterion Covered Total %
statement 124 412 30.1
branch 28 154 18.1
condition 3 80 3.7
subroutine 24 63 38.1
pod 21 48 43.7
total 200 757 26.4


line stmt bran cond sub pod time code
1             package Bio::Phylo::Matrices::DatumRole;
2 16     16   90 use strict;
  16         33  
  16         382  
3 16     16   73 use warnings;
  16         29  
  16         397  
4 16     16   602 use Bio::Phylo::Util::MOP;
  16         27  
  16         114  
5 16     16   79 use base qw'Bio::Phylo::Matrices::TypeSafeData Bio::Phylo::Taxa::TaxonLinker';
  16         28  
  16         5335  
6 16     16   785 use Bio::Phylo::Util::OptionalInterface 'Bio::Seq';
  16         31  
  16         99  
7 16     16   100 use Bio::Phylo::Util::Exceptions 'throw';
  16         33  
  16         700  
8 16     16   116 use Bio::Phylo::Util::CONSTANT qw':objecttypes /looks_like/';
  16         35  
  16         3660  
9 16     16   100 use Bio::Phylo::NeXML::Writable;
  16         33  
  16         76  
10 16     16   77 use Bio::Phylo::Factory;
  16         30  
  16         98  
11             my $LOADED_WRAPPERS = 0;
12             {
13             my $fac = Bio::Phylo::Factory->new;
14             my $logger = __PACKAGE__->get_logger;
15             my $TYPE_CONSTANT = _DATUM_;
16             my $CONTAINER_CONSTANT = _MATRIX_;
17             #{
18             #my @fields = \( my ( %weight, %position, %annotations ) );
19             #}
20              
21             =head1 NAME
22              
23             Bio::Phylo::Matrices::DatumRole - Extra behaviours for a character state sequence
24              
25             =head1 SYNOPSIS
26              
27             use Bio::Phylo::Factory;
28             my $fac = Bio::Phylo::Factory->new;
29              
30             # instantiating a datum object...
31             my $datum = $fac->create_datum(
32             -name => 'Tooth comb size,
33             -type => 'STANDARD',
34             -desc => 'number of teeth in lower jaw comb',
35             -pos => 1,
36             -weight => 2,
37             -char => [ 6 ],
38             );
39              
40             # ...and linking it to a taxon object
41             my $taxon = $fac->create_taxon(
42             -name => 'Lemur_catta'
43             );
44             $datum->set_taxon( $taxon );
45              
46             # instantiating a matrix...
47             my $matrix = $fac->create_matrix;
48              
49             # ...and insert datum in matrix
50             $matrix->insert($datum);
51              
52             =head1 DESCRIPTION
53              
54             The datum object models a single observation or a sequence of observations,
55             which can be linked to a taxon object.
56              
57             =head1 METHODS
58              
59             =head2 CONSTRUCTOR
60              
61             =over
62              
63             =item new()
64              
65             Datum object constructor.
66              
67             Type : Constructor
68             Title : new
69             Usage : my $datum = Bio::Phylo::Matrices::Datum->new;
70             Function: Instantiates a Bio::Phylo::Matrices::Datum
71             object.
72             Returns : A Bio::Phylo::Matrices::Datum object.
73             Args : None required. Optional:
74             -taxon => $taxon,
75             -weight => 0.234,
76             -type => DNA,
77             -pos => 2,
78              
79             =cut
80              
81             sub new : Constructor {
82              
83             # could be child class
84 724     724 1 1305 my $class = shift;
85              
86             # notify user
87 724         3090 $logger->info("constructor called for '$class'");
88 724 100       1575 if ( not $LOADED_WRAPPERS ) {
89 14 0 0 0 0 29 eval do { local $/; <DATA> };
  14 0 0 0 0 54  
  14 0 0 0 0 13060  
  0 0 0 0 0 0  
  0 0 0 0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0 0   0 0 0  
  0     0 0 0  
  0     0 0 0  
  0     0 0 0  
  0     0 0 0  
  0     0 0 0  
  0     0 0 0  
  0     0 0 0  
  0     0 0 0  
  0     0 0 0  
  0     0 0 0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
90 14 50       62 die $@ if $@;
91 14         33 $LOADED_WRAPPERS++;
92             }
93              
94             # go up inheritance tree, eventually get an ID
95 724         2810 my $self = $class->SUPER::new(
96             @_,
97             '-listener' => \&_update_characters,
98             );
99 724         2505 return $self;
100 16     16   98 }
  16         35  
  16         85  
101              
102             =item new_from_bioperl()
103              
104             Datum constructor from Bio::Seq argument.
105              
106             Type : Constructor
107             Title : new_from_bioperl
108             Usage : my $datum =
109             Bio::Phylo::Matrices::Datum->new_from_bioperl($seq);
110             Function: Instantiates a
111             Bio::Phylo::Matrices::Datum object.
112             Returns : A Bio::Phylo::Matrices::Datum object.
113             Args : A Bio::Seq (or similar) object
114              
115             =cut
116              
117             sub new_from_bioperl {
118 0     0 1 0 my ( $class, $seq, @args ) = @_;
119              
120             # want $seq type-check here? Allowable: is-a Bio::PrimarySeq,
121             # Bio::LocatableSeq /maj
122 0   0     0 my $type = $seq->alphabet || $seq->_guess_alphabet || 'dna';
123 0         0 my $self = $class->new( '-type' => $type, @args );
124              
125             # copy seq string
126 0         0 my $seqstring = $seq->seq;
127 0 0 0     0 if ( $seqstring and $seqstring =~ /\S/ ) {
128 0         0 eval { $self->set_char($seqstring) };
  0         0  
129 0 0 0     0 if (
130             $@
131             and looks_like_instance(
132             $@, 'Bio::Phylo::Util::Exceptions::InvalidData'
133             )
134             )
135             {
136 0         0 $logger->error(
137             "\nAn exception of type Bio::Phylo::Util::Exceptions::InvalidData was caught\n\n"
138             . $@->description
139             . "\n\nThe BioPerl sequence object contains invalid data ($seqstring)\n"
140             . "I cannot store this string, I will continue instantiating an empty object.\n"
141             . "---------------------------------- STACK ----------------------------------\n"
142             . $@->trace->as_string
143             . "\n--------------------------------------------------------------------------"
144             );
145             }
146             }
147              
148             # copy name
149 0         0 my $name = $seq->display_id;
150 0 0       0 $self->set_name($name) if defined $name;
151              
152             # copy desc
153 0         0 my $desc = $seq->desc;
154 0 0       0 $self->set_desc($desc) if defined $desc;
155              
156             # only Bio::LocatableSeq objs have these fields...
157 0         0 for my $field (qw(start end strand)) {
158 0 0       0 $self->$field( $seq->$field ) if $seq->can($field);
159             }
160 0         0 return $self;
161             }
162              
163             =back
164              
165             =head2 MUTATORS
166              
167             =over
168              
169             =item set_char()
170              
171             Sets character state(s)
172              
173             Type : Mutator
174             Title : set_char
175             Usage : $datum->set_char($char);
176             Function: Assigns a datum's character value.
177             Returns : Modified object.
178             Args : The $char argument is checked against
179             the allowed ranges for the various
180             character types: IUPAC nucleotide (for
181             types of DNA|RNA|NUCLEOTIDE), IUPAC
182             single letter amino acid codes (for type
183             PROTEIN), integers (STANDARD) or any of perl's
184             decimal formats (CONTINUOUS). The $char can be:
185             * a single character;
186             * a string of characters;
187             * an array reference of characters;
188             * an array of characters;
189             Comments: Note that on assigning characters to a datum,
190             previously set annotations are removed.
191              
192             =cut
193              
194             sub set_char {
195 713     713 1 1077 my $self = shift;
196 713         1853 my $name = $self->get_internal_name;
197 713 100       2173 my $length = ref $_[0] ? join( '', @{ $_[0] } ) : join( '', @_ );
  69         165  
198 713         3815 $logger->info("setting $name $length chars '@_'");
199 713         1296 my @data;
200 713         1312 for my $arg (@_) {
201 713 100       1697 if ( looks_like_instance( $arg, 'ARRAY' ) ) {
202 69         100 push @data, @{$arg};
  69         209  
203             }
204             else {
205 644         996 push @data, @{ $self->get_type_object->split($arg) };
  644         1452  
206             }
207             }
208 713         2150 my $missing = $self->get_missing;
209 713   50     2032 my $position = $self->get_position || 1;
210 713         2154 for ( 1 .. $position - 1 ) {
211 0         0 unshift @data, $missing;
212             }
213 713         981 my @char = @{ $self->get_entities }; # store old data for rollback
  713         1658  
214 713         1262 eval {
215 713         1960 $self->clear;
216 713         2320 $self->insert(@data);
217             };
218 713 100       1613 if ($@) {
219 7         20 $self->clear;
220 7         10 eval { $self->insert(@char) };
  7         51  
221 7         19 undef($@);
222 7         19 throw 'InvalidData' =>
223             sprintf( 'Invalid data for row %s (type %s: %s)',
224             $self->get_internal_name, $self->get_type, join( ' ', @data ) );
225             }
226 706         2236 $self->set_annotations;
227 706         5544 return $self;
228             }
229              
230             =back
231              
232             =head2 ACCESSORS
233              
234             =over
235              
236             =item get_matrix()
237              
238             Gets the matrix (if any) this datum belongs to
239              
240             Type : Accessor
241             Title : get_matrix
242             Usage : my $matrix = $datum->get_matrix;
243             Function: Retrieves the matrix the datum belongs to
244             Returns : Bio::Phylo::Matrices::Matrix
245             Args : NONE
246              
247             =cut
248              
249 133     133 1 319 sub get_matrix { shift->_get_container }
250              
251             =item get_char()
252              
253             Gets characters.
254              
255             Type : Accessor
256             Title : get_char
257             Usage : my $char = $datum->get_char;
258             Function: Retrieves a datum's character value.
259             Returns : In scalar context, returns a single
260             character, or a string of characters
261             (e.g. a DNA sequence, or a space
262             delimited series of continuous characters).
263             In list context, returns a list of characters
264             (of zero or more characters).
265             Args : NONE
266              
267             =cut
268              
269             sub get_char {
270 1462     1462 1 3423 my $self = shift;
271 1462         1948 my @data = @{ $self->get_entities };
  1462         3400  
272 1462 100       3414 if (@data) {
273 715 100       15878 return wantarray ? @data : $self->get_type_object->join( \@data );
274             }
275             else {
276 747 50       2380 return wantarray ? () : '';
277             }
278             }
279            
280             =item get_unaligned_char()
281              
282             Gets unaligned characters, i.e. without gap or missing symbols
283              
284             Type : Accessor
285             Title : get_unaligned_char
286             Usage : my $char = $datum->get_unaligned_char;
287             Function: Retrieves a datum's unaligned character sequence
288             Returns : In scalar context, returns a single
289             character, or a string of characters
290             (e.g. a DNA sequence, or a space
291             delimited series of continuous characters).
292             In list context, returns a list of characters
293             (of zero or more characters).
294             Args : NONE
295              
296             =cut
297            
298             sub get_unaligned_char {
299 0     0 1 0 my $self = shift;
300 0         0 my $gap = $self->get_gap;
301 0         0 my $missing = $self->get_missing;
302 0         0 my @char = $self->get_char;
303 0 0       0 my @data = grep { $_ ne $gap && $_ ne $missing } @char;
  0         0  
304 0 0       0 if (@data) {
305 0 0       0 return wantarray ? @data : $self->get_type_object->join( \@data );
306             }
307             else {
308 0 0       0 return wantarray ? () : '';
309             }
310             }
311              
312             =item get_length()
313              
314             Gets invocant number of characters.
315              
316             Type : Accessor
317             Title : get_length
318             Usage : my $length = $datum->get_length;
319             Function: Retrieves a datum's length.
320             Returns : a SCALAR integer.
321             Args : NONE
322              
323             =cut
324              
325             sub get_length {
326 623     623 1 850 my $self = shift;
327 623 100       1395 if ( my $matrix = $self->_get_container ) {
328 39         93 return $matrix->get_nchar;
329             }
330             else {
331 584   50     904 return scalar( @{ $self->get_entities } ) + ( $self->get_position || 1 ) - 1;
  584         1286  
332             }
333             }
334              
335             =item get_by_index()
336              
337             Gets state at argument index.
338              
339             Type : Accessor
340             Title : get_by_index
341             Usage : my $val = $datum->get_by_index($i);
342             Function: Retrieves state at index $i.
343             Returns : a character state.
344             Args : INT
345              
346             =cut
347              
348             sub get_by_index {
349 50     50 1 72 my ( $self, $index ) = @_;
350 50         107 $logger->debug($index);
351 50   50     103 my $offset = ( $self->get_position || 1 ) - 1;
352 50 50       83 return $self->get_type_object->get_missing if $offset > $index;
353 50         110 my $val = $self->SUPER::get_by_index( $index - $offset );
354 50 50       107 return defined $val ? $val : $self->get_type_object->get_missing;
355             }
356              
357             =item get_index_of()
358              
359             Returns the index of the first occurrence of the
360             state observation in the datum or undef if the datum
361             doesn't contain the argument
362              
363             Type : Generic query
364             Title : get_index_of
365             Usage : my $i = $datum->get_index_of($state)
366             Function: Returns the index of the first occurrence of the
367             state observation in the datum or undef if the datum
368             doesn't contain the argument
369             Returns : An index or undef
370             Args : A contained object
371              
372             =cut
373              
374             sub get_index_of {
375 0     0 1 0 my ( $self, $obj ) = @_;
376 0         0 my $is_numerical =
377             $self->get_type =~ m/^(Continuous|Standard|Restriction)$/;
378 0         0 my $i = 0;
379 0         0 for my $ent ( @{ $self->get_entities } ) {
  0         0  
380 0 0       0 if ($is_numerical) {
381 0 0       0 return $i if $obj == $ent;
382             }
383             else {
384 0 0       0 return $i if $obj eq $ent;
385             }
386 0         0 $i++;
387             }
388 0         0 return;
389             }
390              
391             =back
392              
393             =head2 TESTS
394              
395             =over
396              
397             =item can_contain()
398              
399             Tests if invocant can contain argument.
400              
401             Type : Test
402             Title : can_contain
403             Usage : &do_something if $datum->can_contain( @args );
404             Function: Tests if $datum can contain @args
405             Returns : BOOLEAN
406             Args : One or more arguments as can be provided to set_char
407              
408             =cut
409              
410             sub can_contain {
411 719     719 1 1107 my $self = shift;
412 719         10595 my @data = @_;
413 719 50       1552 if ( my $obj = $self->get_type_object ) {
414 719 50       4120 if ( $obj->isa('Bio::Phylo::Matrices::Datatype::Mixed') ) {
415 0         0 my @split;
416 0         0 for my $datum (@data) {
417 0 0       0 if ( looks_like_implementor( $datum, 'get_char' ) ) {
    0          
418 0         0 my @tmp = $datum->get_char();
419 0         0 my $i = $datum->get_position() - 1;
420 0         0 for (@tmp) {
421 0         0 $split[ $i++ ] = $_;
422             }
423             }
424             elsif ( looks_like_instance( $datum, 'ARRAY' ) ) {
425 0         0 push @split, @{$datum};
  0         0  
426             }
427             else {
428 0         0 my $subtype = $obj->get_type_for_site( scalar(@split) );
429 0         0 push @split, @{ $subtype->split($datum) };
  0         0  
430             }
431             }
432              
433             #return 1;
434 0         0 for my $i ( 1 .. scalar(@split) ) {
435 0         0 my $subtype = $obj->get_type_for_site( $i - 1 );
436 0 0       0 next if $subtype->is_valid( $split[ $i - 1 ] );
437 0         0 throw 'InvalidData' => sprintf(
438             'Invalid char %s at pos %s for type %s',
439             $split[ $i - 1 ],
440             $i, $subtype->get_type,
441             );
442             }
443 0         0 return 1;
444             }
445             else {
446 719         2324 return $obj->is_valid(@data);
447             }
448             }
449             else {
450 0         0 throw 'API' => "No associated type object found,\n"
451             . "this is a bug - please report - thanks";
452             }
453             }
454              
455             =back
456              
457             =head2 CALCULATIONS
458              
459             =over
460              
461             =item calc_state_counts()
462              
463             Calculates occurrences of states.
464              
465             Type : Calculation
466             Title : calc_state_counts
467             Usage : my %counts = %{ $datum->calc_state_counts };
468             Function: Calculates occurrences of states.
469             Returns : Hashref: keys are states, values are counts
470             Args : Optional - one or more states to focus on
471              
472             =cut
473              
474             sub calc_state_counts {
475 23     23 1 47 my $self = shift;
476              
477             # maybe there should be an option to bin continuous values
478             # in X categories, and return the frequencies of those? Anyway,
479             # Hennig86 seems to want continuous values to be counted as well,
480             # so not throwing an exception here.
481             #if ( $self->get_type =~ /^continuous$/i ) {
482             # throw 'BadArgs' => 'Matrix holds continuous values';
483             #}
484 23         43 my %counts;
485 23 50       55 if (@_) {
486 0         0 my %focus = map { $_ => 1 } @_;
  0         0  
487 0         0 my @char = $self->get_char;
488 0         0 for my $c (@char) {
489 0 0       0 if ( exists $focus{$c} ) {
490 0 0       0 if ( not exists $counts{$c} ) {
491 0         0 $counts{$c} = 1;
492             }
493             else {
494 0         0 $counts{$c}++;
495             }
496             }
497             }
498             }
499             else {
500 23         68 my @char = $self->get_char;
501 23         58 for my $c (@char) {
502 101 100       198 if ( not exists $counts{$c} ) {
503 80         187 $counts{$c} = 1;
504             }
505             else {
506 21         50 $counts{$c}++;
507             }
508             }
509             }
510 23         75 return \%counts;
511             }
512              
513             =item calc_distance()
514              
515             Calculates the distance between the invocant and argument
516              
517             Type : Calculation
518             Title : calc_distance
519             Usage : my $dist = $datum1->calc_distance($datum2);
520             Function: Calculates pairwise distance
521             Returns : A number, the distance per site
522             Args : Another datum to calculate the distance to
523             Comments: Assumes the sequences are aligned. Calculates
524             substitutions / total non-missing and non-gapped sites.
525             =cut
526              
527             sub calc_distance {
528 0     0 1 0 my ( $self, $other ) = @_;
529 0         0 my @c1 = $self->get_char;
530 0         0 my @c2 = $other->get_char;
531 0         0 my $t = $self->get_type_object;
532 0         0 my $m = $t->get_missing;
533 0         0 my $g = $t->get_gap;
534 0         0 my $subst = 0;
535 0         0 my $total = 0;
536 0         0 for my $i ( 0 .. $#c1 ) {
537 0 0 0     0 next if $c1[$i] eq $m or $c1[$i] eq $g or $c2[$i] eq $m or $c2[$i] eq $g;
      0        
      0        
538 0         0 $subst += $c1[$i] ne $c2[$i];
539 0         0 $total++;
540             }
541 0 0       0 return $total ? $subst / $total : 9**9**9;
542             }
543              
544             =item calc_state_frequencies()
545              
546             Calculates the frequencies of the states observed in the matrix.
547              
548             Type : Calculation
549             Title : calc_state_frequencies
550             Usage : my %freq = %{ $object->calc_state_frequencies() };
551             Function: Calculates state frequencies
552             Returns : A hash, keys are state symbols, values are frequencies
553             Args : Optional:
554             # if true, counts missing (usually the '?' symbol) as a state
555             # in the final tallies. Otherwise, missing states are ignored
556             -missing => 1
557             # if true, counts gaps (usually the '-' symbol) as a state
558             # in the final tallies. Otherwise, gap states are ignored
559             -gap => 1
560             Comments: Throws exception if matrix holds continuous values
561              
562             =cut
563              
564             sub calc_state_frequencies {
565 5     5 1 9 my $self = shift;
566 5         17 my $counts = $self->calc_state_counts;
567 5         14 my %args = looks_like_hash @_;
568 5         10 for my $arg (qw(missing gap)) {
569 10 50       19 if ( not exists $args{"-${arg}"} ) {
570 10         20 my $method = "get_${arg}";
571 10         28 my $symbol = $self->$method;
572 10         20 delete $counts->{$symbol};
573             }
574             }
575 5         8 my $total = 0;
576 5         9 $total += $_ for values %{$counts};
  5         14  
577 5 50       12 if ( $total > 0 ) {
578 5         7 for my $state ( keys %{$counts} ) {
  5         11  
579 20         30 $counts->{$state} /= $total;
580             }
581             }
582 5         11 return $counts;
583             }
584              
585             =back
586              
587             =head2 METHODS
588              
589             =over
590              
591             =item reverse()
592              
593             Reverses contents.
594              
595             Type : Method
596             Title : reverse
597             Usage : $datum->reverse;
598             Function: Reverses a datum's contained characters
599             Returns : Returns modified $datum
600             Args : NONE
601              
602             =cut
603              
604             sub reverse {
605 0     0 1 0 my $self = shift;
606 0         0 my @char = $self->get_char;
607 0         0 my @reversed = reverse(@char);
608 0         0 $self->set_char( \@reversed );
609             }
610              
611             =item concat()
612              
613             Appends argument to invocant.
614              
615             Type : Method
616             Title : reverse
617             Usage : $datum->concat($datum1);
618             Function: Appends $datum1 to $datum
619             Returns : Returns modified $datum
620             Args : NONE
621              
622             =cut
623              
624             sub concat {
625 0     0 1 0 my ( $self, @data ) = @_;
626 0         0 $logger->info("concatenating objects");
627 0         0 my @newchars;
628 0         0 my @self_chars = $self->get_char;
629 0         0 my $self_i = $self->get_position - 1;
630 0         0 my $self_j = $self->get_length - 1 + $self_i;
631 0         0 @newchars[ $self_i .. $self_j ] = @self_chars;
632 0         0 for my $datum (@data) {
633 0         0 my @chars = $datum->get_char;
634 0         0 my $i = $datum->get_position - 1;
635 0         0 my $j = $datum->get_length - 1 + $i;
636 0         0 @newchars[ $i .. $j ] = @chars;
637             }
638 0         0 my $missing = $self->get_missing;
639 0         0 for my $i ( 0 .. $#newchars ) {
640 0 0       0 $newchars[$i] = $missing if !defined $newchars[$i];
641             }
642 0         0 $self->set_char( \@newchars );
643             }
644              
645             =item consense()
646              
647             Creates consensus sequence out of arguments
648              
649             Type : Method
650             Title : consense
651             Usage : my @chars = $datum->consense($datum1,...);
652             Function: Creates consensus sequence out of arguments
653             Returns : Returns @chars or $seq
654             Args : NONE
655              
656             =cut
657            
658             sub consense {
659 0     0 1 0 my @data = @_;
660            
661             # build two-dimensional array of character states
662 0         0 my @chars;
663 0         0 for my $datum ( @data ) {
664 0         0 my @char = $datum->get_char;
665 0         0 push @chars, \@char;
666             }
667            
668             # get special symbols
669 0         0 my $length = $data[0]->get_length;
670 0         0 my $to = $data[0]->get_type_object;
671 0         0 my $m = $to->get_missing;
672 0         0 my $g = $to->get_gap;
673            
674             # build result
675 0         0 my @result;
676 0         0 for my $i ( 0 .. ( $length - 1 ) ) {
677 0         0 my %col;
678            
679             # get distinct states for column, ignore missing and gap
680 0         0 ROW: for my $row ( @chars ) {
681 0         0 my $c = $row->[$i];
682 0 0 0     0 next ROW if $c eq $m or $c eq $g;
683 0         0 $col{$c} = 1;
684             }
685            
686             # get ambiguity symbol or missing
687 0         0 my @states = keys %col;
688 0 0       0 if ( @states ) {
689 0   0     0 push @result, $to->get_symbol_for_states(@states) || $m;
690             }
691             else {
692 0         0 push @result, $m;
693             }
694             }
695            
696             # return result
697 0 0       0 return wantarray ? @result : $to->join(@result);
698             }
699              
700             =begin comment
701              
702             Validates invocant data contents.
703              
704             Type : Method
705             Title : validate
706             Usage : $datum->validate;
707             Function: Validates character data contained by $datum
708             Returns : True or throws Bio::Phylo::Util::Exceptions::InvalidData
709             Args : NONE
710              
711             =end comment
712              
713             =cut
714              
715             sub _validate {
716 752     752   1120 my $self = shift;
717 752 100       1753 if ( !$self->get_type_object->is_valid($self) ) {
718 3         10 throw 'InvalidData' => 'Invalid data!';
719             }
720             }
721              
722             =item to_xml()
723              
724             Serializes datum to nexml format.
725              
726             Type : Format convertor
727             Title : to_xml
728             Usage : my $xml = $datum->to_xml;
729             Function: Converts datum object into a nexml element structure.
730             Returns : Nexml block (SCALAR).
731             Args : -chars => [] # optional, an array ref of character IDs
732             -states => {} # optional, a hash ref of state IDs
733             -symbols => {} # optional, a hash ref of symbols
734             -special => {} # optional, a hash ref of special symbol IDs
735              
736             =cut
737              
738             sub to_xml {
739 0     0 1 0 my $self = shift;
740 0         0 my %args = looks_like_hash @_;
741 0         0 my $char_ids = $args{'-chars'};
742 0         0 my $state_ids = $args{'-states'};
743 0         0 my $special = $args{'-special'};
744 0 0       0 if ( my $taxon = $self->get_taxon ) {
745 0         0 $self->set_attributes( 'otu' => $taxon->get_xml_id );
746             }
747 0         0 my @char = $self->get_char;
748 0         0 my ( $missing, $gap ) = ( $self->get_missing, $self->get_gap );
749 0         0 my $xml = $self->get_xml_tag;
750 0 0       0 if ( not $args{'-compact'} ) {
751 0         0 for my $i ( 0 .. $#char ) {
752 0         0 my ( $c, $s );
753 0 0 0     0 if ( $missing ne $char[$i] and $gap ne $char[$i] ) {
    0 0        
754 0 0 0     0 if ( $char_ids and $char_ids->[$i] ) {
755 0         0 $c = $char_ids->[$i];
756             }
757             else {
758 0         0 $c = $i;
759             }
760 0 0 0     0 if ( $state_ids and $state_ids->{ uc $char[$i] } ) {
761 0         0 $s = $state_ids->{ uc $char[$i] };
762             }
763             else {
764 0         0 $s = uc $char[$i];
765             }
766             }
767             elsif ( $missing eq $char[$i] or $gap eq $char[$i] ) {
768 0 0 0     0 if ( $char_ids and $char_ids->[$i] ) {
769 0         0 $c = $char_ids->[$i];
770             }
771             else {
772 0         0 $c = $i;
773             }
774 0 0 0     0 if ( $special and $special->{ $char[$i] } ) {
775 0         0 $s = $special->{ $char[$i] };
776             }
777             else {
778 0         0 $s = $char[$i];
779             }
780             }
781              
782             # $cell->set_attributes( 'char' => $c, 'state' => $s );
783             # $xml .= $cell->get_xml_tag(1);
784 0         0 $xml .= sprintf( '<cell char="%s" state="%s"/>', $c, $s );
785             }
786             }
787             else {
788 0         0 my @tmp = map { uc $_ } @char;
  0         0  
789 0         0 my $seq = Bio::Phylo::NeXML::Writable->new(
790             '-tag' => 'seq',
791             '-identifiable' => 0,
792             );
793 0         0 my $seq_text = $self->get_type_object->join( \@tmp );
794 0         0 $xml .=
795             $seq->get_xml_tag . "\n$seq_text\n" . "</" . $seq->get_tag . ">";
796             }
797 0         0 $xml .= sprintf( '</%s>', $self->get_tag );
798 0         0 return $xml;
799             }
800              
801             =item to_dom()
802              
803             Analog to to_xml.
804              
805             Type : Serializer
806             Title : to_dom
807             Usage : $datum->to_dom
808             Function: Generates a DOM subtree from the invocant
809             and its contained objects
810             Returns : an XML::LibXML::Element object
811             Args : none
812              
813             =cut
814              
815             sub to_dom {
816 0     0 1 0 my $self = shift;
817 0         0 my $dom = $_[0];
818 0         0 my @args = @_;
819              
820             # handle dom factory object...
821 0 0 0     0 if ( looks_like_instance( $dom, 'SCALAR' )
822             && $dom->_type == _DOMCREATOR_ )
823             {
824 0         0 splice( @args, 0, 1 );
825             }
826             else {
827 0         0 $dom = $Bio::Phylo::NeXML::DOM::DOM;
828 0 0       0 unless ($dom) {
829 0         0 throw 'BadArgs' => 'DOM factory object not provided';
830             }
831             }
832             ##### make sure argument handling works here....
833 0         0 my %args = looks_like_hash @args;
834 0         0 my $char_ids = $args{'-chars'};
835 0         0 my $state_ids = $args{'-states'};
836 0         0 my $special = $args{'-special'};
837 0 0       0 if ( my $taxon = $self->get_taxon ) {
838 0         0 $self->set_attributes( 'otu' => $taxon->get_xml_id );
839             }
840 0         0 my @char = $self->get_char;
841 0         0 my ( $missing, $gap ) = ( $self->get_missing, $self->get_gap );
842 0         0 my $elt = $self->get_dom_elt($dom);
843 0 0       0 if ( not $args{'-compact'} ) {
844 0         0 for my $i ( 0 .. $#char ) {
845 0 0 0     0 if ( $missing ne $char[$i] and $gap ne $char[$i] ) {
    0 0        
846 0         0 my ( $c, $s );
847 0 0 0     0 if ( $char_ids and $char_ids->[$i] ) {
848 0         0 $c = $char_ids->[$i];
849             }
850             else {
851 0         0 $c = $i;
852             }
853 0 0 0     0 if ( $state_ids and $state_ids->{ uc $char[$i] } ) {
854 0         0 $s = $state_ids->{ uc $char[$i] };
855             }
856             else {
857 0         0 $s = uc $char[$i];
858             }
859 0         0 my $cell_elt = $dom->create_element( '-tag' => 'cell' );
860 0         0 $cell_elt->set_attributes( 'char' => $c );
861 0         0 $cell_elt->set_attributes( 'state' => $s );
862 0         0 $elt->set_child($cell_elt);
863             }
864             elsif ( $missing eq $char[$i] or $gap eq $char[$i] ) {
865 0         0 my ( $c, $s );
866 0 0 0     0 if ( $char_ids and $char_ids->[$i] ) {
867 0         0 $c = $char_ids->[$i];
868             }
869             else {
870 0         0 $c = $i;
871             }
872 0 0 0     0 if ( $special and $special->{ $char[$i] } ) {
873 0         0 $s = $special->{ $char[$i] };
874             }
875             else {
876 0         0 $s = $char[$i];
877             }
878 0         0 my $cell_elt = $dom->create_element( '-tag' => 'cell' );
879 0         0 $cell_elt->set_attributes( 'char' => $c );
880 0         0 $cell_elt->set_attributes( 'state' => $s );
881 0         0 $elt->set_child($cell_elt);
882             }
883             }
884             }
885             else {
886 0         0 my @tmp = map { uc $_ } @char;
  0         0  
887 0         0 my $seq = $self->get_type_object->join( \@tmp );
888 0         0 my $seq_elt = $dom->create_element( '-tag' => 'seq' );
889             #### create a text node here....
890 0         0 $seq_elt->set_text($seq);
891              
892             #$seq_elt->set_child( XML::LibXML::Text->new($seq) );
893             ####
894 0         0 $elt->set_child($seq_elt);
895             }
896 0         0 return $elt;
897             }
898              
899             =item copy_atts()
900              
901             Not implemented!
902              
903             =cut
904              
905       0 1   sub copy_atts { } # XXX not implemented
906              
907             =item complement()
908              
909             Not implemented!
910              
911             =cut
912              
913       0 1   sub complement { } # XXX not implemented
914              
915             =item slice()
916              
917             Not implemented!
918              
919             =cut
920              
921             sub slice { # XXX not implemented
922 0     0 1 0 my $self = shift;
923 0         0 my $start = int $_[0];
924 0         0 my $end = int $_[1];
925 0         0 my @chars = $self->get_char;
926 0         0 my $pos = $self->get_position;
927 0         0 my $slice - $self->copy_atts;
928             }
929 1488     1488   3563 sub _type { $TYPE_CONSTANT }
930 358     358   608 sub _container { $CONTAINER_CONSTANT }
931 18     18   56 sub _tag { 'row' }
932            
933             sub _update_characters {
934 133     133   182 my $self = shift;
935 133 50       273 if ( my $matrix = $self->get_matrix ) {
936 0           $matrix->_update_characters;
937             }
938             }
939             }
940              
941             =back
942              
943             =cut
944              
945             # podinherit_insert_token
946              
947             =head1 SEE ALSO
948              
949             There is a mailing list at L<https://groups.google.com/forum/#!forum/bio-phylo>
950             for any user or developer questions and discussions.
951              
952             =over
953              
954             =item L<Bio::Phylo::Taxa::TaxonLinker>
955              
956             This object inherits from L<Bio::Phylo::Taxa::TaxonLinker>, so the methods
957             defined therein are also applicable to L<Bio::Phylo::Matrices::Datum> objects.
958              
959             =item L<Bio::Phylo::Matrices::TypeSafeData>
960              
961             This object inherits from L<Bio::Phylo::Matrices::TypeSafeData>, so the methods
962             defined therein are also applicable to L<Bio::Phylo::Matrices::Datum> objects.
963              
964             =item L<Bio::Phylo::Manual>
965              
966             Also see the manual: L<Bio::Phylo::Manual> and L<http://rutgervos.blogspot.com>.
967              
968             =back
969              
970             =head1 CITATION
971              
972             If you use Bio::Phylo in published research, please cite it:
973              
974             B<Rutger A Vos>, B<Jason Caravas>, B<Klaas Hartmann>, B<Mark A Jensen>
975             and B<Chase Miller>, 2011. Bio::Phylo - phyloinformatic analysis using Perl.
976             I<BMC Bioinformatics> B<12>:63.
977             L<http://dx.doi.org/10.1186/1471-2105-12-63>
978              
979             =cut
980              
981             1;
982             __DATA__
983              
984             my $DEFAULT_NAME = 'DEFAULT';
985              
986             sub meta_names {
987             my ($self) = @_;
988             my @r;
989             my $names = $self->get_generic('meta') || {};
990             foreach ( sort keys %{ $names } ) {
991             push (@r, $_) unless $_ eq $DEFAULT_NAME;
992             }
993             unshift @r, $DEFAULT_NAME if $names->{$DEFAULT_NAME};
994             return @r;
995             }
996              
997             sub get_SeqFeatures { $logger->warn }
998              
999             sub get_all_SeqFeatures { $logger->warn }
1000              
1001             sub feature_count { $logger->warn }
1002              
1003             sub seq {
1004             my $self = shift;
1005             my $seq = $self->get_char;
1006             return $seq;
1007             }
1008              
1009             # from primary seq
1010             sub subseq {
1011             my ($self,$start,$end,$replace) = @_;
1012              
1013             if( ref($start) && $start->isa('Bio::LocationI') ) {
1014             my $loc = $start;
1015             $replace = $end; # do we really use this anywhere? scary. HL
1016             my $seq = "";
1017             foreach my $subloc ($loc->each_Location()) {
1018             my $piece = $self->subseq($subloc->start(),
1019             $subloc->end(), $replace);
1020             if($subloc->strand() < 0) {
1021             $piece = Bio::PrimarySeq->new('-seq' => $piece)->revcom()->seq();
1022             }
1023             $seq .= $piece;
1024             }
1025             return $seq;
1026             } elsif( defined $start && defined $end ) {
1027             if( $start > $end ){
1028             $self->throw("Bad start,end parameters. Start [$start] has to be ".
1029             "less than end [$end]");
1030             }
1031             if( $start <= 0 ) {
1032             $self->throw("Bad start parameter ($start). Start must be positive.");
1033             }
1034             if( $end > $self->length ) {
1035             $self->throw("Bad end parameter ($end). End must be less than the total length of sequence (total=".$self->length.")");
1036             }
1037              
1038             # remove one from start, and then length is end-start
1039             $start--;
1040             if( defined $replace ) {
1041             return substr( $self->seq(), $start, ($end-$start), $replace);
1042             } else {
1043             return substr( $self->seq(), $start, ($end-$start));
1044             }
1045             } else {
1046             $self->warn("Incorrect parameters to subseq - must be two integers or a Bio::LocationI object. Got:", $self,$start,$end,$replace);
1047             return;
1048             }
1049             }
1050              
1051             sub write_GFF { $logger->warn }
1052              
1053             sub annotation { $logger->warn }
1054              
1055             sub species { $logger->warn }
1056              
1057             sub primary_seq { $logger->warn }
1058              
1059             sub accession_number { $logger->warn }
1060              
1061             sub alphabet {
1062             my $self = shift;
1063             my $type = $self->get_type;
1064             return lc $type;
1065             }
1066              
1067             sub can_call_new { $logger->warn }
1068              
1069             sub desc {
1070             my ( $self, $desc ) = @_;
1071             if ( defined $desc ) {
1072             $self->set_desc( $desc );
1073             }
1074             return $self->get_desc;
1075             }
1076              
1077             sub display_id { shift->get_name }
1078              
1079             sub id { shift->get_name }
1080              
1081             sub is_circular { $logger->warn }
1082              
1083             sub length { shift->get_length }
1084              
1085             sub moltype { shift->alphabet }
1086              
1087             sub primary_id { $logger->warn }
1088              
1089             sub revcom { $logger->warn }
1090              
1091             sub translate { $logger->warn }
1092              
1093             sub trunc { $logger->warn }
1094              
1095             sub get_nse{
1096             my ($self,$char1,$char2) = @_;
1097              
1098             $char1 ||= "/";
1099             $char2 ||= "-";
1100              
1101             $self->throw("Attribute id not set") unless defined($self->id());
1102             $self->throw("Attribute start not set") unless defined($self->start());
1103             $self->throw("Attribute end not set") unless defined($self->end());
1104              
1105             return $self->id() . $char1 . $self->start . $char2 . $self->end ;
1106              
1107             }
1108              
1109             sub strand {
1110             my ( $self, $strand ) = @_;
1111             if ( defined $strand ) {
1112             $self->set_generic( 'strand' => $strand );
1113             }
1114             return $self->get_generic( 'strand' );
1115             }
1116              
1117             sub start {
1118             my ( $self, $start ) = @_;
1119             if ( defined $start ) {
1120             $self->set_position( $start );
1121             }
1122             return $self->get_position;
1123             }
1124              
1125             sub end {
1126             my ( $self, $end ) = @_;
1127             if ( defined $end ) {
1128             $self->set_generic( 'end' => $end );
1129             }
1130             $end = $self->get_generic( 'end' );
1131             if ( defined $end ) {
1132             return $end;
1133             }
1134             else {
1135             return scalar( @{ $self->get_entities } ) + $self->get_position - 1;
1136             }
1137             }