bug 2549; fixed small bug in Bio::Taxon which doesn't catch -common_name
[bioperl-live.git] / Bio / TreeIO / phyloxml.pm
blob17414a4be83def117c4ff1b7884636e07939c85c
1 # $Id: phyloxml.pm 11507 2007-06-23 01:37:45Z jason $
3 # BioPerl module for Bio::TreeIO::phyloxml
5 # Cared for by Mira Han <mirhan@indiana.edu>
7 # Copyright Mira Han
9 # You may distribute this module under the same terms as perl itself
11 # POD documentation - main docs before the code
13 =head1 NAME
15 Bio::TreeIO::phyloxml - TreeIO implementation for parsing PhyloXML format.
17 =head1 SYNOPSIS
19 # do not use this module directly
20 use Bio::TreeIO;
21 my $treeio = Bio::TreeIO->new(-format => 'phyloxml',
22 -file => 'tree.dnd');
23 my $tree = $treeio->next_tree;
25 =head1 DESCRIPTION
27 This module handles parsing and writing of phyloXML format.
29 =head1 FEEDBACK
31 =head2 Mailing Lists
33 User feedback is an integral part of the evolution of this and other
34 Bioperl modules. Send your comments and suggestions preferably to the
35 Bioperl mailing list. Your participation is much appreciated.
37 bioperl-l@bioperl.org - General discussion
38 http://bioperl.org/wiki/Mailing_lists - About the mailing lists
40 =head2 Reporting Bugs
42 Report bugs to the Bioperl bug tracking system to help us keep track
43 of the bugs and their resolution. Bug reports can be submitted viax the
44 web:
46 http://bugzilla.open-bio.org/
48 =head1 AUTHOR - Mira Han
50 Email mirhan@indiana.edu
52 =head1 APPENDIX
54 The rest of the documentation details each of the object methods.
55 Internal methods are usually preceded with a _
57 =cut
60 # Let the code begin...
63 package Bio::TreeIO::phyloxml;
64 use strict;
66 # Object preamble - inherits from Bio::Root::Root
68 use Bio::Tree::Tree;
69 use Bio::Tree::AnnotatableNode;
70 use Bio::Annotation::SimpleValue;
71 use XML::LibXML;
72 use XML::LibXML::Reader;
73 use base qw(Bio::TreeIO);
75 sub _initialize
77 my($self, %args) = @_;
78 $args{-treetype} ||= 'Bio::Tree::Tree';
79 $args{-nodetype} ||= 'Bio::Tree::AnnotatableNode';
80 $self->SUPER::_initialize(%args);
82 # phyloxml TreeIO does not use SAX,
83 # therefore no need to attach EventHandler
84 # instead we will define a reader that is a pull-parser of libXML
85 if ($self->{'_file'}) {
86 $self->{'_reader'} = XML::LibXML::Reader->new(
87 location => $self->{'_file'},
88 no_blanks => 1
92 $self->treetype($args{-treetype});
93 $self->nodetype($args{-nodetype});
94 $self->{'_lastitem'} = {}; # holds open items and the attribute hash
95 $self->_init_func();
98 sub _init_func
100 my ($self) = @_;
101 my %start_elements = (
102 'phylogeny' => \&element_phylogeny,
103 'clade' => \&element_clade,
105 $self->{'_start_elements'} = \%start_elements;
106 my %end_elements = (
107 'phylogeny' => \&end_element_phylogeny,
108 'clade' => \&end_element_clade,
110 $self->{'_end_elements'} = \%end_elements;
114 =head2 next_tree
116 Title : next_tree
117 Usage : my $tree = $treeio->next_tree
118 Function: Gets the next tree in the stream
119 Returns : Bio::Tree::TreeI
120 Args : none
123 =cut
125 sub next_tree
127 my ($self) = @_;
128 my $reader = $self->{'_reader'};
129 my $tree;
130 while ($reader->read)
132 if ($reader->nodeType == XML_READER_TYPE_END_ELEMENT)
134 if ($reader->name eq 'phylogeny')
136 $tree = $self->end_element_phylogeny();
137 last;
140 processNode($self);
142 return $tree;
146 =head2 write_tree
148 Title : write_tree
149 Usage : $treeio->write_tree($tree);
150 Function: Write a tree out to data stream in phyloxml format
151 Returns : none
152 Args : Bio::Tree::TreeI object
154 =cut
156 sub write_tree{
157 my ($self, @trees) = @_;
158 foreach my $tree (@trees) {
159 my $root = $tree->get_root_node;
160 $self->_print("<phylogeny>");
161 $self->_print($self->_write_tree_Helper($root));
162 $self->_print("</phylogeny>");
163 $self->_print("\n");
165 $self->flush if $self->_flush_on_write && defined $self->_fh;
166 return;
170 sub _write_tree_Helper
172 my ($self, $node, $str) = @_; # this self is a Bio::Tree::phyloxml
173 if (ref($node) ne 'Bio::Tree::AnnotatableNode') {
174 $self->throw( "node must be a Bio::Tree::AnnotatableNode" );
176 my $ac = $node->annotation;
177 my $seq = $node->sequence;
179 # start <clade>
180 $str .= '<clade';
181 my @attr = $ac->get_Annotations('_attr'); # check id_source
182 if (@attr) {
183 my @id_source = $attr[0]->get_Annotations('id_source');
184 if (@id_source) {
185 $str .= " id_source=\"".$id_source[0]->value."\"";
188 $str .= ">";
190 # print all descendent nodes
191 foreach my $child ( $node->each_Descendent() ) {
192 $str = $self->_write_tree_Helper($child, $str);
195 # print all annotations
196 $str = print_annotation( $node, $str, $ac );
197 # print all sequences
198 if ($seq) {
199 $str = print_seq_annotation( $self, $str, $seq );
202 $str .= "</clade>";
203 return $str;
207 =head2 processNode
209 Title : processNode
210 Usage :
211 Function:
212 Returns : none
213 Args :
215 =cut
217 sub processNode
219 my ($self) = @_;
220 my $reader = $self->{'_reader'};
221 if ($reader->nodeType == XML_READER_TYPE_ELEMENT)
223 $self->{'_lastitem'}->{$reader->name}++;
224 push @{$self->{'_lastitem'}->{'current'}}, { $reader->name=>{}}; # current holds current element and empty hash for its attributes
226 if (exists $self->{'_start_elements'}->{$reader->name}) {
227 my $method = $self->{'_start_elements'}->{$reader->name};
228 $self->$method();
230 else {
231 $self->element_annotation();
233 if ($reader->isEmptyElement) {
234 # do procedures for XML_READER_TYPE_END_ELEMENT since element is complete
236 if (exists $self->{'_end_elements'}->{$reader->name}) {
237 my $method = $self->{'_end_elements'}->{$reader->name};
238 $self->$method();
240 else {
241 $self->end_element_annotation();
243 $self->{'_lastitem'}->{ $reader->name }--;
244 pop @{$self->{'_lastitem'}->{'current'}};
247 if ($reader->nodeType == XML_READER_TYPE_TEXT)
249 $self->{'_currenttext'} = $reader->value;
251 if ($reader->nodeType == XML_READER_TYPE_END_ELEMENT)
253 if (exists $self->{'_end_elements'}->{$reader->name}) {
254 my $method = $self->{'_end_elements'}->{$reader->name};
255 $self->$method();
257 else {
258 $self->end_element_annotation();
260 $self->{'_lastitem'}->{ $reader->name }--;
261 pop @{$self->{'_lastitem'}->{'current'}};
262 $self->{'_currenttext'} = '';
267 =head2 processAttribute
269 Title : processAttribute
270 Usage :
271 Function:
272 Example :
273 Returns :
274 Args :
276 =cut
278 sub processAttribute
280 my ($self, $data) = @_;
281 my $reader = $self->{'_reader'};
283 # several ways of reading attributes:
284 # read all attributes:
285 if ($reader-> moveToFirstAttribute) {
286 do {
287 $data->{$reader->name()} = $reader->value;
288 } while ($reader-> moveToNextAttribute);
289 $reader-> moveToElement;
291 # back at the element
292 # ...
294 # read a specific attribute:
295 #print "Attribute b: ",$reader-> getAttribute('b'),"\n";
299 =head2 element_phylogeny
301 Title : element_phylogeny
302 Usage : $handler->element_phylogeny
303 Function: Begins a Tree event cycle
304 Returns : none
305 Args : none
307 =cut
309 sub element_phylogeny
311 my ($self) = @_;
312 $self->{'_currentitems'} = []; # holds nodes while parsing current level
313 $self->{'_currentnodes'} = []; # holds nodes while constructing tree
314 $self->{'_currentannotation'} = []; # holds annotationcollection
315 $self->{'_currenttext'} = '';
316 $self->{'_levelcnt'} = [];
317 $self->{'_id_link'} = {};
319 $self->processAttribute($self->current_attr);
320 return;
323 sub end_element_phylogeny
325 my ($self) = @_;
327 my $root;
328 # if there is more than one node in _currentnodes
329 # aggregate the nodes into trees basically ad-hoc.
330 if ( @{$self->{'_currentnodes'}} > 1)
332 $root = $self->nodetype->new( -verbose => $self->verbose,
333 -id => '',
334 tostring => \&node_to_string,
336 while ( @{$self->{'_currentnodes'}} ) {
337 my ($node) = ( shift @{$self->{'_currentnodes'}});
338 $root->add_Descendent($node);
341 # if there is only one node in _currentnodes
342 # that node is root.
343 elsif ( @{$self->{'_currentnodes'}} == 1)
345 $root = shift @{$self->{'_currentnodes'}};
348 my $tree = $self->treetype->new(
349 -verbose => $self->verbose,
350 -root => $root,
351 -id => $self->current_attr->{'name'},
352 %{$self->current_attr}
354 foreach my $tag ( keys %{$self->current_attr} ) {
355 $tree->add_tag_value( $tag, $self->current_attr->{$tag} );
357 return $tree;
361 =head2 element_clade
363 Title : element_clade
364 Usage : $->element_clade
365 Function: Begins a clade cycle
366 Returns : none
367 Args : none
369 =cut
371 sub element_clade
373 my ($self) = @_;
374 my $reader = $self->{'_reader'};
375 my %data = (); # doesn't use current attribute in order to save memory
376 $self->processAttribute(\%data);
377 # create a node (Annotatable Node)
378 my $tnode = $self->nodetype->new( -verbose => $self->verbose,
379 -id => '',
380 tostring => \&node_to_string,
381 %data,
383 # add all attributes as tags (Annotation::SimpleValue)
384 foreach my $tag ( keys %data ) {
385 $tnode->add_tag_value( $tag, $data{$tag} );
387 # if there is id_source add clade to _id_link
388 if (exists $data{'id_source'}) {
389 $self->{'_id_link'}->{$data{'id_source'}} = $tnode;
391 # push into temporary list
392 push @{$self->{'_currentitems'}}, $tnode;
395 sub end_element_clade
397 my ($self) = @_;
398 my $reader = $self->{'_reader'};
400 my $curcount = scalar @{$self->{'_currentnodes'}};
401 my $level = $reader->depth() - 2;
402 my $childcnt = $self->{'_levelcnt'}->[$level+1] || 0;
404 # pop from temporary list
405 my $tnode = pop @{$self->{'_currentitems'}};
406 if ( $childcnt > 0) {
407 if( $childcnt > $curcount)
409 $self->throw("something wrong with event construction treelevel ".
410 "$level is recorded as having $childcnt nodes ".
411 "but current nodes at this level is $curcount\n");
413 my @childnodes = splice( @{$self->{'_currentnodes'}}, - $childcnt);
414 for ( @childnodes ) {
415 $tnode->add_Descendent($_);
417 $self->{'_levelcnt'}->[$level+1] = 0;
419 push @{$self->{'_currentnodes'}}, $tnode;
420 $self->{'_levelcnt'}->[$level]++;
425 =head2 element_annotation
427 Title : element_annotation
428 Usage : $->element_annotation
429 Function:
430 Returns : none
431 Args : none
433 =cut
435 sub element_annotation
437 my ($self) = @_;
438 my $reader = $self->{'_reader'};
439 my $current = $self->current_element();
440 my $prev = $self->prev_element();
442 # read attributes of element
443 $self->processAttribute($self->current_attr);
445 # check idref
446 my @idrefs = ();
447 map { if ($_ =~ /^id_ref/) {push @idrefs, $self->current_attr->{$_};} } keys %{$self->current_attr};
449 my @srcbyidrefs = ();
450 foreach my $idref (@idrefs) { push @srcbyidrefs, $self->{'_id_link'}->{$idref}; }
452 # exception when id_ref is defined but id_src is not, or vice versa.
453 if (@idrefs xor @srcbyidrefs) {
454 $self->throw("id_ref and id_src incompatible: @idrefs, @srcbyidrefs");
457 # we are annotating a Node
458 # set _currentannotation
459 if ( (@srcbyidrefs && $srcbyidrefs[0]->isa($self->nodetype)) || ((@srcbyidrefs == 0) && $prev eq 'clade') ) {
460 # find node to annotate
461 my $tnode;
462 if (@srcbyidrefs) {
463 $tnode = $srcbyidrefs[0];
465 else {
466 $tnode = $self->{'_currentitems'}->[-1];
468 my $ac = $tnode->annotation();
469 # add the new anncollection with the current element as key
470 my $newann = Bio::Annotation::Collection->new();
471 $ac->add_Annotation($current, $newann);
472 # push to current annotation
473 push (@{$self->{'_currentannotation'}}, $newann);
476 # we are already within an annotation
477 else {
478 my $ac = $self->{'_currentannotation'}->[-1];
479 if ($ac) {
480 # add the new anncollection with the current element as key
481 my $newann = Bio::Annotation::Collection->new();
482 $ac->add_Annotation($current, $newann);
483 push (@{$self->{'_currentannotation'}}, $newann);
489 =head2 end_element_annotation
491 Title : end_element_annotation
492 Usage : $->end_element_annotation
493 Function:
494 Returns : none
495 Args : none
497 =cut
499 sub end_element_annotation
501 my ($self) = @_;
502 my $reader = $self->{'_reader'};
503 my $current = $self->current_element();
504 my $prev = $self->prev_element();
506 # check idsrc
507 my $idsrc = $self->current_attr->{'id_source'};
509 # check idref
510 my @idrefs = ();
511 map { if ($_ =~ /^id_ref/) {
512 push @idrefs, $self->current_attr->{$_};
513 delete $self->current_attr->{$_};
514 } } keys %{$self->current_attr};
516 my @srcbyidrefs = ();
517 foreach my $idref (@idrefs) { push @srcbyidrefs, $self->{'_id_link'}->{$idref}; }
519 # exception when id_src is defined but id_ref is not, or vice versa.
520 if (@idrefs xor @srcbyidrefs) {
521 $self->throw("id_ref and id_src incompatible: @idrefs, @srcbyidrefs");
524 # we are annotating a Tree
525 if ( $prev eq 'phylogeny') {
526 if (@srcbyidrefs) {
527 # if annotation regards nodes
528 if ($srcbyidrefs[0]->isa($self->nodetype)) {
529 # goto case node
531 # if annotation regards sequences
532 elsif ($srcbyidrefs[0]->isa("Bio::SeqI")) {
533 # add code to implement sequence_relation among Bio::SeqI's
536 else {
537 # annotate Tree via tree attribute
538 $self->prev_attr->{$current} = $self->{'_currenttext'};
541 # we are annotating a Node
542 if (( @srcbyidrefs && $srcbyidrefs[0]->isa($self->nodetype)) || ((@srcbyidrefs == 0) && $prev eq 'clade'))
544 # pop from current annotation
545 my $ac = pop (@{$self->{'_currentannotation'}});
546 $self->annotateNode( $current, $ac);
547 # additional setups for compatibility with NodeI
548 my $tnode;
549 if (@srcbyidrefs) {
550 $tnode = $srcbyidrefs[0];
552 else {
553 $tnode = $self->{'_currentitems'}->[-1];
555 if ($current eq 'name') {
556 $tnode->id($self->{'_currenttext'});
558 elsif ($current eq 'branch_length') {
559 $tnode->branch_length($self->{'_currenttext'});
561 elsif ($current eq 'confidence') {
562 if ((exists $self->current_attr->{'type'}) && ($self->current_attr->{'type'} eq 'bootstrap')) {
563 $tnode->bootstrap($self->{'_currenttext'}); # this needs to change (adds 'B' annotation)
566 elsif ($current eq 'sequence') {
567 my $str = '';
568 if (my @molseq = $ac->get_Annotations('mol_seq')) {
569 my @strac = $molseq[0]->get_Annotations('_text');
570 $str = $strac[0]->value();
572 my $newseq = Bio::Seq->new(-seq => $str, -annotation=>$ac);
573 $tnode->sequence($newseq);
574 $ac->remove_Annotations('mol_seq');
575 $tnode->annotation->remove_Annotations($current);
576 # if there is id_source add sequence to _id_link
577 if ($idsrc) {
578 $self->{'_id_link'}->{$idsrc} = $newseq;
581 elsif ($idsrc && $current eq 'taxonomy') {
582 # if there is id_source add sequence to _id_link
583 $self->{'_id_link'}->{$idsrc} = $ac;
586 elsif ($prev eq 'clade_relation') {
588 # we are within an Annotation
589 else {
590 my $ac = pop (@{$self->{'_currentannotation'}});
591 if ($ac) {
592 $self->annotateNode( $current, $ac);
598 =head2 annotateNode
600 Title : annotateNode
601 Usage : $->annotateNode( $element, $ac)
602 Function:
603 Returns : none
604 Args : none
606 =cut
608 sub annotateNode
610 my ($self, $element, $newac) = @_;
611 # if attribute exists then add Annotation::Collection
612 if ( scalar keys %{$self->current_attr} ) {
613 my $newattr = Bio::Annotation::Collection->new();
614 foreach my $tag (keys %{$self->current_attr}) {
615 my $sv = new Bio::Annotation::SimpleValue(
616 -value => $self->current_attr->{$tag}
618 $newattr->add_Annotation($tag, $sv);
620 $newac->add_Annotation('_attr', $newattr);
622 # if text exists add text as SimpleValue
623 if ( $self->{'_currenttext'} ) {
624 my $newvalue = new Bio::Annotation::SimpleValue( -value => $self->{'_currenttext'} );
625 $newac->add_Annotation('_text', $newvalue);
630 =head2 element_id
632 Title : element_id
633 Usage : $->element_id
634 Function: identifier element used by phylogeny, clade, taxonomy
635 Returns : none
636 Args : none
638 =cut
640 sub element_id
642 my ($self) = @_;
643 my $reader = $self->{'_reader'};
648 =head2 current_attr
650 Title : current_attr
651 Usage :
652 Function: returns the attribute hash for current item
653 Example :
654 Returns :
655 Args :
657 =cut
659 sub current_attr {
660 my ($self) = @_;
662 return 0 if ! defined $self->{'_lastitem'} ||
663 ! defined $self->{'_lastitem'}->{'current'}->[-1];
664 my @keys = keys %{$self->{'_lastitem'}->{'current'}->[-1]};
665 (@keys == 1) || die "there should be only one key for each hash";
666 return $self->{'_lastitem'}->{'current'}->[-1]->{$keys[0]};
669 =head2 prev_attr
671 Title : prev_attr
672 Usage :
673 Function: returns the attribute hash for previous item
674 Example :
675 Returns :
676 Args :
678 =cut
680 sub prev_attr {
681 my ($self) = @_;
683 return 0 if ! defined $self->{'_lastitem'} ||
684 ! defined $self->{'_lastitem'}->{'current'}->[-2];
685 my @keys = keys %{$self->{'_lastitem'}->{'current'}->[-2]};
686 (@keys == 1) || die "there should be only one key for each hash";
687 return $self->{'_lastitem'}->{'current'}->[-2]->{$keys[0]};
690 =head2 current_element
692 Title : current_element
693 Usage :
694 Function:
695 Example :
696 Returns :
697 Args :
699 =cut
701 sub current_element {
702 my ($self) = @_;
704 return 0 if ! defined $self->{'_lastitem'} ||
705 ! defined $self->{'_lastitem'}->{'current'}->[-1];
706 my @keys = keys %{$self->{'_lastitem'}->{'current'}->[-1]};
707 (@keys == 1) || die "there should be only one key for each hash";
708 return $keys[0];
711 =head2 prev_element
713 Title : prev_element
714 Usage :
715 Function:
716 Example :
717 Returns :
718 Args :
720 =cut
722 sub prev_element {
723 my ($self) = @_;
725 return 0 if ! defined $self->{'_lastitem'} ||
726 ! defined $self->{'_lastitem'}->{'current'}->[-2];
727 my @keys = keys %{$self->{'_lastitem'}->{'current'}->[-2]};
728 (@keys == 1) || die "there should be only one key for each hash";
729 return $keys[0];
732 =head2 in_element
734 Title : in_element
735 Usage :
736 Function:
737 Example :
738 Returns :
739 Args :
742 =cut
744 sub in_element{
745 my ($self,$e) = @_;
747 return 0 if ! defined $self->{'_lastitem'} ||
748 ! defined $self->{'_lastitem'}->{'current'}->[-1];
749 my @keys = keys %{$self->{'_lastitem'}->{'current'}->[-1]};
750 (@keys == 1) || die "there should be only one key for each hash";
751 return ($e eq $keys[0]);
754 =head2 within_element
756 Title : within_element
757 Usage :
758 Function:
759 Example :
760 Returns :
761 Args :
764 =cut
766 sub within_element{
767 my ($self,$e) = @_;
768 return $self->{'_lastitem'}->{$e};
771 =head2 treetype
773 Title : treetype
774 Usage : $obj->treetype($newval)
775 Function:
776 Returns : value of treetype
777 Args : newvalue (optional)
780 =cut
782 sub treetype{
783 my ($self,$value) = @_;
784 if( defined $value) {
785 $self->{'treetype'} = $value;
787 return $self->{'treetype'};
790 =head2 nodetype
792 Title : nodetype
793 Usage : $obj->nodetype($newval)
794 Function:
795 Returns : value of nodetype
796 Args : newvalue (optional)
799 =cut
801 sub nodetype{
802 my ($self,$value) = @_;
803 if( defined $value) {
804 $self->{'nodetype'} = $value;
806 return $self->{'nodetype'};
810 =head1 Methods for implementing to_string callback for AnnotatableNode
812 =cut
814 =head2 node_to_string
816 Title : node_to_string
817 Usage : $annotatablenode->to_string_callback(\&node_to_string)
818 Function: set as callback in AnnotatableNode, prints the node information in string
819 Returns : string of node information
820 Args :
822 =cut
825 sub node_to_string
827 my ($self) = @_; # this self is a Bio::Tree::AnnotatableNode
828 # not a Bio::TreeIO::phyloxml
829 my $str = '';
830 my $ac = $self->annotation;
831 my $seq = $self->sequence;
833 # start <clade>
834 $str .= '<clade';
835 my @attr = $ac->get_Annotations('_attr'); # check id_source
836 if (@attr) {
837 my @id_source = $attr[0]->get_Annotations('id_source');
838 if (@id_source) {
839 $str .= " id_source=\"".$id_source[0]->value."\"";
842 $str .= '>';
844 # print all annotations
845 $str = print_annotation( $self, $str, $ac );
846 # print all sequences
847 if ($seq) {
848 $str = print_seq_annotation( $self, $str, $seq );
851 $str .= '</clade>';
852 return $str;
855 sub print_annotation
857 my ($self, $str, $ac) = @_;
859 my @all_anns = $ac->get_Annotations();
860 foreach my $ann (@all_anns) {
861 my $key = $ann->tagname;
862 if ($key eq '_attr') { next; } # attributes are already printed in the previous level
863 if (ref($ann) eq 'Bio::Annotation::SimpleValue')
865 if ($key eq '_text') {
866 $str .= $ann->value;
868 else {
869 $str .= "<$key>";
870 $str .= $ann->value;
871 $str .= "</$key>";
874 elsif (ref($ann) eq 'Bio::Annotation::Collection')
876 my @attrs = $ann->get_Annotations('_attr');
877 if (@attrs) { # if there is a attribute collection
878 $str .= "<$key";
879 $str = print_attr($self, $str, $attrs[0]);
880 $str .= ">";
882 else {
883 $str .= "<$key>";
885 $str = print_annotation($self, $str, $ann);
886 $str .= "</$key>";
889 return $str;
892 sub print_attr
894 my ($self, $str, $ac) = @_;
895 my @all_attrs = $ac->get_Annotations();
896 foreach my $attr (@all_attrs) {
897 if (ref($attr) ne 'Bio::Annotation::SimpleValue') {
898 $self->throw("attribute should be a SimpleValue");
900 $str .= ' ';
901 $str .= $attr->tagname;
902 $str .= '=';
903 $str .= $attr->value;
905 return $str;
908 sub print_seq_annotation
910 my ($self, $str, $seq) = @_;
912 $str .= "<sequence>";
913 my @all_anns = $seq->annotation->get_Annotations();
914 foreach my $ann (@all_anns) {
915 my $key = $ann->tagname;
916 if ($key eq '_attr') { next; } # attributes are already printed in the previous level
917 if (ref($ann) eq 'Bio::Annotation::SimpleValue')
919 if ($key eq '_text') {
920 $str .= $ann->value;
922 else {
923 $str .= "<$key>";
924 $str .= $ann->value;
925 $str .= "</$key>";
928 elsif (ref($ann) eq 'Bio::Annotation::Collection')
930 my @attrs = $ann->get_Annotations('_attr');
931 if (@attrs) { # if there is a attribute collection
932 $str .= "<$key";
933 $str = print_attr($self, $str, $attrs[0]);
934 $str .= ">";
936 else {
937 $str .= "<$key>";
939 $str = print_annotation($self, $str, $ann);
940 $str .= "</$key>";
943 # print mol_seq
944 if ($seq->seq()) {
945 $str .= "<mol_seq>";
946 $str .= $seq->seq();
947 $str .= "</mol_seq>";
950 $str .= "</sequence>";
951 return $str;