File Coverage

Bio/SearchIO/exonerate.pm
Criterion Covered Total %
statement 213 260 81.9
branch 60 90 66.6
condition 10 27 37.0
subroutine 14 18 77.7
pod 14 15 93.3
total 311 410 75.8


line stmt bran cond sub pod time code
1             #
2             # BioPerl module for Bio::SearchIO::exonerate
3             #
4             # Please direct questions and support issues to
5             #
6             # Cared for by Jason Stajich
7             #
8             # Copyright Jason Stajich
9             #
10             # You may distribute this module under the same terms as perl itself
11              
12             # POD documentation - main docs before the code
13              
14             =head1 NAME
15              
16             Bio::SearchIO::exonerate - parser for Exonerate
17              
18             =head1 SYNOPSIS
19              
20             # do not use this module directly, it is a driver for SearchIO
21              
22             use Bio::SearchIO;
23             my $searchio = Bio::SearchIO->new(-file => 'file.exonerate',
24             -format => 'exonerate');
25              
26              
27             while( my $r = $searchio->next_result ) {
28             print $r->query_name, "\n";
29             }
30              
31             =head1 DESCRIPTION
32              
33             This is a driver for the SearchIO system for parsing Exonerate (Guy
34             Slater) output. You can get Exonerate at
35             http://www.ebi.ac.uk/~guy/exonerate/
36             [until Guy puts up a Web reference,publication for it.]).
37              
38             An optional parameter -min_intron is supported by the L
39             initialization method. This is if you run Exonerate with a different
40             minimum intron length (default is 30) the parser will be able to
41             detect the difference between standard deletions and an intron. Still
42             some room to play with there that might cause this to get
43             misinterpreted that has not been fully tested or explored.
44              
45             The VULGAR and CIGAR formats should be parsed okay now creating HSPs
46             where appropriate (so merging match states where appropriate rather
47             than breaking an HSP at each indel as it may have done in the past).
48             The GFF that comes from exonerate is still probably a better way to go
49             if you are doing protein2genome or est2genome mapping.
50             For example you can see this script:
51              
52             ### TODO: Jason, this link is dead, do we have an updated one?
53             http://fungal.genome.duke.edu/~jes12/software/scripts/process_exonerate_gff3.perl.txt
54              
55             If your report contains both CIGAR and VULGAR lines only the first one
56             will processed for a given Query/Target pair. If you preferentially
57             want to use VULGAR or CIGAR add one of these options when initializing
58             the SearchIO object.
59              
60             -cigar => 1
61             OR
62             -vulgar => 1
63              
64             Or set them via these methods.
65              
66             $parser->cigar(1)
67             OR
68             $parser->vulgar(1)
69              
70              
71              
72             =head1 FEEDBACK
73              
74             =head2 Mailing Lists
75              
76             User feedback is an integral part of the evolution of this and other
77             Bioperl modules. Send your comments and suggestions preferably to
78             the Bioperl mailing list. Your participation is much appreciated.
79              
80             bioperl-l@bioperl.org - General discussion
81             http://bioperl.org/wiki/Mailing_lists - About the mailing lists
82              
83             =head2 Support
84              
85             Please direct usage questions or support issues to the mailing list:
86              
87             I
88              
89             rather than to the module maintainer directly. Many experienced and
90             reponsive experts will be able look at the problem and quickly
91             address it. Please include a thorough description of the problem
92             with code and data examples if at all possible.
93              
94             =head2 Reporting Bugs
95              
96             Report bugs to the Bioperl bug tracking system to help us keep track
97             of the bugs and their resolution. Bug reports can be submitted via the
98             web:
99              
100             https://github.com/bioperl/bioperl-live/issues
101              
102             =head1 AUTHOR - Jason Stajich
103              
104             Email jason-at-bioperl.org
105              
106             =head1 APPENDIX
107              
108             The rest of the documentation details each of the object methods.
109             Internal methods are usually preceded with a _
110              
111             =cut
112              
113              
114             # Let the code begin...
115              
116              
117             package Bio::SearchIO::exonerate;
118 1     1   3 use strict;
  1         1  
  1         27  
119 1     1   3 use vars qw(@STATES %MAPPING %MODEMAP $DEFAULT_WRITER_CLASS $MIN_INTRON);
  1         1  
  1         59  
120              
121 1     1   3 use base qw(Bio::SearchIO);
  1         2  
  1         1810  
122              
123             %MODEMAP = ( 'ExonerateOutput' => 'result',
124             'Hit' => 'hit',
125             'Hsp' => 'hsp'
126             );
127              
128             %MAPPING =
129             (
130             'Hsp_query-from'=> 'HSP-query_start',
131             'Hsp_query-to' => 'HSP-query_end',
132             'Hsp_hit-from' => 'HSP-hit_start',
133             'Hsp_hit-to' => 'HSP-hit_end',
134             'Hsp_qseq' => 'HSP-query_seq',
135             'Hsp_hseq' => 'HSP-hit_seq',
136             'Hsp_midline' => 'HSP-homology_seq',
137             'Hsp_score' => 'HSP-score',
138             'Hsp_qlength' => 'HSP-query_length',
139             'Hsp_hlength' => 'HSP-hit_length',
140             'Hsp_align-len' => 'HSP-hsp_length',
141             'Hsp_identity' => 'HSP-identical',
142             'Hsp_gaps' => 'HSP-hsp_gaps',
143             'Hsp_hitgaps' => 'HSP-hit_gaps',
144             'Hsp_querygaps' => 'HSP-query_gaps',
145              
146             'Hit_id' => 'HIT-name',
147             'Hit_desc' => 'HIT-description',
148             'Hit_len' => 'HIT-length',
149             'Hit_score' => 'HIT-score',
150              
151             'ExonerateOutput_program' => 'RESULT-algorithm_name',
152             'ExonerateOutput_query-def' => 'RESULT-query_name',
153             'ExonerateOutput_query-desc'=> 'RESULT-query_description',
154             'ExonerateOutput_query-len' => 'RESULT-query_length',
155             );
156              
157             $DEFAULT_WRITER_CLASS = 'Bio::SearchIO::Writer::HitTableWriter';
158              
159             $MIN_INTRON=30; # This is the minimum intron size
160              
161             =head2 new
162              
163             Title : new
164             Usage : my $obj = Bio::SearchIO::exonerate->new();
165             Function: Builds a new Bio::SearchIO::exonerate object
166             Returns : an instance of Bio::SearchIO::exonerate
167             Args : -min_intron => somewhat obselete option, how to determine if a
168             an indel is an intron or a local gap. Use VULGAR
169             rather than CIGAR to avoid this heuristic,default 30.
170             -cigar => 1 set this to 1 if you want to parse
171             CIGAR exclusively.
172             -vulgar => 1 set this to 1 if you want to parse VULGAR
173             exclusively, setting both to 1 will revert
174             to the default behavior of just parsing the
175             first line that it sees.
176              
177             =cut
178              
179             sub new {
180 5     5 1 6 my ($class) = shift;
181 5         15 my $self = $class->SUPER::new(@_);
182              
183 5         14 my ($min_intron,$cigar,
184             $vulgar) = $self->_rearrange([qw(MIN_INTRON
185             CIGAR
186             VULGAR)], @_);
187 5 50       12 if( $min_intron ) {
188 0         0 $MIN_INTRON = $min_intron;
189             }
190 5 50 33     12 if( $cigar && $vulgar ) {
191 0         0 $self->warn("cannot get HSPs from both CIGAR and VULGAR lines, will just choose whichever comes first (same as if you had chosen neither");
192 0         0 $cigar = 0; $vulgar=0;
  0         0  
193             }
194 5         10 $self->cigar($cigar);
195 5         10 $self->vulgar($vulgar);
196 5         35 $self;
197             }
198              
199             =head2 next_result
200              
201             Title : next_result
202             Usage : my $hit = $searchio->next_result;
203             Function: Returns the next Result from a search
204             Returns : Bio::Search::Result::ResultI object
205             Args : none
206              
207             =cut
208              
209             sub next_result{
210 10     10 1 1125 my ($self) = @_;
211 10         36 local $/ = "\n";
212 10         7 local $_;
213              
214 10         11 $self->{'_last_data'} = '';
215 10         10 my ($reporttype,$seenquery,$reportline);
216 10         18 $self->start_document();
217 10         10 my @hit_signifs;
218             my $seentop;
219 0         0 my (@q_ex, @m_ex, @h_ex); ## gc addition
220 10         30 while( defined($_ = $self->_readline) ) {
221             # warn( "Reading $_");
222 375 100       1175 if( /^\s*Query:\s+(\S+)\s*(.+)?/ ) {
    100          
    100          
    100          
223 6 50       11 if( $seentop ) {
224 0         0 $self->end_element({'Name' => 'ExonerateOutput'});
225 0         0 $self->_pushback($_);
226 0         0 return $self->end_document();
227             }
228 6         8 $seentop = 1;
229 6         15 my ($nm,$desc) = ($1,$2);
230 6 100       14 chomp($desc) if defined $desc;
231 6         9 $self->{'_result_count'}++;
232 6         23 $self->start_element({'Name' => 'ExonerateOutput'});
233 6         24 $self->element({'Name' => 'ExonerateOutput_query-def',
234             'Data' => $nm });
235 6         18 $self->element({'Name' => 'ExonerateOutput_query-desc',
236             'Data' => $desc });
237 6         17 $self->element({'Name' => 'ExonerateOutput_program',
238             'Data' => 'Exonerate' });
239 6         10 $self->{'_seencigar'} = 0;
240 6         20 $self->{'_vulgar'} = 0;
241              
242             } elsif ( /^Target:\s+(\S+)\s*(.+)?/ ) {
243 5         12 my ($nm,$desc) = ($1,$2);
244 5 100       12 chomp($desc) if defined $desc;
245 5         11 $self->start_element({'Name' => 'Hit'});
246 5         16 $self->element({'Name' => 'Hit_id',
247             'Data' => $nm});
248 5         16 $self->element({'Name' => 'Hit_desc',
249             'Data' => $desc});
250 5         7 $self->{'_seencigar'} = 0;
251 5         11 $self->{'_vulgar'} = 0;
252             } elsif( s/^vulgar:\s+(\S+)\s+ # query sequence id
253             (\d+)\s+(\d+)\s+([\-\+\.])\s+ # query start-end-strand
254             (\S+)\s+ # target sequence id
255             (\d+)\s+(\d+)\s+([\-\+])\s+ # target start-end-strand
256             (-?\d+)\s+ # score
257             //ox ) {
258 4 50 33     10 next if( $self->cigar || $self->{'_seencigar'});
259 4         5 $self->{'_vulgar'}++;
260             #
261             # Note from Ewan. This is ugly - copy and paste from
262             # cigar line parsing. Should unify somehow...
263             #
264 4 50       9 if( ! $self->within_element('result') ) {
265 0         0 $self->start_element({'Name' => 'ExonerateOutput'});
266 0         0 $self->element({'Name' => 'ExonerateOutput_query-def',
267             'Data' => $1 });
268             }
269 4 100       6 if( ! $self->within_element('hit') ) {
270 1         5 $self->start_element({'Name' => 'Hit'});
271 1         5 $self->element({'Name' => 'Hit_id',
272             'Data' => $5});
273             }
274              
275             ## gc note:
276             ## $qe and $he are no longer used for calculating the ends,
277             ## just the $qs and $hs values and the alignment and insert lenghts
278 4         13 my ($qs,$qe,$qstrand) = ($2,$3,$4);
279 4         7 my ($hs,$he,$hstrand) = ($6,$7,$8);
280 4         6 my $score = $9;
281             # $self->element({'Name' => 'ExonerateOutput_query-len',
282             # 'Data' => $qe});
283             # $self->element({'Name' => 'Hit_len',
284             # 'Data' => $he});
285              
286             ## gc note:
287             ## add one because these values are zero-based
288             ## this calculation was originally done lower in the code,
289             ## but it's clearer to do it just once at the start
290 4         26 my @rest = split;
291 4         6 my ($qbegin,$qend) = ('query-from', 'query-to');
292              
293 4 50       6 if( $qstrand eq '-' ) {
294 0         0 $qstrand = -1; $qe++;
  0         0  
295             } else {
296 4         5 $qstrand = 1;
297 4         4 $qs++;
298             }
299 4         6 my ($hbegin,$hend) = ('hit-from', 'hit-to');
300              
301 4 50       6 if( $hstrand eq '-' ) {
302 0         0 $hstrand = -1;
303 0         0 $he++;
304             } else {
305 4         4 $hstrand = 1;
306 4         242 $hs++;
307             }
308             # okay let's do this right and generate a set of HSPs
309             # from the cigar line/home/bio1/jes12/bin/exonerate --model est2genome --bestn 1 t/data/exonerate_cdna.fa t/data/exonerate_genomic_rev.fa
310              
311 4         4 my ($aln_len,$inserts,$deletes) = (0,0,0);
312 4         8 my ($laststate,@events,$gaps) =( '' );
313 4         9 while( @rest >= 3 ) {
314 60         58 my ($state,$len1,$len2) = (shift @rest, shift @rest, shift @rest);
315             #
316             # HSPs are only the Match cases; otherwise we just
317             # move the coordinates on by the correct amount
318             #
319              
320 60 100       63 if( $state eq 'M' ) {
321 20 100       19 if( $laststate eq 'G' ) {
322             # merge gaps across Match states so the HSP
323             # goes across
324 4         5 $events[-1]->{$qend} = $qs + $len1*$qstrand - $qstrand;
325 4         7 $events[-1]->{$hend} = $hs + $len2*$hstrand - $hstrand;
326 4         4 $events[-1]->{'gaps'} = $gaps;
327             } else {
328 16         54 push @events,
329             { 'score' => $score,
330             'align-len' => $len1,
331             $qbegin => $qs,
332             $qend => ($qs + $len1*$qstrand - $qstrand),
333             $hbegin => $hs,
334             $hend => ($hs + $len2*$hstrand - $hstrand),
335             };
336             }
337 20         15 $gaps = 0;
338             } else {
339 40 100       45 $gaps = $len1 + $len2 if $state eq 'G';
340             }
341 60         41 $qs += $len1*$qstrand;
342 60         35 $hs += $len2*$hstrand;
343 60         68 $laststate= $state;
344             }
345 4         6 for my $event ( @events ) {
346 16         30 $self->start_element({'Name' => 'Hsp'});
347 16         47 while( my ($key,$val) = each %$event ) {
348 100         185 $self->element({'Name' => "Hsp_$key",
349             'Data' => $val});
350             }
351 16         26 $self->element({'Name' => 'Hsp_identity',
352             'Data' => 0});
353 16         26 $self->end_element({'Name' => 'Hsp'});
354             }
355              
356             # end of hit
357 4         13 $self->element({'Name' => 'Hit_score',
358             'Data' => $score});
359             # issued end...
360 4         9 $self->end_element({'Name' => 'Hit'});
361 4         11 $self->end_element({'Name' => 'ExonerateOutput'});
362              
363 4         9 return $self->end_document();
364              
365             } elsif( s/^cigar:\s+(\S+)\s+ # query sequence id
366             (\d+)\s+(\d+)\s+([\-\+])\s+ # query start-end-strand
367             (\S+)\s+ # target sequence id
368             (\d+)\s+(\d+)\s+([\-\+])\s+ # target start-end-strand
369             (-?\d+)\s+ # score
370             //ox ) {
371 2 50 33     4 next if( $self->vulgar || $self->{'_seenvulgar'});
372 2         4 $self->{'_cigar'}++;
373              
374 2 50       5 if( ! $self->within_element('result') ) {
375 0         0 $self->start_element({'Name' => 'ExonerateOutput'});
376 0         0 $self->element({'Name' => 'ExonerateOutput_query-def',
377             'Data' => $1 });
378             }
379 2 50       5 if( ! $self->within_element('hit') ) {
380 0         0 $self->start_element({'Name' => 'Hit'});
381 0         0 $self->element({'Name' => 'Hit_id',
382             'Data' => $5});
383             }
384             ## gc note:
385             ## $qe and $he are no longer used for calculating the ends,
386             ## just the $qs and $hs values and the alignment and insert lenghts
387 2         7 my ($qs,$qe,$qstrand) = ($2,$3,$4);
388 2         5 my ($hs,$he,$hstrand) = ($6,$7,$8);
389 2         5 my $score = $9;
390             # $self->element({'Name' => 'ExonerateOutput_query-len',
391             # 'Data' => $qe});
392             # $self->element({'Name' => 'Hit_len',
393             # 'Data' => $he});
394              
395 2         14 my @rest = split;
396 2 50       6 if( $qstrand eq '-' ) {
397 2         2 $qstrand = -1;
398 2         3 ($qs,$qe) = ($qe,$qs); # flip-flop if we're on opp strand
399 2         5 $qs--; $qe++;
  2         6  
400 0         0 } else { $qstrand = 1; }
401 2 50       4 if( $hstrand eq '-' ) {
402 0         0 $hstrand = -1;
403 0         0 ($hs,$he) = ($he,$hs); # flip-flop if we're on opp strand
404 0         0 $hs--; $he++;
  0         0  
405 2         2 } else { $hstrand = 1; }
406             # okay let's do this right and generate a set of HSPs
407             # from the cigar line
408              
409             ## gc note:
410             ## add one because these values are zero-based
411             ## this calculation was originally done lower in the code,
412             ## but it's clearer to do it just once at the start
413 2         3 $qs++; $hs++;
  2         1  
414              
415 2         4 my ($aln_len,$inserts,$deletes) = (0,0,0);
416 2         16 while( @rest >= 2 ) {
417 10         14 my ($state,$len) = (shift @rest, shift @rest);
418 10 50       19 if( $state eq 'I' ) {
    100          
419 0         0 $inserts+=$len;
420             } elsif( $state eq 'D' ) {
421 4 50       8 if( $len >= $MIN_INTRON ) {
422 4         12 $self->start_element({'Name' => 'Hsp'});
423              
424 4         12 $self->element({'Name' => 'Hsp_score',
425             'Data' => $score});
426 4         11 $self->element({'Name' => 'Hsp_align-len',
427             'Data' => $aln_len});
428 4         11 $self->element({'Name' => 'Hsp_identity',
429             'Data' => $aln_len -
430             ($inserts + $deletes)});
431              
432             # HSP ends where the other begins
433 4         10 $self->element({'Name' => 'Hsp_query-from',
434             'Data' => $qs});
435             ## gc note:
436             ## $qs is now the start of the next hsp
437             ## the end of this hsp is 1 before this position
438             ## (or 1 after in case of reverse strand)
439 4         5 $qs += $aln_len*$qstrand;
440 4         10 $self->element({'Name' => 'Hsp_query-to',
441             'Data' => $qs - ($qstrand*1)});
442              
443 4         6 $hs += $deletes*$hstrand;
444 4         9 $self->element({'Name' => 'Hsp_hit-from',
445             'Data' => $hs});
446 4         5 $hs += $aln_len*$hstrand;
447 4         9 $self->element({'Name' => 'Hsp_hit-to',
448             'Data' => $hs-($hstrand*1)});
449              
450 4         10 $self->element({'Name' => 'Hsp_align-len',
451             'Data' => $aln_len + $inserts
452             + $deletes});
453 4         13 $self->element({'Name' => 'Hsp_identity',
454             'Data' => $aln_len });
455              
456 4         11 $self->element({'Name' => 'Hsp_gaps',
457             'Data' => $inserts + $deletes});
458 4         11 $self->element({'Name' => 'Hsp_querygaps',
459             'Data' => $inserts});
460 4         10 $self->element({'Name' => 'Hsp_hitgaps',
461             'Data' => $deletes});
462              
463             ## gc addition start
464              
465 4         9 $self->element({'Name' => 'Hsp_qseq',
466             'Data' => shift @q_ex,
467             });
468 4         10 $self->element({'Name' => 'Hsp_hseq',
469             'Data' => shift @h_ex,
470             });
471 4         10 $self->element({'Name' => 'Hsp_midline',
472             'Data' => shift @m_ex,
473             });
474             ## gc addition end
475 4         9 $self->end_element({'Name' => 'Hsp'});
476              
477 4         4 $aln_len = $inserts = $deletes = 0;
478             }
479 4         9 $deletes+=$len;
480             } else {
481 6         12 $aln_len += $len;
482             }
483             }
484 2         6 $self->start_element({'Name' => 'Hsp'});
485              
486             ## gc addition start
487              
488 2         6 $self->element({'Name' => 'Hsp_qseq',
489             'Data' => shift @q_ex,
490             });
491 2         26 $self->element({'Name' => 'Hsp_hseq',
492             'Data' => shift @h_ex,
493             });
494 2         6 $self->element({'Name' => 'Hsp_midline',
495             'Data' => shift @m_ex,
496             });
497             ## gc addition end
498              
499 2         6 $self->element({'Name' => 'Hsp_score',
500             'Data' => $score});
501              
502 2         5 $self->element({'Name' => 'Hsp_query-from',
503             'Data' => $qs});
504              
505 2         4 $qs += $aln_len*$qstrand;
506 2         7 $self->element({'Name' => 'Hsp_query-to',
507             'Data' => $qs - ($qstrand*1)});
508              
509 2         4 $hs += $deletes*$hstrand;
510 2         5 $self->element({'Name' => 'Hsp_hit-from',
511             'Data' => $hs});
512 2         2 $hs += $aln_len*$hstrand;
513 2         7 $self->element({'Name' => 'Hsp_hit-to',
514             'Data' => $hs -($hstrand*1)});
515              
516 2         6 $self->element({'Name' => 'Hsp_align-len',
517             'Data' => $aln_len});
518              
519 2         7 $self->element({'Name' => 'Hsp_identity',
520             'Data' => $aln_len - ($inserts + $deletes)});
521              
522 2         5 $self->element({'Name' => 'Hsp_gaps',
523             'Data' => $inserts + $deletes});
524              
525 2         5 $self->element({'Name' => 'Hsp_querygaps',
526             'Data' => $inserts});
527 2         6 $self->element({'Name' => 'Hsp_hitgaps',
528             'Data' => $deletes});
529 2         5 $self->end_element({'Name' => 'Hsp'});
530              
531 2         7 $self->element({'Name' => 'Hit_score',
532             'Data' => $score});
533              
534 2         7 $self->end_element({'Name' => 'Hit'});
535 2         5 $self->end_element({'Name' => 'ExonerateOutput'});
536              
537 2         7 return $self->end_document();
538             } else {
539             # skipping this line
540             }
541             }
542 4 50       17 return $self->end_document() if( $seentop );
543             }
544              
545             =head2 start_element
546              
547             Title : start_element
548             Usage : $eventgenerator->start_element
549             Function: Handles a start element event
550             Returns : none
551             Args : hashref with at least 2 keys 'Data' and 'Name'
552              
553              
554             =cut
555              
556             sub start_element{
557 271     271 1 203 my ($self,$data) = @_;
558             # we currently don't care about attributes
559 271         217 my $nm = $data->{'Name'};
560 271         217 my $type = $MODEMAP{$nm};
561              
562 271 100       355 if( $type ) {
563 34 50       59 if( $self->_eventHandler->will_handle($type) ) {
564 34         72 my $func = sprintf("start_%s",lc $type);
565 34         41 $self->_eventHandler->$func($data->{'Attributes'});
566             }
567 34         41 unshift @{$self->{'_elements'}}, $type;
  34         45  
568 34 100       63 if($type eq 'result') {
569 6         12 $self->{'_values'} = {};
570 6         9 $self->{'_result'}= undef;
571             }
572             }
573              
574             }
575              
576             =head2 end_element
577              
578             Title : start_element
579             Usage : $eventgenerator->end_element
580             Function: Handles an end element event
581             Returns : none
582             Args : hashref with at least 2 keys 'Data' and 'Name'
583              
584              
585             =cut
586              
587             sub end_element {
588 271     271 1 180 my ($self,$data) = @_;
589 271         176 my $nm = $data->{'Name'};
590 271         187 my $type = $MODEMAP{$nm};
591 271         163 my $rc;
592              
593 271 100       372 if( $type = $MODEMAP{$nm} ) {
    50          
594 34 50       55 if( $self->_eventHandler->will_handle($type) ) {
595 34         61 my $func = sprintf("end_%s",lc $type);
596             $rc = $self->_eventHandler->$func($self->{'_reporttype'},
597 34         40 $self->{'_values'});
598             }
599 34         26 shift @{$self->{'_elements'}};
  34         38  
600              
601             } elsif( $MAPPING{$nm} ) {
602              
603 237 50       247 if ( ref($MAPPING{$nm}) =~ /hash/i ) {
604 0         0 my $key = (keys %{$MAPPING{$nm}})[0];
  0         0  
605 0         0 $self->{'_values'}->{$key}->{$MAPPING{$nm}->{$key}} = $self->{'_last_data'};
606             } else {
607 237         320 $self->{'_values'}->{$MAPPING{$nm}} = $self->{'_last_data'};
608             }
609             } else {
610 0         0 $self->debug( "unknown nm $nm, ignoring\n");
611             }
612 271         190 $self->{'_last_data'} = ''; # remove read data if we are at
613             # end of an element
614 271 100 100     458 $self->{'_result'} = $rc if( defined $type && $type eq 'result' );
615 271         403 return $rc;
616             }
617              
618             =head2 element
619              
620             Title : element
621             Usage : $eventhandler->element({'Name' => $name, 'Data' => $str});
622             Function: Convience method that calls start_element, characters, end_element
623             Returns : none
624             Args : Hash ref with the keys 'Name' and 'Data'
625              
626              
627             =cut
628              
629             sub element{
630 237     237 1 159 my ($self,$data) = @_;
631 237         224 $self->start_element($data);
632 237         217 $self->characters($data);
633 237         245 $self->end_element($data);
634             }
635              
636             =head2 characters
637              
638             Title : characters
639             Usage : $eventgenerator->characters($str)
640             Function: Send a character events
641             Returns : none
642             Args : string
643              
644              
645             =cut
646              
647             sub characters{
648 237     237 1 151 my ($self,$data) = @_;
649              
650 237 100 66     738 return unless ( defined $data->{'Data'} && $data->{'Data'} !~ /^\s+$/ );
651              
652 215         209 $self->{'_last_data'} = $data->{'Data'};
653             }
654              
655             =head2 within_element
656              
657             Title : within_element
658             Usage : if( $eventgenerator->within_element($element) ) {}
659             Function: Test if we are within a particular element
660             This is different than 'in' because within can be tested
661             for a whole block.
662             Returns : boolean
663             Args : string element name
664              
665              
666             =cut
667              
668             sub within_element{
669 12     12 1 10 my ($self,$name) = @_;
670             return 0 if ( ! defined $name &&
671             ! defined $self->{'_elements'} ||
672 12 50 33     30 scalar @{$self->{'_elements'}} == 0) ;
  12   33     31  
673 12         9 foreach ( @{$self->{'_elements'}} ) {
  12         19  
674 17 100       24 if( $_ eq $name ) {
675 11         23 return 1;
676             }
677             }
678 1         4 return 0;
679             }
680              
681              
682             =head2 in_element
683              
684             Title : in_element
685             Usage : if( $eventgenerator->in_element($element) ) {}
686             Function: Test if we are in a particular element
687             This is different than 'in' because within can be tested
688             for a whole block.
689             Returns : boolean
690             Args : string element name
691              
692              
693             =cut
694              
695             sub in_element{
696 0     0 1 0 my ($self,$name) = @_;
697 0 0       0 return 0 if ! defined $self->{'_elements'}->[0];
698 0         0 return ( $self->{'_elements'}->[0] eq $name)
699             }
700              
701             =head2 start_document
702              
703             Title : start_document
704             Usage : $eventgenerator->start_document
705             Function: Handle a start document event
706             Returns : none
707             Args : none
708              
709              
710             =cut
711              
712             sub start_document{
713 10     10 1 11 my ($self) = @_;
714 10         14 $self->{'_lasttype'} = '';
715 10         13 $self->{'_values'} = {};
716 10         26 $self->{'_result'}= undef;
717 10         19 $self->{'_elements'} = [];
718 10         17 $self->{'_reporttype'} = 'exonerate';
719             }
720              
721              
722             =head2 end_document
723              
724             Title : end_document
725             Usage : $eventgenerator->end_document
726             Function: Handles an end document event
727             Returns : Bio::Search::Result::ResultI object
728             Args : none
729              
730              
731             =cut
732              
733             sub end_document{
734 6     6 1 7 my ($self,@args) = @_;
735 6         40 return $self->{'_result'};
736             }
737              
738              
739             sub write_result {
740 0     0 1 0 my ($self, $blast, @args) = @_;
741              
742 0 0       0 if( not defined($self->writer) ) {
743 0         0 $self->warn("Writer not defined. Using a $DEFAULT_WRITER_CLASS");
744 0         0 $self->writer( $DEFAULT_WRITER_CLASS->new() );
745             }
746 0         0 $self->SUPER::write_result( $blast, @args );
747             }
748              
749             sub result_count {
750 0     0 1 0 my $self = shift;
751 0         0 return $self->{'_result_count'};
752             }
753              
754 0     0 0 0 sub report_count { shift->result_count }
755              
756             =head2 vulgar
757              
758             Title : vulgar
759             Usage : $obj->vulgar($newval)
760             Function: Get/Set flag, do you want to build HSPs from VULGAR string?
761             Returns : value of vulgar (a scalar)
762             Args : on set, new value (a scalar or undef, optional)
763              
764              
765             =cut
766              
767             sub vulgar{
768 7     7 1 5 my $self = shift;
769 7 100       14 my $x = shift if @_;
770 7 50       30 if( @_ ) {
771 0 0 0     0 if( $_[0] && $self->{'_cigar'} ) {
772 0         0 $self->warn("Trying to set vulgar and cigar both to 1, must be either or");
773 0         0 $self->{'_cigar'} = 0;
774 0         0 return $self->{'_vulgar'} = 0;
775             }
776             }
777 7         12 return $self->{'_vulgar'};
778             }
779              
780             =head2 cigar
781              
782             Title : cigar
783             Usage : $obj->cigar($newval)
784             Function: Get/Set boolean flag do you want to build HSPs from CIGAR strings?
785             Returns : value of cigar (a scalar)
786             Args : on set, new value (a scalar or undef, optional)
787              
788              
789             =cut
790              
791             sub cigar{
792 9     9 1 8 my $self = shift;
793 9 100       16 my $x = shift if @_;
794 9 50       13 if( @_ ) {
795 0 0 0     0 if( $_[0] && $self->{'_vulgar'} ) {
796 0         0 $self->warn("Trying to set vulgar and cigar both to 1, must be either or");
797 0         0 $self->{'_vulgar'} = 0;
798 0         0 return $self->{'_cigar'} = 0;
799             }
800             }
801 9         20 return $self->{'_cigar'};
802             }
803              
804             1;
805