2 # BioPerl module for Bio::SearchIO::hmmer3
4 # Please direct questions and support issues to <bioperl-l@bioperl.org>
6 # Cared for by Thomas Sharpton <thomas.sharpton@gmail.com>
8 # Copyright Thomas Sharpton
10 # You may distribute this module under the same terms as perl itself
12 # POD documentation - main docs before the code
22 my $searchio = Bio::SearchIO->new(
25 -file => 'hmmsearch.out'
28 my $result = $searchio->next_result;
29 my $hit = $result->next_hit;
30 print $hit->name, $hit->description, $hit->significance,
33 my $hsp = $hit->next_hsp;
34 print $hsp->start('hit'), $hsp->end('hit'), $hsp->start('query'),
35 $hsp->end('query'), "\n";
39 Code to parse output from hmmsearch, hmmscan, and nhmmer, compatible with
40 both version 2 and version 3 of the HMMER package from L<http://hmmer.org>.
46 User feedback is an integral part of the evolution of this and other
47 Bioperl modules. Send your comments and suggestions preferably to
48 the Bioperl mailing list. Your participation is much appreciated.
50 bioperl-l@bioperl.org - General discussion
51 http://bioperl.org/wiki/Mailing_lists - About the mailing lists
55 Please direct usage questions or support issues to the mailing list:
57 L<bioperl-l@bioperl.org>
59 rather than to the module maintainer directly. Many experienced and
60 reponsive experts will be able look at the problem and quickly
61 address it. Please include a thorough description of the problem
62 with code and data examples if at all possible.
66 Report bugs to the Bioperl bug tracking system to help us keep track
67 of the bugs and their resolution. Bug reports can be submitted via
70 https://redmine.open-bio.org/projects/bioperl/
72 =head1 AUTHOR - Thomas Sharpton
74 Email thomas.sharpton@gmail.com
76 Describe contact details here
80 Additional contributors names and emails here
86 The rest of the documentation details each of the object methods.
87 Internal methods are usually preceded with a _
91 # Let the code begin...
93 package Bio
::SearchIO
::hmmer3
;
97 use Bio
::Factory
::ObjectFactory
;
98 use vars
qw(%MAPPING %MODEMAP);
99 use base qw(Bio::SearchIO::hmmer);
103 # mapping of HMMER items to Bioperl hash keys
105 'HMMER_Output' => 'result',
111 'Hsp_bit-score' => 'HSP-bits',
112 'Hsp_score' => 'HSP-score',
113 'Hsp_evalue' => 'HSP-evalue',
114 'Hsp_query-from' => 'HSP-query_start',
115 'Hsp_query-to' => 'HSP-query_end',
116 'Hsp_query-strand' => 'HSP-query_strand',
117 'Hsp_hit-from' => 'HSP-hit_start',
118 'Hsp_hit-to' => 'HSP-hit_end',
119 'Hsp_hit-strand' => 'HSP-hit_strand',
120 'Hsp_positive' => 'HSP-conserved',
121 'Hsp_identity' => 'HSP-identical',
122 'Hsp_gaps' => 'HSP-hsp_gaps',
123 'Hsp_hitgaps' => 'HSP-hit_gaps',
124 'Hsp_querygaps' => 'HSP-query_gaps',
125 'Hsp_qseq' => 'HSP-query_seq',
126 'Hsp_hseq' => 'HSP-hit_seq',
127 'Hsp_midline' => 'HSP-homology_seq',
128 'Hsp_align-len' => 'HSP-hsp_length',
129 'Hsp_query-frame' => 'HSP-query_frame',
130 'Hsp_hit-frame' => 'HSP-hit_frame',
132 'Hit_id' => 'HIT-name',
133 'Hit_len' => 'HIT-length',
134 'Hit_accession' => 'HIT-accession',
135 'Hit_desc' => 'HIT-description',
136 'Hit_signif' => 'HIT-significance',
137 'Hit_score' => 'HIT-score',
139 'HMMER_program' => 'RESULT-algorithm_name',
140 'HMMER_version' => 'RESULT-algorithm_version',
141 'HMMER_query-def' => 'RESULT-query_name',
142 'HMMER_query-len' => 'RESULT-query_length',
143 'HMMER_query-acc' => 'RESULT-query_accession',
144 'HMMER_querydesc' => 'RESULT-query_description',
145 'HMMER_hmm' => 'RESULT-hmm_name',
146 'HMMER_seqfile' => 'RESULT-sequence_file',
147 'HMMER_db' => 'RESULT-database_name',
154 Usage : my $hit = $searchio->next_result;
155 Function: Returns the next Result from a search
156 Returns : Bio::Search::Result::ResultI object
163 my $seentop = 0; # Placeholder for when we deal with multi-query reports
165 my ( $last, @hit_list, @hsp_list, %hspinfo, %hitinfo, %domaincounter );
169 my $verbose = $self->verbose; # cache for speed? JES's idea in hmmer.pm
170 $self->start_document();
173 # This is here to ensure that next_result doesn't produce infinite loop
174 if ( !defined( $_ = $self->_readline ) ) {
178 $self->_pushback($_);
181 # Regex goes here for HMMER3
182 # Start with hmmsearch processing
183 while ( defined( $_ = $self->_readline ) ) {
187 # Grab the program name
188 if ( $_ =~ m/^\#\s(\S+)\s\:\:\s/ ) {
191 # TO DO: customize the above regex to adapt to other
192 # program types (hmmscan, etc)
193 $self->start_element( { 'Name' => 'HMMER_Output' } );
194 $self->{'_result_count'}++; #Might need to move to another block
196 { 'Name' => 'HMMER_program',
202 # Get the HMMER package version and release date
203 elsif ( $_ =~ m/^\#\sHMMER\s+(\S+)\s+\((.+)\)/ ) {
205 my $versiondate = $2;
206 $self->{'_hmmidline'} = $_;
208 { 'Name' => 'HMMER_version',
215 elsif ( $_ =~ /^\#\squery \w+ file\:\s+(\S+)/ ) {
216 if ( $self->{'_reporttype'} eq 'HMMSEARCH'
217 || $self->{'_reporttype'} eq 'NHMMER' )
219 $self->{'_hmmfileline'} = $lineorig;
221 { 'Name' => 'HMMER_hmm',
226 elsif ( $self->{'_reporttype'} eq 'HMMSCAN' ) {
227 $self->{'_hmmseqline'} = $lineorig;
229 { 'Name' => 'HMMER_seqfile',
236 # If this is a report without alignments
237 elsif ( $_ =~ m/^\#\sshow\salignments\sin\soutput/ ) {
238 $self->{'_alnreport'} = 0;
241 # Get the database info
242 elsif ( $_ =~ m/^\#\starget\s\S+\sdatabase\:\s+(\S+)/ ) {
244 if ( $self->{'_reporttype'} eq 'HMMSEARCH'
245 || $self->{'_reporttype'} eq 'NHMMER' )
247 $self->{'_hmmseqline'} = $lineorig;
249 { 'Name' => 'HMMER_seqfile',
254 elsif ( $self->{'_reporttype'} eq 'HMMSCAN' ) {
255 $self->{'_hmmfileline'} = $lineorig;
257 { 'Name' => 'HMMER_hmm',
265 elsif ( $_ =~ s/^Query:\s+// ) {
267 # TO DO: code to deal with multi-query report
268 unless (s/\s+\[[L|M]\=(\d+)\]$//) {
269 warn "Error parsing length for query, offending line $_\n";
274 { 'Name' => 'HMMER_query-len',
279 { 'Name' => 'HMMER_query-def',
286 elsif ( $_ =~ s/^Accession:\s+// ) {
289 { 'Name' => 'HMMER_query-acc',
295 # Get description data
296 elsif ( $_ =~ s/^Description:\s+// ) {
299 { 'Name' => 'HMMER_querydesc',
305 # hmmsearch, nhmmer, and hmmscan-specific formatting here
307 defined $self->{'_reporttype'}
308 && ( $self->{'_reporttype'} eq 'HMMSEARCH'
309 || $self->{'_reporttype'} eq 'HMMSCAN'
310 || $self->{'_reporttype'} eq 'NHMMER' )
313 # Complete sequence table data above inclusion threshold,
314 # hmmsearch or hmmscan
315 if ( $_ =~ m/Scores for complete sequence/ ) {
316 while ( defined( $_ = $self->_readline ) ) {
317 if ( $_ =~ m/inclusion threshold/
318 || m/Domain( and alignment)? annotation for each/
319 || m/\[No hits detected/
322 $self->_pushback($_);
327 next if ( m/\-\-\-/ || m/^\s+E-value\s+score/ || m/^$/ );
328 my ($eval_full, $score_full, $bias_full, $eval_best,
329 $score_best, $bias_best, $exp, $n,
330 $hitid, $desc, @hitline
332 @hitline = split( " ", $_ );
333 $eval_full = shift @hitline;
334 $score_full = shift @hitline;
335 $bias_full = shift @hitline;
336 $eval_best = shift @hitline;
337 $score_best = shift @hitline;
338 $bias_best = shift @hitline;
339 $exp = shift @hitline;
341 $hitid = shift @hitline;
342 $desc = join " ", @hitline;
344 if ( !defined($desc) ) {
348 [ $hitid, $desc, $eval_full, $score_full ];
349 $hitinfo{$hitid} = $#hit_list;
354 if ( /Scores for complete hits/ ) {
355 while ( defined( $_ = $self->_readline ) ) {
357 if ( /inclusion threshold/
358 || /Annotation for each hit/
359 || /\[No hits detected/
362 $self->_pushback($_);
367 next if ( /\-\-\-/ || /^\s+E-value\s+score/ || /^$/ );
368 my ($eval, $score, $bias, $hitid,
369 $start, $end, $desc, @hitline
371 @hitline = split( " ", $_ );
372 $eval = shift @hitline;
373 $score = shift @hitline;
374 $bias = shift @hitline;
375 $hitid = shift @hitline;
376 $start = shift @hitline;
377 $end = shift @hitline;
378 $desc = join ' ', @hitline;
380 $desc = '' if ( !defined($desc) );
382 push @hit_list, [ $hitid, $desc, $eval, $score ];
383 $hitinfo{$hitid} = $#hit_list;
387 # Complete sequence table data below inclusion threshold
388 # not currently fully implemented
389 elsif ( /inclusion threshold/ ) {
390 while ( defined( $_ = $self->_readline ) ) {
391 if ( /Domain( and alignment)? annotation for each/
392 || /Internal pipeline statistics summary/
393 || /Annotation for each hit\s+\(and alignments\)/
396 $self->_pushback($_);
399 next if ( $_ =~ m/^$/ );
400 my ($eval_full, $score_full, $bias_full, $eval_best,
401 $score_best, $bias_best, $exp, $n,
402 $hitid, $desc, @hitline
404 @hitline = split( " ", $_ );
405 $eval_full = shift @hitline;
406 $score_full = shift @hitline;
407 $bias_full = shift @hitline;
408 $eval_best = shift @hitline;
409 $score_best = shift @hitline;
410 $bias_best = shift @hitline;
411 $exp = shift @hitline;
413 $hitid = shift @hitline;
414 $desc = join " ", @hitline;
416 $hitinfo{$hitid} = "below_inclusion";
420 # Domain annotation for each sequence table data, hmmscan
421 elsif ( /Domain( and alignment)? annotation for each/ ) {
422 @hsp_list = (); # Here for multi-query reports
425 while ( defined( $_ = $self->_readline ) ) {
426 if ( /Internal pipeline statistics/
427 || /\[No targets detected/ )
429 $self->_pushback($_);
432 if ( $_ =~ m/^\>\>\s(.*?)\s+/ ) {
435 # Skip hits below inclusion threshold
436 next if ( $hitinfo{$name} eq "below_inclusion" );
437 $domaincounter{$name} = 0;
439 while ( defined( $_ = $self->_readline ) ) {
441 # Grab table data for sequence
442 if ( $_ =~ m/Internal pipeline statistics/
445 $self->_pushback($_);
448 if ( $_ =~ m/Alignments for each domain/ ) {
449 $self->_pushback($_);
452 if ( $_ =~ m/^\s+\#\s+score/
453 || $_ =~ m/^\s\-\-\-\s+/
463 # Grab hsp data from table, push into @hsp;
464 if (my ($domain_num, $score, $bias,
465 $ceval, $ieval, $hmmstart,
466 $hmmstop, $qalistart, $qalistop,
467 $envstart, $envstop, $envbound,
470 = m
|^\s
+(\d
+)\s\
!*\?*\s
+ # domain number
471 (\S
+)\s
+(\S
+)\s
+ # score, bias
472 (\S
+)\s
+(\S
+)\s
+ # c-eval, i-eval
473 (\d
+)\s
+(\d
+).+?
# hmm start, stop
474 (\d
+)\s
+(\d
+).+?
# query start, stop
475 (\d
+)\s
+(\d
+).+?
# env start, stop
480 # Keep it simple for now. let's customize later
483 $qalistart, $qalistop,
488 my $info = $hit_list[ $hitinfo{$name} ];
489 if ( !defined $info ) {
491 "Incomplete sequence information; can't find $name, hitinfo says $hitinfo{$name}\n"
495 $domaincounter{$name}++;
497 = $name . "_" . $domaincounter{$name};
498 push @hsp_list, [ $name, @vals ];
499 $hspinfo{$hsp_key} = $#hsp_list;
502 print "missed this line: $_\n";
506 elsif ( /Alignments for each domain/ ) {
507 my $domain_count = 0;
512 # There's an optional block, so we sometimes need to
513 # count to 3, and sometimes to 4.
517 my ( $hline, $midline, $qline );
519 while ( defined( $_ = $self->_readline ) ) {
521 || $_ =~ m/Internal pipeline statistics/ )
523 $self->_pushback($_);
526 elsif ($hitinfo{$name} eq "below_inclusion"
531 elsif ( $_ =~ /\s\s\=\=\sdomain\s(\d+)\s+/ ) {
534 my $key = $name . "_" . $domainnum;
535 $hsp = $hsp_list[ $hspinfo{$key} ];
537 $midline = $$hsp[-2];
542 # model data track, some reports don't have
543 elsif ( $_ =~ m/\s+\S+\sCS$/ ) {
549 elsif ( $count == $max_count - 3 ) {
552 my @data = split( " ", $_ );
558 elsif ( $count == $max_count - 2 ) {
561 # storage isn't quite right - need to remove
562 # leading/lagging whitespace while preserving
563 # gap data (latter isn't done, former is)
570 elsif ( $count == $max_count - 1 ) {
573 my @data = split( " ", $_ );
579 elsif ( $count == $max_count ) {
586 $$hsp[-2] = $midline;
598 # Annotation for each hit, nhmmer
599 # This code is currently incomplete, the alignment strings
600 # are not being captured
601 elsif ( /Annotation for each hit\s+\(and alignments\)/ ) {
605 while ( defined( $_ = $self->_readline ) ) {
606 if ( $_ =~ m/Internal pipeline statistics/
607 || m/\[No targets detected/ )
609 $self->_pushback($_);
612 if ( /^>>\s+(\S+)\s+/ ) {
615 while ( defined( $_ = $self->_readline ) ) {
617 if ( $_ =~ m/Internal pipeline statistics/
620 $self->_pushback($_);
624 $_ =~ /^\s+#\s+score/
625 || $_ =~ /^\s+------\s+/
628 || $_ =~ /^\s+Alignment:/
629 || $_ =~ /^\s+score:/
630 || $_ =~ /^\s+score\s+bias/
631 || $_ =~ /^\s+\S+\s+\d+\s+([\s+gatc-]+)/ # Alignment, line 1
632 || $_ =~ /^\s{20,}([\s+gatc-]+)/ # Alignment, line 2
633 || $_ =~ /^\s+$name\s+\d+\s+([\s+GATC-]+)/ # Alignment, line 3
634 || $_ =~ /^\s+[\d.\*]+/ # Alignment, line 4
642 (\d
+)\s
+(\d
+)\s
+[.\
[\
]]*\s
+
643 (\d
+)\s
+(\d
+)\s
+[.\
[\
]]*\s
+
644 (\d
+)\s
+(\d
+)\s
+[.\
[\
]]*\s
+
648 my ($score, $bias, $eval,
649 $hmmstart, $hmmstop, $hitstart,
650 $hitstop, $envstart, $envstop,
654 $1, $2, $3, $4, $5, $6,
665 my $info = $hit_list[ $hitinfo{$name} ];
666 if ( !defined $info ) {
668 "Incomplete information: can't find HSP $name in list of hits\n"
672 $domaincounter{$name}++;
673 my $hsp_key = $name . "_" . $domaincounter{$name};
674 push @hsp_list, [ $name, @vals ];
675 $hspinfo{$hsp_key} = $#hsp_list;
678 print "Missed this line: $_\n";
685 elsif ( m/Internal pipeline statistics/ || m!^//! ) {
687 # If within hit, hsp close;
688 if ( $self->within_element('hit') ) {
689 if ( $self->within_element('hsp') ) {
690 $self->end_element( { 'Name' => 'Hsp' } );
692 $self->end_element( { 'Name' => 'Hit' } );
695 # Grab summary statistics of run
696 while ( defined( $_ = $self->_readline ) ) {
697 last if ( $_ =~ m/^\/\
/$/ );
700 # Do a lot of processing of hits and hsps here
701 while ( my $hit = shift @hit_list ) {
702 my $hit_name = shift @
$hit;
703 my $hit_desc = shift @
$hit;
704 my $hit_signif = shift @
$hit;
705 my $hit_score = shift @
$hit;
706 my $num_domains = $domaincounter{$hit_name} || 0;
708 $self->start_element( { 'Name' => 'Hit' } );
710 { 'Name' => 'Hit_id',
715 { 'Name' => 'Hit_desc',
720 { 'Name' => 'Hit_signif',
721 'Data' => $hit_signif
725 { 'Name' => 'Hit_score',
730 for my $i ( 1 .. $num_domains ) {
731 my $key = $hit_name . "_" . $i;
732 my $hsp = $hsp_list[ $hspinfo{$key} ];
733 if ( defined $hsp ) {
734 my $hsp_name = shift @
$hsp;
735 $self->start_element( { 'Name' => 'Hsp' } );
737 { 'Name' => 'Hsp_identity',
742 { 'Name' => 'Hsp_positive',
747 { 'Name' => 'Hsp_hit-from',
748 'Data' => shift @
$hsp
752 { 'Name' => 'Hsp_hit-to',
753 'Data' => shift @
$hsp
757 { 'Name' => 'Hsp_query-from',
758 'Data' => shift @
$hsp
762 { 'Name' => 'Hsp_query-to',
763 'Data' => shift @
$hsp
767 { 'Name' => 'Hsp_score',
768 'Data' => shift @
$hsp
772 { 'Name' => 'Hsp_evalue',
773 'Data' => shift @
$hsp
777 { 'Name' => 'Hsp_hseq',
778 'Data' => shift @
$hsp
782 { 'Name' => 'Hsp_midline',
783 'Data' => shift @
$hsp
787 { 'Name' => 'Hsp_qseq',
788 'Data' => shift @
$hsp
792 # Only nhmmer output has strand information
793 if ( $self->{'_reporttype'} eq 'NHMMER' ) {
794 my $hstart = $self->get_from_element('HSP-hit_start');
795 my $hend = $self->get_from_element('HSP-hit_end');
796 my $hstrand = ( $hstart < $hend ) ?
1 : -1;
798 my $qstart = $self->get_from_element('HSP-query_start');
799 my $qend = $self->get_from_element('HSP-query_end');
800 my $qstrand = ( $qstart < $qend ) ?
1 : -1;
803 { 'Name' => 'Hsp_query-strand',
808 { 'Name' => 'Hsp_hit-strand',
814 $self->end_element( { 'Name' => 'Hsp' } );
817 $self->end_element( { 'Name' => 'Hit' } );
825 print "Missed line: $_\n";
830 $self->end_element( { 'Name' => 'HMMER_Output' } );
831 my $result = $self->end_document();
837 Title : start_element
838 Usage : $eventgenerator->start_element
839 Function: Handles a start event
841 Args : hashref with at least 2 keys 'Data' and 'Name'
847 my ( $self, $data ) = @_;
849 # we currently don't care about attributes
850 my $nm = $data->{'Name'};
851 my $type = $MODEMAP{$nm};
853 if ( $self->_eventHandler->will_handle($type) ) {
854 my $func = sprintf( "start_%s", lc $type );
855 $self->_eventHandler->$func( $data->{'Attributes'} );
857 unshift @
{ $self->{'_elements'} }, $type;
860 && $type eq 'result' )
862 $self->{'_values'} = {};
863 $self->{'_result'} = undef;
870 Usage : $eventgeneartor->end_element
871 Function: Handles and end element event
873 Args : hashref with at least 2 keys 'Data' and 'Name'
879 my ( $self, $data ) = @_;
880 my $nm = $data->{'Name'};
881 my $type = $MODEMAP{$nm};
884 if ( $nm eq 'HMMER_program' ) {
885 if ( $self->{'_last_data'} =~ /(N?HMM\S+)/i ) {
886 $self->{'_reporttype'} = uc $1;
890 # Hsp are sort of weird, in that they end when another
891 # object begins so have to detect this in end_element for now
892 if ( $nm eq 'Hsp' ) {
893 foreach (qw(Hsp_qseq Hsp_midline Hsp_hseq)) {
894 my $data = $self->{'_last_hspdata'}->{$_};
895 if ( $data && $_ eq 'Hsp_hseq' ) {
897 # replace hmm '.' gap symbol by '-'
906 $self->{'_last_hspdata'} = {};
909 if ( $self->_eventHandler->will_handle($type) ) {
910 my $func = sprintf( "end_%s", lc $type );
911 $rc = $self->_eventHandler->$func( $self->{'_reporttype'},
912 $self->{'_values'} );
914 my $lastelem = shift @
{ $self->{'_elements'} };
916 elsif ( $MAPPING{$nm} ) {
917 if ( ref( $MAPPING{$nm} ) =~ /hash/i ) {
918 my $key = ( keys %{ $MAPPING{$nm} } )[0];
919 $self->{'_values'}->{$key}->{ $MAPPING{$nm}->{$key} }
920 = $self->{'_last_data'};
923 $self->{'_values'}->{ $MAPPING{$nm} } = $self->{'_last_data'};
925 # print "lastdata is " . $self->{'_last_data'} . "\n";
929 $self->debug("unknown nm $nm, ignoring\n");
931 $self->{'_last_data'} = ''; # remove read data if we are at
933 $self->{'_result'} = $rc if ( defined $type && $type eq 'result' );
940 Usage : $eventhandler->element({'Name' => $name, 'Data' => $str});
941 Function: Convenience method that calls start_element, characters, end_element
943 Args : Hash ref with the keys 'Name' and 'Data'
948 my ( $self, $data ) = @_;
949 $self->start_element($data);
950 $self->characters($data);
951 $self->end_element($data);
954 =head2 get_from_element
956 Title : get_from_element
957 Usage : $self->get_from_element('HSP-hit_start');
958 Function: Convenience method to retrieve data from '_values' hash
964 sub get_from_element
{
965 my ($self,$key) = @_;
966 my $values = $self->{_values
};
973 Usage : $eventgenerator->characters($str)
974 Function: Send a character events
981 my ( $self, $data ) = @_;
983 if ( $self->in_element('hsp')
984 && $data->{'Name'} =~ /Hsp\_(qseq|hseq|midline)/o
985 && defined $data->{'Data'} )
987 $self->{'_last_hspdata'}->{ $data->{'Name'} } .= $data->{'Data'};
989 return unless ( defined $data->{'Data'} && $data->{'Data'} !~ /^\s+$/o );
991 $self->{'_last_data'} = $data->{'Data'};
994 =head2 within_element
996 Title : within_element
997 Usage : if( $eventgenerator->within_element( $element ) ) {}
998 Function: Test if we are within a particular element
999 This is different than 'in' because within can be tested for
1002 Args : string element name
1006 sub within_element
{
1007 my ( $self, $name ) = @_;
1010 || !defined $self->{'_elements'}
1011 || scalar @
{ $self->{'_elements'} } == 0 );
1012 foreach ( @
{ $self->{'_elements'} } ) {
1013 return 1 if ( $_ eq $name );
1021 Usage : if( $eventgenerator->in_element( $element ) ) {}
1022 Function: Test if we are in a particular element
1023 This is different than 'within' because 'in' only
1024 tests its immediate parent
1026 Args : string element name
1031 my ( $self, $name ) = @_;
1032 return 0 if !defined $self->{'_elements'}->[0];
1033 return ( $self->{'_elements'}->[0] eq $name );
1036 =head2 start_document
1038 Title : start_document
1039 Usage : $eventgenerator->start_document
1040 Function: Handle a start document event
1046 sub start_document
{
1048 $self->{'_lasttype'} = '';
1049 $self->{'_values'} = {};
1050 $self->{'_result'} = undef;
1051 $self->{'_elements'} = [];
1056 Title : end_document
1057 Usage : $eventgenerator->end_document
1058 Function: Handles an end document event
1059 Returns : Bio::Search::Result::ResultI object
1066 return $self->{'_result'};
1071 Title : result_count
1072 Usage : my $count = $searchio->result_count
1073 Function: Returns the number of results processed
1081 return $self->{'_result_count'};