File Coverage

blib/lib/GenOO/Data/File/SAM/Record.pm
Criterion Covered Total %
statement 87 96 90.6
branch 14 22 63.6
condition n/a
subroutine 29 31 93.5
pod 0 22 0.0
total 130 171 76.0


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::SAM::Record - Object representing a record of a sam file
6              
7             =head1 SYNOPSIS
8              
9             # Object representing a record of a sam file
10              
11             # To initialize
12             my $sam_record = GenOO::Data::File::SAM::Record->new(
13             fields => [qname,flag, rname, pos, mapq, cigar,
14             rnext, pnext, tlen, seq, qual, tags]
15             );
16              
17              
18             =head1 DESCRIPTION
19              
20             This object represents a record of a sam file and offers methods for accessing the different attributes.
21             It implements several additional methods that transform original attributes in more manageable attributes.
22             eg. from the FLAG attribute the actual strand is extracted etc.
23              
24             =head1 EXAMPLES
25              
26             # Check if the record corresponds to a match
27             my $mapped = $sam_record->is_mapped;
28            
29             # Check if the record corresponds to a non match
30             my $unmapped = $sam_record->is_unmapped;
31            
32             # Parse the FLAG attribute and return 1 or -1 for the strand
33             my $strand = $sam_record->strand;
34              
35             =cut
36              
37             # Let the code begin...
38              
39             package GenOO::Data::File::SAM::Record;
40             $GenOO::Data::File::SAM::Record::VERSION = '1.5.2';
41              
42             #######################################################################
43             ####################### Load External modules #####################
44             #######################################################################
45 1     1   9 use Modern::Perl;
  1         2  
  1         45  
46 1     1   188 use autodie;
  1         2  
  1         8  
47 1     1   5607 use Moose;
  1         2  
  1         9  
48 1     1   7808 use namespace::autoclean;
  1         3  
  1         11  
49              
50              
51             #######################################################################
52             ####################### Interface attributes ######################
53             #######################################################################
54             has 'fields' => (
55             traits => ['Array'],
56             is => 'ro',
57             isa => 'ArrayRef[Str]',
58             default => sub { [] },
59             handles => {
60             all_fields => 'elements',
61             add_field => 'push',
62             map_fields => 'map',
63             filter_fields => 'grep',
64             find_field => 'first',
65             get_field => 'get',
66             join_fields => 'join',
67             count_fields => 'count',
68             has_fields => 'count',
69             has_no_fields => 'is_empty',
70             sorted_fields => 'sort',
71             },
72             required => 1,
73             );
74              
75             has 'tags' => (
76             is => 'ro',
77             builder => '_read_tags',
78             init_arg => undef,
79             lazy => 1,
80             );
81              
82             has 'alignment_length' => (
83             is => 'ro',
84             builder => '_calculate_alignment_length',
85             init_arg => undef,
86             lazy => 1,
87             );
88              
89             has 'start' => (
90             is => 'ro',
91             builder => '_calculate_start',
92             init_arg => undef,
93             lazy => 1,
94             );
95              
96             has 'stop' => (
97             is => 'ro',
98             builder => '_calculate_stop',
99             init_arg => undef,
100             lazy => 1,
101             );
102              
103             has 'strand' => (
104             is => 'ro',
105             builder => '_calculate_strand',
106             init_arg => undef,
107             lazy => 1,
108             );
109              
110             has 'copy_number' => (
111             is => 'ro',
112             default => 1,
113             lazy => 1
114             );
115              
116             has 'extra' => (
117             is => 'rw',
118             init_arg => undef,
119             );
120              
121              
122             #######################################################################
123             ########################## Consumed Roles #########################
124             #######################################################################
125             with
126             'GenOO::Region' => {
127             -alias => { mid_position => 'region_mid_position' },
128             -excludes => 'mid_position',
129             },
130             'GenOO::Data::File::SAM::CigarAndMDZ' => {
131             };
132              
133              
134              
135             #######################################################################
136             ######################## Interface Methods ########################
137             #######################################################################
138             sub qname { # String [!-?A-~]f1,255g Query template NAME
139 2     2 0 1646 my ($self) = @_;
140            
141 2         107 return $self->fields->[0];
142             }
143              
144             sub flag { # Int [0,216-1] bitwise FLAG
145 2042     2042 0 4247 my ($self) = @_;
146            
147 2042         54692 return $self->fields->[1];
148             }
149              
150             sub rname { # String \*|[!-()+-<>-~][!-~]* Reference sequence NAME
151 647     647 0 2398 my ($self) = @_;
152            
153 647         16538 return $self->fields->[2];
154             }
155              
156             sub pos { # Int [0,229-1] 1-based leftmost mapping POSition
157 48     48 0 1261 my ($self) = @_;
158            
159 48         1371 return $self->fields->[3];
160             }
161              
162             sub mapq { # Int [0,28-1] MAPping Quality
163 2     2 0 1240 my ($self) = @_;
164            
165 2         85 return $self->fields->[4];
166             }
167              
168             sub cigar { # String \*|([0-9]+[MIDNSHPX=])+ CIGAR string
169 196     196 0 1543 my ($self) = @_;
170            
171 196         5859 return $self->fields->[5];
172             }
173              
174             sub rnext { # String \*|=|[!-()+-<>-~][!-~]* Ref. name of the mate/next segment
175 2     2 0 1187 my ($self) = @_;
176            
177 2         80 return $self->fields->[6];
178             }
179              
180             sub pnext { # Int [0,229-1] Position of the mate/next segment
181 2     2 0 1179 my ($self) = @_;
182            
183 2         80 return $self->fields->[7];
184             }
185              
186             sub tlen { # Int [-229+1,229-1] observed Template LENgth
187 2     2 0 1151 my ($self) = @_;
188            
189 2         80 return $self->fields->[8];
190             }
191              
192             sub seq { # String \*|[A-Za-z=.]+ segment SEQuence
193 29     29 0 1238 my ($self) = @_;
194            
195 29         873 return $self->fields->[9];
196             }
197              
198             sub qual { # String [!-~]+ ASCII of Phred-scaled base QUALity+33
199 2     2 0 1220 my ($self) = @_;
200            
201 2         81 return $self->fields->[10];
202             }
203              
204             sub sequence {
205 0     0 0 0 my ($self) = @_;
206              
207 0         0 return $self->seq;
208             }
209              
210             sub query_seq {
211 8     8 0 1195 my ($self) = @_;
212            
213 8 50       232 if (defined $self->strand) {
    0          
214 8 100       205 if ($self->strand == 1) {
    50          
215 5         13 return $self->seq;
216             }
217             elsif ($self->strand == -1) {
218 3         14 my $seq = reverse($self->seq);
219 3         11 $seq =~ tr/ATGCUatgcu/TACGAtacga/;
220 3         15 return $seq;
221             }
222             }
223             elsif ($self->is_unmapped) {
224 0         0 return $self->seq;
225             }
226             else {
227 0         0 return undef;
228             }
229             }
230              
231             sub query_length {
232 19     19 0 1240 my ($self) = @_;
233            
234 19         54 return CORE::length($self->seq); # using seq to avoid costs of query_seq
235             }
236              
237             sub tag {
238 45     45 0 1326 my ($self, $tag_id) = @_;
239            
240 45 50       1448 if (defined $self->tags) {
241 45         1197 return $self->tags->{$tag_id};
242             }
243             }
244              
245             sub mdz {
246 32     32 0 59 my ($self) = @_;
247            
248 32         80 return $self->tag('MD:Z');
249             }
250              
251             sub to_string {
252 1     1 0 1172 my ($self) = @_;
253            
254 1         59 return $self->join_fields("\t");
255             }
256              
257             sub is_mapped {
258 1349     1349 0 3944 my ($self) = @_;
259            
260 1349 100       3084 if ($self->flag & 4) {
261 337         10152 return 0;
262             }
263             else {
264 1012         21969 return 1;
265             }
266             }
267              
268             sub is_unmapped {
269 3     3 0 1152 my ($self) = @_;
270            
271 3 100       8 if ($self->flag & 4) {
272 1         13 return 1;
273             }
274             else {
275 2         11 return 0;
276             }
277             }
278              
279             sub is_secondary {
280 0     0 0 0 my ($self) = @_;
281            
282 0 0       0 if ($self->flag & 256) {
283 0         0 return 1;
284             }
285             else {
286 0         0 return 0;
287             }
288             }
289              
290             sub add_tag {
291 1     1 0 1157 my ($self, $tag, $value) = @_;
292              
293 1         6 my $t = $self->tag($tag);
294 1 50       5 if (defined $t) {
295 0         0 die "tag $tag already exists in \"" . $self->to_string . "\"\n";
296             }
297 1         74 $self->add_field("$tag:$value");
298 1         32 $self->tags->{$tag} = $value;
299             }
300              
301             sub set_flag {
302 1     1 0 10 my ($self, $value) = @_;
303            
304 1         49 $self->fields->[1] = $value;
305             }
306              
307              
308             #######################################################################
309             ######################### Private methods ##########################
310             #######################################################################
311             sub _calculate_alignment_length {
312 6     6   14 my ($self) = @_;
313            
314 6         173 return $self->length;
315             }
316              
317             sub _calculate_start {
318 46     46   101 my ($self) = @_;
319            
320 46         124 return $self->pos - 1;
321             }
322              
323             sub _calculate_stop {
324 13     13   27 my ($self) = @_;
325            
326 13         365 return $self->start + $self->M_count + $self->D_count + $self->N_count + $self->EQ_count + $self->X_count + $self->P_count - 1;
327             }
328              
329             sub _calculate_strand {
330 687     687   1146 my ($self) = @_;
331            
332 687 100       1327 if ($self->flag & 16) {
    100          
333 319         8623 return -1;
334             }
335             elsif ($self->is_mapped) {
336 365         9618 return 1;
337             }
338             else {
339 3         80 return undef;
340             }
341             }
342              
343             sub _read_tags {
344 29     29   59 my ($self) = @_;
345            
346 29         51 my %tags;
347            
348 29         1102 my @tags_array = @{$self->fields}[11..$self->count_fields-1];
  29         786  
349 29         94 foreach my $tag_var (@tags_array) {
350 65         251 $tag_var =~ /([A-Za-z][A-Za-z0-9]):([AifZHB]):(.+)/;
351 65         132 my $tag = $1;
352 65         117 my $tag_type = $2;
353 65         124 my $tag_value = $3;
354 65         230 $tags{"$tag:$tag_type"} = $tag_value;
355             }
356            
357 29         837 return \%tags;
358             }
359              
360              
361             #######################################################################
362             ############################ Finalize #############################
363             #######################################################################
364             __PACKAGE__->meta->make_immutable;
365              
366             1;