File Coverage

blib/lib/BLASTaid.pm
Criterion Covered Total %
statement 81 135 60.0
branch 25 58 43.1
condition 2 9 22.2
subroutine 12 14 85.7
pod 5 5 100.0
total 125 221 56.5


line stmt bran cond sub pod time code
1             package BLASTaid;
2              
3             # | PACKAGE | BLASTaid
4             # | AUTHOR | Todd Wylie
5             # | EMAIL | perldev@monkeybytes.org
6             # | ID | $Id: BLASTaid.pm 20 2006-03-15 21:28:53Z Todd Wylie $
7              
8 2     2   59432 use version; $VERSION = qv('0.0.3');
  2         4892  
  2         14  
9 2     2   166 use warnings;
  2         4  
  2         77  
10 2     2   13 use strict;
  2         8  
  2         55  
11 2     2   13 use Carp;
  2         4  
  2         214  
12 2     2   2115 use IO::File;
  2         25134  
  2         285  
13 2     2   19 use IO::Seekable;
  2         5  
  2         4007  
14              
15              
16             # --------------------------------------------------------------------------
17             # N E W (class CONSTRUCTOR)
18             # ==========================================================================
19             # USAGE : BLASTaid->new();
20             # PURPOSE : Constructor for class.
21             # RETURNS : Object handle.
22             # PARAMETERS : report => ''
23             # : index => ''
24             # THROWS : croaks if arguments are missing or report file is suspect
25             # COMMENTS : Feed the interface a WU-BLAST report path and also the
26             # : path/name where you want the index file saved. If the index
27             # : file specified already exists, use it.
28             # --------------------------------------------------------------------------
29             sub new {
30 2     2 1 21 my ($class, %arg) = @_;
31              
32             # Do some simple file validation checks.
33 2 50       8 if (!$arg{report} ) { croak "new requires a REPORT value" }
  0         0  
34 2 50       40 if (!-f $arg{report}) { croak "report [$arg{report}] does not exist" }
  0         0  
35 2 50       125 if (!-T $arg{report}) { croak "report [$arg{report}] is not a text report" }
  0         0  
36            
37             # Deal with the index file initialization.
38 2 50       8 if (!$arg{index}) { croak "new requires a INDEX value" }
  0         0  
39            
40             # Class setup.
41 2         12 my $self = {
42             _report => $arg{report},
43             _index => $arg{index},
44             _ignored => [],
45             };
46 2         5 bless($self, $class);
47            
48             # If the supplied index already exists then use it. Else, build
49             # the index file. Populate the object with the index regardless.
50 2 50       46 if (-f $self->{_index}) {
51 2         8 $self->_load_index_file();
52             }
53             else {
54 0         0 $self->_build_index();
55             }
56            
57 2         15 return($self);
58             }
59              
60              
61             # --------------------------------------------------------------------------
62             # E A C H R E P O R T (accessor method)
63             # ==========================================================================
64             # USAGE : BLASTaid->each_report( ignore => 'yes' );
65             # PURPOSE : Accessor method, iterator for object.
66             # RETURNS : Query names.
67             # PARAMETERS : ignore => ''
68             # THROWS : croaks if no query names are in object
69             # COMMENTS : If ignore = yes, reports with no alignments are skipped.
70             # --------------------------------------------------------------------------
71             sub each_report {
72 1     1 1 4 my ($class, %arg) = @_;
73            
74             # Must have a valid ignore clause:
75 1 50       4 if (!defined $arg{ignore}) { croak "each_report requires a IGNORE value" }
  0         0  
76              
77             # Iterate through the record index returning associated query
78             # name:
79 1         2 my @queries;
80 1         2 foreach my $id ( sort {$a <=> $b} keys %{$class->{_id}} ) {
  172         149  
  1         18  
81 41 100       83 unless ( $class->{_id}->{$id}->{_name} eq "END-OF-FILE" ) {
82 40 50       58 unless ( $arg{ignore} eq "yes" ) {
83 0         0 push( @queries, $class->{_id}->{$id}->{_name} );
84             }
85             else {
86 40 50       71 if ($class->{_id}->{$id}->{_alignments} eq "TRUE") {
87 40         70 push( @queries, $class->{_id}->{$id}->{_name} );
88             }
89             else {
90 0         0 push( @{$class->{_ignored}}, $class->{_id}->{$id}->{_name} );
  0         0  
91             }
92             }
93             }
94             }
95 1 50       6 if (@queries < 1) { croak "no queries found in object" }
  0         0  
96            
97 1         7 return(@queries);
98             }
99              
100              
101             # --------------------------------------------------------------------------
102             # R E T U R N R E P O R T (accessor method)
103             # ==========================================================================
104             # USAGE : BLASTaid->return_report( query => 'contig1.0' );
105             # PURPOSE : Accessor method for returning a report,
106             # RETURNS : String of BLAST report content.
107             # PARAMETERS : query => ''
108             # THROWS : croaks if no query argument is indicated
109             # : croaks if report string is null
110             # : croaks if a record is partial (no EXIT CODE)
111             # --------------------------------------------------------------------------
112             sub return_report {
113 1     1 1 4 my ($class, %arg) = @_;
114            
115             # Must have a query name to continue.
116 1 50 33     9 if (!defined $arg{query} || $arg{query} eq "" ) { croak "return_report must have a QUERY argument" }
  0         0  
117 1 50       6 if (!defined $class->{_queries}->{_name}->{$arg{query}}) { croak "return_report cannot locate $arg{query} in object" }
  0         0  
118              
119             # Seek ahead to the entry and grab the report's text.
120 1         2 my $pass = "false";
121 1         7 my $REPORT = new IO::File;
122 1 50       29 $REPORT->open( "$class->{_report}" ) or croak "could not open file $class->{_report}";
123 1         115 $REPORT->seek( $class->{_queries}->{_name}->{$arg{query}}->{_start}, 0 );
124 1         11 my $string;
125 1         34 my $seeking = <$REPORT>;
126 1         2 $string = $seeking;
127             SEEKENTRY:
128 1         5 while (<$REPORT>) {
129 1024 100       1580 if ($_ =~ /EXIT CODE (\d+)/) { $pass = "true" };
  1         3  
130 1024 100       1463 last SEEKENTRY if ($_ =~ /^BLAST/);
131 1023         2020 $string .= $_;
132             }
133 1         13 $REPORT->close;
134            
135             # Error checking.
136 1 50       22 if ($string eq "" ) { croak "return_report has null return for $arg{query}" };
  0         0  
137 1 50       5 if ($pass ne "true") { croak "$arg{query} is a partial report" };
  0         0  
138            
139 1         77 return($string);
140             }
141              
142              
143             # --------------------------------------------------------------------------
144             # B U I L D I N D E X (internal method)
145             # ==========================================================================
146             # USAGE : BLASTaid->_build_index();
147             # PURPOSE : Builds & saves the index file for byte positions.
148             # RETURNS : none
149             # PARAMETERS : none
150             # THROWS : croaks if BLAST report cannot be opened
151             # : croaks if object entries are not complete
152             # --------------------------------------------------------------------------
153             sub _build_index {
154 0     0   0 my $class = shift;
155            
156             # Open the specified BLAST report and index it.
157 0         0 my $id;
158 0         0 my $REPORT = new IO::File;
159 0 0       0 $REPORT->open( "$class->{_report}" ) or croak "could not open file $class->{_report}";
160 0         0 while (<$REPORT>) {
161 0 0       0 if (/^(BLAST\S+)\s+/) {
    0          
    0          
    0          
162 0         0 $id++;
163 0         0 $class->{_id}->{$id}->{_start} = $REPORT->tell - length($_);
164 0         0 $class->{_id}->{$id}->{_alignments} = "TRUE";
165 0         0 $class->{_id}->{$id}->{_type} = $1;
166             }
167             elsif (/^Query\=\s+(\S+)/) {
168 0         0 $class->{_id}->{$id}->{_name} = $1;
169             }
170             elsif (/\s*.+NONE.+\s*/) {
171             # No alignments:
172 0         0 $class->{_id}->{$id}->{_alignments} = "FALSE";
173             }
174             elsif ($REPORT->eof) {
175 0         0 $id++;
176 0         0 $class->{_id}->{$id}->{_start} = $REPORT->tell - length($_);
177 0         0 $class->{_id}->{$id}->{_name} = "END-OF-FILE";
178 0         0 $class->{_id}->{$id}->{_alignments} = "FALSE";
179 0         0 $class->{_id}->{$id}->{_type} = "N/A";
180             }
181             }
182 0         0 $REPORT->close;
183            
184             # Make sure that all entries have needed values. Revise the object
185             # to support the query names as unique keys.
186 0         0 foreach my $report (sort {$a <=> $b} keys %{$class->{_id}}) {
  0         0  
  0         0  
187 0 0 0     0 if (
188             !defined $class->{_id}->{$report}->{_name} ||
189             !defined $class->{_id}->{$report}->{_start}
190             ) {
191 0         0 croak "missing QUERY NAME or START for entry $report";
192             }
193             else {
194 0         0 $class->{_queries}->{_name}->{ $class->{_id}->{$report}->{_name} } = {
195             _start => $class->{_id}->{$report}->{_start},
196             _alignments => $class->{_id}->{$report}->{_alignments},
197             _type => $class->{_id}->{$report}->{_type},
198             };
199             }
200             }
201            
202             # Save the object to an index file.
203 0         0 $class->_save_index_file();
204            
205 0         0 return($class);
206             }
207              
208              
209             # --------------------------------------------------------------------------
210             # S A V E I N D E X F I L E (internal method)
211             # ==========================================================================
212             # USAGE : BLASTaid->_save_index_file();
213             # PURPOSE : Saves the object to an index byte file.
214             # RETURNS : none
215             # PARAMETERS : none
216             # THROWS : croaks if index file cannot be saved
217             # --------------------------------------------------------------------------
218             sub _save_index_file {
219 0     0   0 my $class = shift;
220            
221             # Save the object to a file.
222 0         0 my $OUT = new IO::File;
223 0 0       0 $OUT->open( ">$class->{_index}" ) or croak "could not write file $class->{_index}";
224 0         0 foreach my $report (sort {$a <=> $b} keys %{$class->{_id}}) {
  0         0  
  0         0  
225 0         0 my $line = sprintf "%-15s %-15s %-15s %-15s $class->{_id}->{$report}->{_name}", $report, $class->{_id}->{$report}->{_start}, $class->{_id}->{$report}->{_alignments}, $class->{_id}->{$report}->{_type};
226 0         0 $OUT->print("$line\n");
227             }
228 0         0 $OUT->close;
229            
230 0         0 return($class);
231             }
232              
233              
234             # --------------------------------------------------------------------------
235             # L O A D I N D E X F I L E (internal method)
236             # ==========================================================================
237             # USAGE : BLASTaid->_load_index_file();
238             # PURPOSE : Populates an object from a saved index file.
239             # RETURNS : none
240             # PARAMETERS : none
241             # THROWS : croaks if index file cannot be opened
242             # : croaks if object entries are not complete
243             # : croaks if the first entry is not byte position 0
244             # --------------------------------------------------------------------------
245             sub _load_index_file {
246 2     2   2 my $class = shift;
247            
248             # Load object from a file.
249 2         14 my $IN = new IO::File;
250 2 50       72 $IN->open( "$class->{_index}" ) or croak "could not open file $class->{_index}";
251 2         120 while(<$IN>) {
252 82         98 chomp;
253 82         198 my ($report, $start, $alignments, $type, $name) = split(/\s+/, $_);
254 82         444 $class->{_id}->{$report} = {
255             _start => $start,
256             _name => $name,
257             _type => $type,
258             _alignments => $alignments,
259             };
260             }
261            
262             # Make sure that all entries have needed values. Revise the object
263             # to support the query names as unique keys.
264 2 50       9 if ($class->{_id}->{1}->{_start} != 0) { croak "check incoming INDEX format" }
  0         0  
265 2         3 foreach my $report (sort {$a <=> $b} keys %{$class->{_id}}) {
  338         257  
  2         28  
266 82 50 33     393 if (
267             !defined $class->{_id}->{$report}->{_name} ||
268             !defined $class->{_id}->{$report}->{_start} ||
269             !defined $class->{_id}->{$report}->{_type}
270             ) {
271 0         0 croak "missing QUERY NAME or START or TYPE for entry $report";
272             }
273             else {
274 82         435 $class->{_queries}->{_name}->{ $class->{_id}->{$report}->{_name} } = {
275             _start => $class->{_id}->{$report}->{_start},
276             _alignments => $class->{_id}->{$report}->{_alignments},
277             _type => $class->{_id}->{$report}->{_type},
278             };
279             }
280             }
281            
282 2         28 return($class);
283             }
284              
285              
286             # --------------------------------------------------------------------------
287             # T Y P E (accessor method)
288             # ==========================================================================
289             # USAGE : BLASTaid->type( report => '' )
290             # PURPOSE : Returns the BLAST report type.
291             # RETURNS : Scalar: BLAST type name.
292             # PARAMETERS : report => ''
293             # THROWS : croaks if report attribute is missing
294             # : croaks if type is null in the index onject
295             # --------------------------------------------------------------------------
296             sub type {
297 1     1 1 4 my ( $class, %arg ) = @_;
298            
299             # Do some simple file validation checks.
300 1 50       4 if ( !$arg{report} ) { croak "new requires a REPORT value" }
  0         0  
301              
302             # Validation & return:
303 1 50       6 if (defined $class->{_queries}->{_name}->{$arg{report}}->{_type}) {
304 1         5 return( $class->{_queries}->{_name}->{$arg{report}}->{_type} );
305             }
306             else {
307 0         0 croak "type is null for report $arg{report}";
308             }
309            
310             }
311              
312              
313             # --------------------------------------------------------------------------
314             # U N D E F (accessor method)
315             # ==========================================================================
316             # USAGE : BLASTaid->undef();
317             # PURPOSE : Deletes the object.
318             # RETURNS : Scalar: BLAST type name.
319             # PARAMETERS : none
320             # THROWS : none
321             # --------------------------------------------------------------------------
322             sub undef {
323 1     1 1 853 my $class = shift;
324            
325             # Delete content from the object.
326 1         2 undef(%{$class});
  1         56  
327            
328 1         6 return($class);
329             }
330              
331             1; # End of module.
332              
333             __END__