File Coverage

blib/lib/GenOO/Data/File/BED.pm
Criterion Covered Total %
statement 100 107 93.4
branch 32 44 72.7
condition n/a
subroutine 29 29 100.0
pod 0 23 0.0
total 161 203 79.3


line stmt bran cond sub pod time code
1             # POD documentation - main docs before the code
2              
3             =head1 NAME
4              
5             GenOO::Data::File::BED - Object implementing methods for accessing bed formatted files (http://genome.ucsc.edu/FAQ/FAQformat#format1)
6              
7             =head1 SYNOPSIS
8              
9             # Object that manages a bed file.
10              
11             # To initialize
12             my $bed_file = GenOO::Data::File::BED->new({
13             FILE => undef,
14             EXTRA_INFO => undef,
15             });
16              
17             =head1 DESCRIPTION
18              
19             This object offers methods to read a bed file line by line.
20              
21             =head1 EXAMPLES
22              
23             # Create object
24             my $bed_file = GenOO::Data::File::BED->new({
25             FILE => 't/sample_data/sample.bed.gz'
26             });
27              
28             # Read one record at a time
29             my $record = $bed_file->next_record();
30              
31             =cut
32              
33             # Let the code begin...
34              
35             package GenOO::Data::File::BED;
36             $GenOO::Data::File::BED::VERSION = '1.5.2';
37              
38             #######################################################################
39             ####################### Load External modules #####################
40             #######################################################################
41 1     1   5819 use Modern::Perl;
  1         4  
  1         32  
42 1     1   369 use autodie;
  1         2  
  1         23  
43 1     1   6441 use Moose;
  1         6  
  1         28  
44 1     1   11700 use namespace::autoclean;
  1         2  
  1         28  
45              
46              
47             #######################################################################
48             ######################### Load GenOO modules ######################
49             #######################################################################
50 1     1   873 use GenOO::Data::File::BED::Record;
  1         3  
  1         1917  
51              
52              
53             #######################################################################
54             ####################### Interface attributes ######################
55             #######################################################################
56             has 'file' => (
57             isa => 'Maybe[Str]',
58             is => 'rw',
59             required => 1
60             );
61              
62             has 'redirect_score_to_copy_number' => (
63             traits => ['Bool'],
64             is => 'rw',
65             isa => 'Bool',
66             default => 0,
67             lazy => 1
68             );
69              
70              
71             #######################################################################
72             ######################## Private attributes #######################
73             #######################################################################
74             has '_filehandle' => (
75             is => 'ro',
76             builder => '_open_filehandle',
77             init_arg => undef,
78             lazy => 1,
79             );
80              
81              
82             #######################################################################
83             ############################## BUILD ##############################
84             #######################################################################
85             sub BUILD {
86 20     20 0 40 my $self = shift;
87              
88 20         81 $self->init_header;
89 20         164 $self->init_records_cache;
90 20         145 $self->init_records_read_count;
91              
92 20         141 $self->parse_header_section;
93             }
94              
95              
96             #######################################################################
97             ######################## Interface Methods ########################
98             #######################################################################
99             sub records_read_count {
100 5     5 0 3797 my ($self) = @_;
101 5         68 return $self->{RECORDS_READ_COUNT};
102             }
103              
104             sub next_record {
105 54     54 0 3930 my ($self) = @_;
106              
107 54         137 my $record;
108 54 100       147 if ($self->record_cache_not_empty) {
109 9         43 $record = $self->next_record_from_cache;
110             }
111             else {
112 45         104 $record = $self->next_record_from_file;
113             }
114              
115 54 100       143 if (defined $record) {
116 49         198 $self->increment_records_read_count;
117             }
118 54         445 return $record;
119             }
120              
121              
122             #######################################################################
123             ####################### Private Methods ############################
124             #######################################################################
125             sub set_eof_reached {
126 5     5 0 22 my ($self) = @_;
127 5         47 $self->{EOF} = 1;
128             }
129              
130             sub header {
131 1     1 0 3879 my ($self) = @_;
132 1         30 return $self->{HEADER};
133             }
134              
135             sub records_cache {
136 61     61 0 3927 my ($self) = @_;
137 61         398 return $self->{RECORDS_CACHE};
138             }
139              
140             sub init_header {
141 20     20 0 44 my ($self) = @_;
142 20         70 $self->{HEADER} = {};
143             }
144              
145             sub init_records_cache {
146 20     20 0 55 my ($self) = @_;
147 20         84 $self->{RECORDS_CACHE} = [];
148             }
149              
150             sub init_records_read_count {
151 20     20 0 60 my ($self) = @_;
152 20         61 $self->{RECORDS_READ_COUNT} = 0;
153             }
154              
155             sub increment_records_read_count {
156 50     50 0 3871 my ($self) = @_;
157 50         117 $self->{RECORDS_READ_COUNT}++;
158             }
159              
160             sub parse_header_section {
161 20     20 0 69 my ($self) = @_;
162              
163 20         696 my $filehandle = $self->_filehandle;
164 20         892 while (my $line = $filehandle->getline) {
165 80 100       4262 if ($self->line_looks_like_header($line)) {
    50          
166 60         443 $self->recognize_and_store_header_line($line);
167             }
168             elsif ($self->line_looks_like_record($line)) {
169             # the while loop will read one line after header. Usually, this is the first record and unfortunately in zipped files we cannot go back
170 20         173 my $record = $self->parse_record_line($line);
171 20         197 $self->add_to_records_cache($record);
172 20         717 return;
173             }
174             else {
175 0         0 return;
176             }
177             }
178             }
179              
180             # TODO fix to store "browser" and "track" lines
181             sub recognize_and_store_header_line {
182 60     60 0 1293 my ($self, $line) = @_;
183             # if ($self->line_looks_like_version($line)) {
184             # $self->parse_and_store_version_line($line);
185             # }
186             # else {
187             # $self->parse_and_store_header_line($line);
188             # }
189             }
190              
191             sub add_to_records_cache {
192 20     20 0 56 my ($self, $record) = @_;
193 20         42 push @{$self->{RECORDS_CACHE}}, $record,
  20         185  
194             }
195              
196             sub next_record_from_file {
197 46     46 0 3792 my ($self) = @_;
198              
199 46         1357 while (my $line = $self->_filehandle->getline) {
200 41 50       1074 if ($self->line_looks_like_record($line)) {
201 41         100 return $self->parse_record_line($line);
202             }
203             else {
204 0 0       0 if ($self->line_looks_like_header($line)) {
205 0         0 die "Record was expected but line looks like a header - the header should have been parsed already. $line\n";
206             }
207             else {
208 0         0 warn "Record was expected but line looks different. $line\n";
209             }
210             }
211             }
212              
213 5         247 $self->set_eof_reached;
214 5         13 return undef;
215             }
216              
217             sub next_record_from_cache {
218 10     10 0 3940 my ($self) = @_;
219              
220 10         30 my $record = shift @{$self->{RECORDS_CACHE}};
  10         48  
221 10 50       35 if (defined $record) {
222 10         61 return $record;
223             }
224             else {
225 0         0 return undef;
226             }
227             }
228              
229             sub parse_record_line {
230 62     62 0 4164 my ($self, $line) = @_;
231              
232 62         216 chomp $line;
233 62         797 my ($chr,$start,$stop_1,$name,$score,$strand,$thick_start,$thick_stop,$rgb,$block_count,$block_sizes,$block_starts) = split(/\t/,$line);
234              
235 62         934 my $data = {
236             rname => $chr,
237             start => $start,
238             stop_1based => $stop_1,
239             name => $name,
240             score => $score,
241             strand_symbol => $strand,
242             };
243              
244 62 100       2285 ($data->{copy_number} = $score) if $self->redirect_score_to_copy_number;
245 62 50       440 ($data->{thick_start} = $thick_start) if defined $thick_start;
246 62 50       383 ($data->{thick_stop_1based} = $thick_stop) if defined $thick_stop;
247 62 50       450 ($data->{rgb} = $rgb) if defined $rgb;
248 62 100       234 ($data->{block_count} = $block_count) if defined $block_count;
249 62 100       196 ($data->{block_sizes} = [split(/,/,$block_sizes)]) if defined $block_sizes;
250 62 100       240 ($data->{block_starts} = [split(/,/,$block_starts)]) if defined $block_starts;
251              
252 62         2479 return GenOO::Data::File::BED::Record->new($data);
253             }
254              
255             sub line_looks_like_comment {
256 1     1 0 3744 my ($self, $line) = @_;
257 1 50       35 return ($line =~ /^#/) ? 1 : 0;
258             }
259              
260             sub line_looks_like_header {
261 83     83 0 4037 my ($self, $line) = @_;
262 83 100       1151 return ($line =~ /^(track|browser)/) ? 1 : 0;
263             }
264              
265             sub line_looks_like_record {
266 65     65 0 3966 my ($self, $line) = @_;
267 65 100       635 return ($line !~ /^(#|track|browser)/) ? 1 : 0;
268             }
269              
270             sub record_cache_not_empty {
271 56     56 0 3985 my ($self) = @_;
272 56 100       157 return ($self->record_cache_size > 0) ? 1 : 0;
273             }
274              
275             sub record_cache_is_empty {
276 2     2 0 3986 my ($self) = @_;
277 2 100       28 return ($self->record_cache_size == 0) ? 1 : 0;
278             }
279              
280             sub record_cache_size {
281 60     60 0 3962 my ($self) = @_;
282 60         86 return scalar @{$self->records_cache};
  60         162  
283             }
284              
285             sub is_eof_reached {
286 2     2 0 3773 my ($self) = @_;
287 2         32 return $self->{EOF};
288             }
289              
290              
291             #######################################################################
292             ######################### Private Methods #########################
293             #######################################################################
294             sub _open_filehandle {
295 20     20   50 my ($self) = @_;
296              
297 20         50 my $read_mode;
298             my $HANDLE;
299 20 50       575 if (!defined $self->file) {
    50          
300 0         0 open ($HANDLE, '<-', $self->file);
301             }
302             elsif ($self->file =~ /\.gz$/) {
303 20 50       552 die 'Cannot open file ' . $self->file . "\n" if ! -e $self->file;
304 20         628 open($HANDLE, 'gzip -dc ' . $self->file . ' |');
305             }
306             else {
307 0         0 open ($HANDLE, '<', $self->file);
308             }
309              
310 20         93267 return $HANDLE;
311             }
312              
313              
314             #######################################################################
315             ############################ Finalize #############################
316             #######################################################################
317             __PACKAGE__->meta->make_immutable;
318              
319             1;