File Coverage

blib/lib/CracTools/SAMReader/SAMline.pm
Criterion Covered Total %
statement 229 294 77.8
branch 68 126 53.9
condition 12 33 36.3
subroutine 38 44 86.3
pod 39 39 100.0
total 386 536 72.0


line stmt bran cond sub pod time code
1             ###############################################################################
2             # #
3             # Copyright © 2012-2013 -- IRB/INSERM #
4             # (Institut de Recherche en Biothérapie / #
5             # Institut National de la Santé et de la #
6             # Recherche Médicale) #
7             # #
8             # Auteurs/Authors: Jerôme AUDOUX #
9             # Nicolas PHILIPPE #
10             # #
11             # ------------------------------------------------------------------------- #
12             # #
13             # Ce fichier fait partie de la suite CracTools qui contient plusieurs pipeline#
14             # intégrés permettant de traiter les évênements biologiques présents dans du #
15             # RNA-Seq. Les CracTools travaillent à partir d'un fichier SAM de CRAC et d'un#
16             # fichier d'annotation au format GFF3. #
17             # #
18             # Ce logiciel est régi par la licence CeCILL soumise au droit français et #
19             # respectant les principes de diffusion des logiciels libres. Vous pouvez #
20             # utiliser, modifier et/ou redistribuer ce programme sous les conditions de #
21             # la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA sur #
22             # le site "http://www.cecill.info". #
23             # #
24             # En contrepartie de l'accessibilité au code source et des droits de copie, #
25             # de modification et de redistribution accordés par cette licence, il n'est #
26             # offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons, #
27             # seule une responsabilité restreinte pèse sur l'auteur du programme, le #
28             # titulaire des droits patrimoniaux et les concédants successifs. #
29             # #
30             # À cet égard l'attention de l'utilisateur est attirée sur les risques #
31             # associés au chargement, à  l'utilisation, à  la modification et/ou au #
32             # développement et à la reproduction du logiciel par l'utilisateur étant #
33             # donné sa spécificité de logiciel libre, qui peut le rendre complexe à #
34             # manipuler et qui le réserve donc à des développeurs et des professionnels #
35             # avertis possédant des connaissances informatiques approfondies. Les #
36             # utilisateurs sont donc invités à  charger et tester l'adéquation du #
37             # logiciel à leurs besoins dans des conditions permettant d'assurer la #
38             # sécurité de leurs systêmes et ou de leurs données et, plus généralement, #
39             # à l'utiliser et l'exploiter dans les mêmes conditions de sécurité. #
40             # #
41             # Le fait que vous puissiez accéder à cet en-tête signifie que vous avez #
42             # pris connaissance de la licence CeCILL, et que vous en avez accepté les #
43             # termes. #
44             # #
45             # ------------------------------------------------------------------------- #
46             # #
47             # This file is part of the CracTools which provide several integrated #
48             # pipeline to analyze biological events present in RNA-Seq data. CracTools #
49             # work on a SAM file generated by CRAC and an annotation file in GFF3 format.#
50             # #
51             # This software is governed by the CeCILL license under French law and #
52             # abiding by the rules of distribution of free software. You can use, #
53             # modify and/ or redistribute the software under the terms of the CeCILL #
54             # license as circulated by CEA, CNRS and INRIA at the following URL #
55             # "http://www.cecill.info". #
56             # #
57             # As a counterpart to the access to the source code and rights to copy, #
58             # modify and redistribute granted by the license, users are provided only #
59             # with a limited warranty and the software's author, the holder of the #
60             # economic rights, and the successive licensors have only limited #
61             # liability. #
62             # #
63             # In this respect, the user's attention is drawn to the risks associated #
64             # with loading, using, modifying and/or developing or reproducing the #
65             # software by the user in light of its specific status of free software, #
66             # that may mean that it is complicated to manipulate, and that also #
67             # therefore means that it is reserved for developers and experienced #
68             # professionals having in-depth computer knowledge. Users are therefore #
69             # encouraged to load and test the software's suitability as regards their #
70             # requirements in conditions enabling the security of their systems and/or #
71             # data to be ensured and, more generally, to use and operate it in the same #
72             # conditions as regards security. #
73             # #
74             # The fact that you are presently reading this means that you have had #
75             # knowledge of the CeCILL license and that you accept its terms. #
76             # #
77             ###############################################################################
78              
79             =encoding utf8
80              
81             =head1 NAME
82              
83             CracTools::SAMReader::SAMline - The object for manipulation a SAM line.
84              
85             =head1 SYNOPSIS
86              
87             use CracTools::SAMReader::SAMline;
88              
89             $sam_line = CracTools::SAMReader::SAMline->new($line);
90              
91             =head1 DESCRIPTION
92              
93             An object for easy acces to SAM line fields. See SAM Specifications for more informations :
94             http://samtools.sourceforge.net/SAM1.pdf
95              
96             =cut
97              
98             package CracTools::SAMReader::SAMline;
99              
100              
101 2     2   27374 use strict;
  2         4  
  2         84  
102 2     2   12 use warnings;
  2         4  
  2         55  
103 2     2   10 use Carp;
  2         4  
  2         222  
104              
105 2     2   1615 use CracTools::Utils;
  2         15  
  2         10796  
106              
107             =head1 Variables
108              
109             =head2 %flags
110              
111             SAM flags :
112              
113             =over 2
114              
115             =item * MULTIPLE_SEGMENTS => 1
116              
117             =item * PROPERLY_ALIGNED => 2
118              
119             =item * UNMAPPED => 4,
120              
121             =item * NEXT_UNMAPPED => 8,
122              
123             =item * REVERSE_COMPLEMENTED => 16,
124              
125             =item * NEXT_REVERSE_COMPLEMENTED => 32,
126              
127             =item * FIRST_SEGMENT => 64,
128              
129             =item * LAST_SEGMENT => 128,
130              
131             =item * SECONDARY_ALIGNMENT => 256,
132              
133             =item * QUALITY_CONTROLS_FAILED => 512,
134              
135             =item * PCR_DUPLICATED => 1024,
136              
137             =item * CHIMERIC_ALIGNMENT => 2048,
138              
139             =back
140              
141             =cut
142              
143             our %flags = ( MULTIPLE_SEGMENTS => 1,
144             PROPERLY_ALIGNED => 2,
145             UNMAPPED => 4,
146             NEXT_UNMAPPED => 8,
147             REVERSE_COMPLEMENTED => 16,
148             NEXT_REVERSE_COMPLEMENTED => 32,
149             FIRST_SEGMENT => 64,
150             LAST_SEGMENT => 128,
151             SECONDARY_ALIGNMENT => 256,
152             QUALITY_CONTROLS_FAILED => 512,
153             PCR_DUPLICATED => 1024,
154             CHIMERIC_ALIGNMENT => 2048,
155             );
156              
157             =head1 STATIC PARSING METHODS
158              
159             These methods can be used without creating an CracTools::SAMReader::SAMline object.
160             They are designed to provided efficient performance when parsing huge SAM files,
161             because creating object in Perl can be long and useless for some purposes.
162              
163             =cut
164              
165             =head2 hasEvent
166              
167             Arg [1] : String - SAM line
168             Arg [2] : eventType
169              
170             =cut
171              
172             sub hasEvent {
173 2     2 1 12 my ($line,$event_type) = @_;
174 2 50 33     15 croak("Missing argument(s)") unless defined $line && defined $event_type;
175 2         73 return $line =~ /XE:Z:\d+:\d+:$event_type/i;
176             }
177              
178             =head1 Methods
179              
180             =head2 new
181              
182             Arg [1] : String - SAM line in TAB-separated format.
183              
184             Example : $sam_line = CracTools::SAMline->new$($line);
185             Description : Create a new CracTools::SAMline obect.
186             ReturnType : CracTools::SAMline
187             Exceptions : none
188              
189             =cut
190              
191             sub new {
192 7     7 1 12 my $class = shift;
193 7         11 my ($line) = @_;
194 7         18 chomp $line;
195 7         50 my ($qname,$flag,$rname,$pos,$mapq,$cigar,$rnext,$pnext,$tlen,$seq,$qual,@others) = split("\t",$line);
196 7         15 my %extended_fields;
197 7         16 foreach(@others) {
198 15         23 my $key = substr($_,0,2);
199 15         23 my $value = substr($_,5);
200             #my($key,$type,$value) = split(':',$_,3);
201             # Hack in case there is multiple field with the same tag
202 15 100       49 $extended_fields{$key} = defined $extended_fields{$key}? $extended_fields{$key}.";".$value : $value;
203             }
204              
205 7         122 my $self = bless{
206             qname => $qname,
207             flag => $flag,
208             rname => $rname,
209             pos => $pos,
210             mapq => $mapq,
211             cigar => $cigar,
212             rnext => $rnext,
213             pnext => $pnext,
214             tlen => $tlen,
215             seq => $seq,
216             qual => $qual,
217             extended_fields => \%extended_fields,
218             line => $line,
219             }, $class;
220              
221 7         33 return $self;
222             }
223              
224             =head2 isFlagged
225              
226             Arg [1] : Integer - The flag to test (1,2,4,8, ... ,1024)
227              
228             Example : if($SAMline->isFlagged($fags{unmapped}) {
229             DO_SOMETHING...
230             };
231             Description : Test if the line has the flag in parameter setted.
232             ReturnType : Boolean
233             Exceptions : none
234              
235             =cut
236              
237             sub isFlagged {
238 3     3 1 8 my $self = shift;
239 3         3 my $flag = shift;
240 3         9 return $self->flag & $flag;
241             }
242              
243             =head2 getStrand
244              
245             Example : $strand = $SAMline->getStrand();
246             Description : Return the strand of the SAMline :
247             - "1" if forward strand
248             - "-1" if reverse strand
249             ReturnType : 1 or -1
250             Exceptions : none
251              
252             =cut
253              
254             sub getStrand {
255 0     0 1 0 my $self = shift;
256 0 0       0 if($self->isFlagged($flags{REVERSE_COMPLEMENTED})) {
257 0         0 return -1;
258             } else {
259 0         0 return 1;
260             }
261             }
262              
263             =head2 getOriginalSeq
264              
265             Descrition : Return the original sequence as it was in the FASTQ file.
266             In fact we reverse complemente the sequence if flag 16 is raised.
267              
268             =cut
269              
270             sub getOriginalSeq {
271 0     0 1 0 my $self = shift;
272 0 0       0 if($self->isFlagged($flags{REVERSE_COMPLEMENTED})) {
273 0         0 return CracTools::Utils::reverseComplement($self->seq);
274             } else {
275 0         0 return $self->seq;
276             }
277             }
278              
279             =head2 getLocAsCracFormat
280              
281             Example : $loc = $SAMline->getLocAsCracFormat();
282             Description : Return the location of the sequence using CRAC format : "chr|strand,position".
283             For example : X|-1,2154520
284             ReturnType : String
285             Exceptions : none
286              
287             =cut
288              
289             sub getLocAsCracFormat {
290 0     0 1 0 my $self = shift;
291 0         0 return $self->rname."|".$self->getStrand.",".$self->pos;
292             }
293              
294             =head2 getPatch
295              
296             Description : If the SAMline has been modified, this method will generate
297             a patch in UnifiedDiff format that represent the changes.
298             ReturnType : String (patch) if line has changed, False (0) either.
299             Exceptions : none
300              
301             =cut
302              
303             sub getPatch {
304 0     0 1 0 my $self = shift;
305 0         0 my $line_number = shift;
306 0 0       0 croak("Cannot generate patch without the line number in argument") unless defined $line_number;
307 0 0       0 if($self->updatedLine ne $self->line) {
308 0         0 my $line1 = $self->line;
309 0         0 my $line2 = $self->updatedLine;
310 0         0 chomp($line1);
311 0         0 chomp($line2);
312 0         0 return "@@ -$line_number,1 +$line_number,1 @@\n-$line1\n+$line2";
313             } else {
314 0         0 return 0;
315             }
316             }
317              
318             =head1 GETTERS AND SETTERS
319              
320             =cut
321              
322             =head2 line
323              
324             Description : Getterr for the whole SAMline as a string.
325             ReturnType : String
326             Exceptions : none
327              
328             =cut
329              
330             sub line {
331 2     2 1 4 my $self = shift;
332 2         11 return $self->{line};
333             }
334              
335             =head2 updatedLine
336              
337             Description : Getter/Setter for the updated line.
338             If there is not updated line, this method return
339             the original SAM line.
340             RetrunType : String
341              
342             =cut
343              
344             sub updatedLine {
345 3     3 1 6 my $self = shift;
346 3         4 my $updated_line = shift;
347 3 100       12 if(defined $updated_line) {
    100          
348 1         3 $self->{updated_line} = $updated_line;
349 1         9 return 1;
350             } elsif (!defined $self->{updated_line}) {
351 1         5 return $self->line;
352             } else {
353 1         17 return $self->{updated_line};
354             }
355             }
356              
357             =head2 qname
358              
359             Description : Getter/Setter for attribute qname
360             ReturnType : String
361             Exceptions : none
362              
363             =cut
364              
365             sub qname {
366 3     3 1 1548 my $self = shift;
367 3         4 my $new_qname = shift;
368 3 100       12 if(defined $new_qname) {
369 1         4 $self->{qname} = $new_qname;
370             }
371 3         13 return $self->{qname};
372             }
373              
374             =head2 flag
375              
376             Description : Getter/Setter for attribute flag
377             ReturnType : String
378             Exceptions : none
379              
380             =cut
381              
382             sub flag {
383 6     6 1 10 my $self = shift;
384 6         7 my $new_flag = shift;
385 6 100       17 if(defined $new_flag) {
386 1         3 $self->{flag} = $new_flag;
387             }
388 6         34 return $self->{flag};
389             }
390              
391             =head2 rname
392              
393             Description : Getter/Setter for attribute rname (chromosome for eucaryotes)
394             ReturnType : String
395             Exceptions : none
396              
397             =cut
398              
399             sub rname {
400 6     6 1 8 my $self = shift;
401 6         9 my $new_rname = shift;
402 6 100       15 if(defined $new_rname) {
403 3         221 $self->{rname} = $new_rname;
404             }
405 6         25 return $self->{rname};
406             }
407              
408             =head2 chr
409              
410             Description : Getter/Setter for attribute rname (Alias)
411             ReturnType : String
412             Exceptions : none
413              
414             =cut
415              
416             sub chr {
417 3     3 1 5 my $self = shift;
418 3         7 $self->rname(@_);
419             }
420              
421             =head2 pos
422              
423             Description : Getter/Setter for attribute pos (position of the sequence)
424             ReturnType : String
425             Exceptions : none
426              
427             =cut
428              
429             sub pos {
430 3     3 1 5 my $self = shift;
431 3         5 my $new_pos = shift;
432 3 100       8 if(defined $new_pos) {
433 1         3 $self->{pos} = $new_pos;
434             }
435 3         11 return $self->{pos};
436             }
437              
438             =head2 mapq
439              
440             Description : Getter/Setter for attribute mapq (mapping quality)
441             ReturnType : String
442             Exceptions : none
443              
444             =cut
445              
446             sub mapq {
447 3     3 1 5 my $self = shift;
448 3         5 my $new_mapq = shift;
449 3 100       9 if(defined $new_mapq) {
450 1         3 $self->{mapq} = $new_mapq;
451             }
452 3         18 return $self->{mapq};
453             }
454              
455             =head2 cigar
456              
457             Description : Getter/Setter for attribute cigar (see SAM doc)
458             ReturnType : String
459             Exceptions : none
460              
461             =cut
462              
463             sub cigar {
464 4     4 1 7 my $self = shift;
465 4         7 my $new_cigar = shift;
466 4 100       12 if(defined $new_cigar) {
467 1         2 $self->{cigar} = $new_cigar;
468             }
469 4         25 return $self->{cigar};
470             }
471              
472             =head2 rnext
473              
474             Description : Getter/Setter for attribute rnext (see SAM doc)
475             ReturnType : String
476             Exceptions : none
477              
478             =cut
479              
480             sub rnext {
481 3     3 1 5 my $self = shift;
482 3         5 my $new_rnext = shift;
483 3 100       10 if(defined $new_rnext) {
484 1         3 $self->{rnext} = $new_rnext;
485             }
486 3         11 return $self->{rnext};
487             }
488              
489             =head2 pnext
490              
491             Description : Getter/Setter for attribute pnext (see SAM doc)
492             ReturnType : Integer
493             Exceptions : none
494              
495             =cut
496              
497             sub pnext {
498 3     3 1 6 my $self = shift;
499 3         5 my $new_pnext = shift;
500 3 100       17 if(defined $new_pnext) {
501 1         3 $self->{pnext} = $new_pnext;
502             }
503 3         12 return $self->{pnext};
504             }
505              
506             =head2 tlen
507              
508             Description : Getter/Setter for attribute tlen (sequence length)
509             ReturnType : Integer
510             Exceptions : none
511              
512             =cut
513              
514             sub tlen {
515 3     3 1 6 my $self = shift;
516 3         4 my $new_tlen = shift;
517 3 100       7 if(defined $new_tlen) {
518 1         3 $self->{tlen} = $new_tlen;
519             }
520 3         12 return $self->{tlen};
521             }
522              
523             =head2 seq
524              
525             Description : Getter/Setter for attribute seq (the sequence).
526             Please use getOriginalSeq if you want to retrieve the oriented
527             sequence, that what you need in most cases.
528             ReturnType : String
529             Exceptions : none
530              
531             =cut
532              
533             sub seq {
534 4     4 1 988 my $self = shift;
535 4         8 my $new_seq = shift;
536 4 100       14 if(defined $new_seq) {
537 1         3 $self->{seq} = $new_seq;
538             }
539 4         23 return $self->{seq};
540             }
541              
542             =head2 qual
543              
544             Description : Getter/Setter for attribute qual (sequence quality)
545             ReturnType : String
546             Exceptions : none
547              
548             =cut
549              
550             sub qual {
551 3     3 1 6 my $self = shift;
552 3         4 my $new_qual = shift;
553 3 100       7 if(defined $new_qual) {
554 1         3 $self->{qual} = $new_qual;
555             }
556 3         13 return $self->{qual};
557             }
558              
559             =head2 getOptionalField
560              
561             Example :
562             Description :
563             ReturnType :
564              
565             =cut
566              
567             sub getOptionalField {
568 0     0 1 0 my $self = shift;
569 0         0 my $field = shift;
570 0 0       0 croak("Missing \$field argument to call getOptionalField") unless defined $field;
571 0         0 return $self->{extended_fields}{$field};
572             }
573              
574              
575             =head2 getChimericAlignments
576              
577             Description : Parser of SA fields of SAM file in order to find chimeric reads
578             ReturnType : Array reference
579             Elements are hash [ chr => String,
580             pos => int,
581             strand => 1/-1,
582             cigar => String,
583             mapq => int,
584             edist => int
585             ]
586              
587             =cut
588              
589             sub getChimericAlignments {
590 1     1 1 4 my $self = shift;
591             # check the existence of the SA field in the SAM line
592 1 50       6 if (defined $self->{extended_fields}{SA}){
593 1         2 my @array_hash;
594 1         6 my (@SA_alignments) = split(/;/,$self->{extended_fields}{SA});
595 1         6 for (my $i=0 ; $i < scalar @SA_alignments ; $i++){
596 2         8 my ($chr,$pos,$strand,$cigar,$mapq,$edist) = split(/,/,$SA_alignments[$i]);
597             # strand switch from "+,-" to "1,-1"
598 2 100       8 if ($strand eq '+'){
599 1         2 $strand = 1;
600             }else{
601 1         3 $strand = -1;
602             }
603 2         12 my $hash = { chr => $chr,
604             pos => $pos,
605             strand => $strand,
606             cigar => $cigar,
607             mapq => $mapq,
608             edist => $edist};
609 2         7 push(@array_hash,$hash);
610             }
611 1         5 return \@array_hash;
612             }
613 0         0 return undef;
614             }
615              
616             =head2 getCigarOperatorsCount
617              
618             Example : my %cigar_counts = %{ $sam_line->getCigarOperatorsCount() };
619             print "nb mismatches; ",$cigar_counts{X},"\n";
620             Description : Return a hash reference where the keys are the cigar operators and the values
621             the sum of length associated for each operator.
622             For cigar 5S3M1X2M10S, getCigarOperatorsCounts() will retrun :
623             { 'S' => 15,
624             'M' => 5,
625             'X' => 1,
626             };
627             ReturnType : Hash reference
628             =cut
629              
630             sub getCigarOperatorsCount {
631 1     1 1 3726 my $self = shift;
632 1         6 my @ops = $self->cigar =~ /(\d+\D)/g;
633 1         3 my %ops_occ;
634 1         3 foreach (@ops) {
635 3         11 my ($nb,$op) = $_ =~ /(\d+)(\D)/;
636 3 100       13 $ops_occ{$op} = 0 unless defined $ops_occ{$op};
637 3         8 $ops_occ{$op} += $nb;
638             }
639 1         8 return \%ops_occ;
640             }
641              
642             =head2 pSupport
643              
644             Description : Return the support profile of the read if the SAM file has been generated with
645             CRAC option --detailed
646             ReturnType : String
647              
648             =cut
649              
650             sub pSupport {
651 1     1 1 3 my $self = shift;
652 1         5 $self->loadSamDetailed;
653 1         5 return $self->{sam_detailed}{p_support};
654             }
655              
656             =head2 pLoc
657              
658             Description : Return the location profile of the read if the SAM file has been generated with
659             CRAC option --detailed
660             ReturnType : String
661              
662             =cut
663              
664             sub pLoc {
665 1     1 1 3 my $self = shift;
666 1         4 $self->loadSamDetailed;
667 1         5 return $self->{sam_detailed}{p_loc};
668             }
669              
670             =head2 pairedChimera
671              
672             Description : return the chimeric coordinates of the paired chimera associated to this read if there is one
673              
674             ReturnType : array(chr1,pos1,strand1,chr2,pos2,strand2) or undef
675              
676             =cut
677              
678             sub pairedChimera {
679 1     1 1 2 my $self = shift;
680 1         5 $self->loadPaired();
681 1 50       5 if(defined $self->{extended_fields}{XP}{chimera}) {
682 1         5 my ($crac_loc1,$crac_loc2) = split(":",$self->{extended_fields}{XP}{chimera});
683 1         4 return (expandCracLoc($crac_loc1),expandCracLoc($crac_loc2));
684             } else {
685 0         0 return undef;
686             }
687             }
688              
689             =head2 isPairedClassified
690              
691             Arg [1] : String - The class to test :
692             - "unique"
693             - "duplicated"
694             - "multiple"
695              
696             Description : Test paired-end read clasification
697              
698             ReturnType : Boolean
699              
700             =cut
701              
702             sub isPairedClassified {
703 3     3 1 3639 my $self = shift;
704 3         8 my $class = shift;
705 3         8 $self->loadPaired();
706              
707 3 100 66     21 if(defined $self->{extended_fields}{XP}{loc} && ref($self->{extended_fields}{XP}{loc}) ne 'HASH') {
708 1         6 my ($uniq,$dupli,$multi) = split(":",$self->{extended_fields}{XP}{loc});
709 1         7 $self->{extended_fields}{XP}{loc} = {unique => $uniq, duplicated => $dupli, multiple => $multi};
710              
711             }
712 3         18 return $self->{extended_fields}{XP}{loc}{$class};
713             }
714              
715              
716             =head2 genericInfo
717            
718             [1] : Key of the generic info
719             [2] : (Optional) Value of the generic info
720              
721             Description : Getter/Setter enable to store additional (generic) information
722             about the SAMline as a Key/Value.
723             Example : # Set a generic info
724             $read->genericInfo("foo","bar")
725              
726             # Get a generic info
727             print $read->genericInfo("foo"); # this will print "bar"
728             ReturnType : ?
729             Exceptions : none
730              
731             =cut
732              
733             sub genericInfo {
734 2     2 1 5 my ($self,$key,$value) = @_;
735 2 50       8 if(!defined $key) {
    100          
736 0         0 die "You need to provide a key in order to set or get a genericInfo\n";
737             } elsif(defined $value) {
738 1         4 $self->{genericInfo}{$key} = $value;
739             } else {
740 1         6 return $self->{genericInfo}{$key};
741             }
742             }
743              
744             =head2 isClassified
745              
746             Arg [1] : String - The class to test :
747             - "unique"
748             - "duplicated"
749             - "multiple"
750             - "normal"
751             - "almostNormal"
752              
753             Example : if($sam_line->isClassified('normal')) {
754             DO_SOMETHING;
755             }
756             Description : Test if the line is classified according to the parameter value.
757             ReturnType : Boolean
758             Exceptions : none
759              
760             =cut
761              
762             sub isClassified {
763 2     2 1 5 my $self = shift;
764 2         3 my $class = shift;
765            
766 2 50       6 croak "Missing class argument" unless defined $class;
767              
768 2 100       8 if($class eq "unique") {
    50          
    50          
    0          
    0          
769 1         5 return $self->{extended_fields}{XU};
770             } elsif($class eq "duplicated") {
771 0         0 return $self->{extended_fields}{XD};
772             } elsif($class eq "multiple") {
773 1         16 return $self->{extended_fields}{XM};
774             } elsif($class eq "normal") {
775 0         0 return $self->{extended_fields}{XN} == 1;
776             } elsif($class eq "almostNormal") {
777 0         0 return $self->{extended_fields}{XN} == 2;
778             } else {
779 0         0 croak "Class argument ($class) does not match any case";
780             }
781             }
782              
783             =head2 events
784              
785             Arg [1] : String - The event type to return :
786             - Junction
787             - Ins
788             - Del
789             - SNP
790             - Error
791             - Chimera
792             - Undetermined
793             - BioUndetermined
794             - ... (see CRAC SAM format specifications for more informations).
795             Example : my @junctions = @{$line->events('Junction')};
796             foreach my $junction (@junctions) {
797             print "Foud Junction : [type : $junction->{type}, loc : $junction->{loc}, gap : $junction->{gap}]\n";
798             }
799             Description : Return all events of the type specified in parameter
800             ReturnType : Array reference
801             Exceptions : none
802              
803             =cut
804              
805             sub events {
806 3     3 1 8 my $self = shift;
807 3         4 my $event_type = shift;
808 3         10 $self->loadEvents($event_type);
809 3 50       9 if(defined $self->{events}{$event_type}) {
810 3         18 return $self->{events}{$event_type};
811             } else {
812 0         0 return [];
813             }
814             }
815              
816             =head1 PRIVATE METHODS
817              
818             =head2 loadEvents
819              
820             Example : $sam_line->loadEvents();
821             Description : Loading of events attributes
822             ReturnType : none
823             Exceptions : none
824              
825             =cut
826              
827             sub loadEvents {
828 3     3 1 5 my $self = shift;
829 3         4 my $event_type_to_load = shift;
830             ## TODO avoid double loading when doing lazy loading
831 3 100 66     24 if(defined $event_type_to_load && defined $self->{$event_type_to_load}{loaded}) {
832 2         5 return 0;
833             }
834 1 50 33     10 if(!defined $self->{events} && defined $self->{extended_fields}{XE}) {
835             # Init events
836 1         6 my @events = split(";",$self->{extended_fields}{XE});
837 1         3 foreach my $event (@events) {
838 1         9 my ($event_id,$event_break_id,$event_type,$event_infos) = $event =~ /([^:]+):([^:]+):([^:]+):(.*)/g;
839 1 50 33     11 next if(defined $event_type_to_load && $event_type ne $event_type_to_load);
840 1 50       14 if(defined $event_id) {
841 1         2 my %event_hash;
842 1 50 0     5 if($event_type eq 'Junction') {
    0          
    0          
    0          
    0          
    0          
    0          
843 1         11 my ($type,$pos_read,$loc,$gap) = split(':',$event_infos);
844 1         12 my ($chr,$pos,$strand) = expandCracLoc($loc);
845 1         10 %event_hash = ( type => $type,
846             pos => $pos_read,
847             loc => {chr => $chr, pos => $pos, strand => $strand},
848             gap => $gap,
849             );
850             } elsif($event_type eq 'Ins' || $event_type eq 'Del') {
851 0         0 my ($score,$pos_read,$loc,$nb) = split(':',$event_infos);
852 0         0 my ($chr,$pos,$strand) = expandCracLoc($loc);
853 0         0 %event_hash = ( score => $score,
854             pos => $pos_read,
855             loc => {chr => $chr, pos => $pos, strand => $strand},
856             nb => $nb,
857             );
858             } elsif($event_type eq 'SNP') {
859 0         0 my ($score,$pos_read,$loc,$expected,$actual) = split(':',$event_infos);
860 0         0 my ($chr,$pos,$strand) = expandCracLoc($loc);
861 0         0 %event_hash = ( score => $score,
862             pos => $pos_read,
863             loc => {chr => $chr, pos => $pos, strand => $strand},
864             expected => $expected,
865             actual => $actual,
866             );
867             } elsif($event_type eq 'Error') {
868 0         0 my ($type,$pos,$score,$other1,$other2) = split(':',$event_infos);
869 0         0 %event_hash = ( score => $score,
870             pos => $pos,
871             type => $type,
872             other1 => $other1,
873             other2 => $other2,
874             );
875             } elsif($event_type eq 'chimera') {
876 0         0 my ($pos_read,$loc1,$loc2) = split(':',$event_infos);
877 0         0 my ($chr1,$pos1,$strand1) = expandCracLoc($loc1);
878 0         0 my ($chr2,$pos2,$strand2) = expandCracLoc($loc2);
879 0         0 %event_hash = ( pos => $pos_read,
880             loc1 => {chr => $chr1, pos => $pos1, strand => $strand1},
881             loc2 => {chr => $chr2, pos => $pos2, strand => $strand2},
882             );
883             } elsif($event_type eq 'Undetermined') {
884 0         0 %event_hash = ( message => $event_infos,
885             );
886             } elsif($event_type eq 'BioUndetermined') {
887 0         0 my ($pos,$message) = $event_infos =~ /([^:]+):(.*)/;
888 0         0 %event_hash = ( pos => $pos,
889             message => $message,
890             );
891             }
892 1 50       6 if (keys %event_hash > 1) {
893 1         3 $event_hash{event_id} = $event_id;
894 1         2 $event_hash{break_id} = $event_break_id;
895 1         3 $event_hash{event_type} = $event_type;
896 1         5 $self->addEvent(\%event_hash);
897             }
898             }
899             }
900             # If we have only load a specific event type
901 1 50       3 if(defined $event_type_to_load) {
902 1         5 $self->{$event_type_to_load}{loaded} = 1;
903             # Else we have load every events.
904             } else {
905 0         0 $self->{events}{loaded} = 1;
906             }
907             }
908             }
909              
910             =head2 addEvent
911              
912             Arg [1] : String - The event type
913             Arg [2] : Hash reference - The event object
914             Example : $line->addEvent($event_type,\%event);
915             Description : Return all events of the type specified in parameter
916             ReturnType : none
917             Exceptions : none
918              
919             =cut
920              
921             sub addEvent {
922 2     2 1 5 my $self = shift;
923 2         2 my $event = shift;
924 2         5 my $event_type = $event->{event_type};
925 2 100       10 if(defined $self->{events}{$event_type}) {
926 1         3 push(@{$self->{events}{$event_type}},$event);
  1         48  
927             } else {
928 1         8 $self->{events}{$event_type} = [$event];
929             }
930             }
931              
932             =head2 removeEvent
933              
934             Arg [1] : Hash reference - The event object
935              
936             Description : Remove the event from the event hash and from the line.
937              
938             =cut
939              
940             sub removeEvent {
941 2     2 1 18 my $self = shift;
942 2         3 my $delete_event = shift;
943 2         4 my $type = $delete_event->{event_type};
944 2 50 33     15 if(defined $type && defined $self->{events}{$type}) {
945 2         4 my $i = 0;
946 2         2 foreach my $event (@{$self->{events}{$type}}) {
  2         6  
947 2 50       9 if($event eq $delete_event) {
948 2         2 splice @{$self->{events}{$type}}, $i, 1;
  2         7  
949 2         8 return 1;
950             }
951 0         0 $i++;
952             }
953             }
954 0         0 return 0;
955             }
956              
957             =head2 updateEvent
958            
959              
960             =cut
961              
962             sub updateEvent {
963 1     1 1 3147 my $self = shift;
964 1         3 my $event = shift;
965 1         3 my $new_event_type = shift;
966 1         5 my %new_event = @_;
967              
968             # Update new event with old break id and event id
969 1         3 $new_event{event_type} = $new_event_type;
970 1         3 $new_event{event_id} = $event->{event_id};
971 1         3 $new_event{break_id} = $event->{break_id};
972              
973 1 50       6 if($self->removeEvent($event)) {
974             # Catch warnings on string concatenation that correspond to missing
975             # field in the hash for the event to update
976 1     0   10 local $SIG{'__WARN__'} = sub {croak("Invalid event hash for event type '$new_event_type'");};
  0         0  
977 1         5 my $base_XE = 'XE:Z:'.$new_event{event_id}.':'.$new_event{break_id};
978 1         3 my $new_XE = $base_XE . ':';
979 1 50 0     4 if($new_event_type eq 'Junction') {
    0          
    0          
    0          
    0          
    0          
    0          
980 1         7 my $loc = compressCracLoc($new_event{loc}{chr},$new_event{loc}{pos},$new_event{loc}{strand});
981 1         12 $new_XE .= $new_event_type.':'.
982             $new_event{type}.':'.
983             $new_event{pos}.':'.
984             $loc.':'.
985             $new_event{gap};
986             } elsif($new_event_type eq 'Ins' || $new_event_type eq 'Del') {
987 0         0 my $loc = compressCracLoc($new_event{loc}{chr},$new_event{loc}{pos},$new_event{loc}{strand});
988 0         0 $new_XE .= $new_event_type.':'.
989             $new_event{score}.':'.
990             $new_event{pos}.':'.
991             $loc.':'.
992             $new_event{nb};
993             } elsif($new_event_type eq 'SNP') {
994 0         0 my $loc = compressCracLoc($new_event{loc}{chr},$new_event{loc}{pos},$new_event{loc}{strand});
995 0         0 $new_XE .= $new_event_type.':'.
996             $new_event{score}.':'.
997             $new_event{pos}.':'.
998             $loc.':'.
999             $new_event{expected}.':'.
1000             $new_event{actual};
1001             } elsif($new_event_type eq 'Error') {
1002 0         0 my $loc = compressCracLoc($new_event{loc}{chr},$new_event{loc}{pos},$new_event{loc}{strand});
1003 0         0 $new_XE .= $new_event_type.':'.
1004             $new_event{type}.':'.
1005             $new_event{pos}.':'.
1006             $new_event{score}.':'.
1007             $new_event{other1}.':'.
1008             $new_event{other2};
1009             } elsif($new_event_type eq 'chimera') {
1010 0         0 my $loc1 = compressCracLoc($new_event{loc1}{chr},$new_event{loc1}{pos},$new_event{loc1}{strand});
1011 0         0 my $loc2 = compressCracLoc($new_event{loc2}{chr},$new_event{loc2}{pos},$new_event{loc2}{strand});
1012 0         0 $new_XE .= $new_event_type.':'.
1013             $new_event{pos}.':'.
1014             $loc1.':'.
1015             $loc2;
1016             } elsif($new_event_type eq 'Undetermined') {
1017 0         0 $new_XE .= $new_event_type.':'.
1018             $new_event{message};
1019             } elsif($new_event_type eq 'BioUndetermined') {
1020 0         0 $new_XE .= $new_event_type.':'.
1021             $new_event{pos}.':'.
1022             $new_event{message};
1023             } else {
1024 0         0 croak("Unknown type of event : $new_event_type");
1025             }
1026 1         5 $self->addEvent(\%new_event);
1027 1         6 my $new_line = $self->updatedLine;
1028 1         44 $new_line =~ s/($base_XE:[^\t]*)/$new_XE/;
1029 1         4 $self->updatedLine($new_line);
1030             } else {
1031 0         0 croak('Event not find');
1032             }
1033             }
1034              
1035             =head2 loadSamDetailed
1036              
1037             Example : $sam_line->loadSamDetailed();
1038             Description : Loading of sam detaileds attributes
1039             ReturnType : none
1040             Exceptions : none
1041              
1042             =cut
1043              
1044             sub loadSamDetailed {
1045 2     2 1 3 my $self = shift;
1046 2 100       10 if(!defined $self->{sam_detailed}) {
1047             #my ($detailed) = $self->line =~ /XR:Z:([^\s]+)/g;
1048 1         4 my $detailed = $self->{extended_fields}{XR};
1049 1         6 my @detailed_fields = split(";",$detailed);
1050 1         3 foreach (@detailed_fields) {
1051 2         6 my ($k,$v) = split('=',$_);
1052 2 100       11 if($k eq 'p_loc') {
    50          
1053 1         5 $self->{sam_detailed}{p_loc} = $v;
1054             } elsif($k eq 'p_support') {
1055 1         6 $self->{sam_detailed}{p_support} = $v;
1056             } else {
1057 0         0 carp("Unknown sam detailed field : $k");
1058             }
1059             }
1060 1         4 $self->{sam_detailed}{loaded} = 1;
1061             }
1062             }
1063              
1064             =head2 loadPaired
1065              
1066             Example : $sam_line->loadPaired();
1067             Description : Loading of sam detaileds attributes
1068             ReturnType : none
1069             Exceptions : none
1070              
1071             =cut
1072              
1073             sub loadPaired {
1074 4     4 1 7 my $self = shift;
1075             # If XP fields exist and we haven't load it already
1076 4 100 66     38 if(defined $self->{extended_fields}{XP} && ref($self->{extended_fields}{XP}) ne 'HASH') {
1077 1         6 my @paired_fields = split(";",$self->{extended_fields}{XP});
1078 1         4 $self->{extended_fields}{XP} = {}; # Chamge type of XP exetended field from scalar to hash ref
1079 1         2 foreach (@paired_fields) {
1080 2         5 my($key,$value) = split(":",$_,2);
1081 2         8 $self->{extended_fields}{XP}{$key} = $value;
1082             }
1083             }
1084             }
1085              
1086             =head2 expandCracLoc
1087              
1088             Arg [1] : String - Localisation in crac format : Chromosome|strand,position
1089             Ex : X|-1,2332377
1090              
1091             Description : Extract Chromosme, position and strand as separated variable from
1092             the localisation in CRAC format.
1093             ReturnType : Array($chromosome,$position,$strand)
1094              
1095             =cut
1096              
1097             sub expandCracLoc {
1098 3     3 1 6 my $loc = shift;
1099 3         23 my($chr,$strand,$pos) = $loc =~ /(\S+)\|(\S+)?,(\S+)?/;
1100 3         13 return ($chr,$pos,$strand);
1101             }
1102              
1103             =head2 compressCracLoc
1104              
1105             Arg [1] : String - Chromosome
1106             Arg [2] : Integer - Postition
1107             Arg [3] : Integer (1,-1) - Strand
1108              
1109             Description : Reverse function of "expandCracLoc"
1110             ReturnType : String (localisation in CRAC format)
1111              
1112             =cut
1113              
1114             sub compressCracLoc {
1115 1     1 1 9 my ($chr,$pos,$strand) = @_;
1116 1 50 33     13 confess("Missing argument") unless defined $chr && defined $pos && defined $strand;
      33        
1117 1         5 return $chr."|".$strand.",".$pos;
1118             }
1119              
1120             =head1 AUTHORS
1121              
1122             Jerome AUDOUX ELE.
1123              
1124             =head1 COPYRIGHT AND LICENSE
1125              
1126             Copyright (C) 2012-2013 -- IRB/INSERM
1127             (Institut de Recherche en Biothérapie /
1128             Institut National de la Santé et de la
1129             Recherche Médicale)
1130             LIRMM/UM2
1131             (Laboratoire d'Informatique, de Robotique et de
1132             Microélectronique de Montpellier /
1133             Université de Montpellier 2)
1134              
1135             =head2 FRENCH
1136              
1137             Ce fichier fait partie du Pipeline de traitement de données NGS de la
1138             plateforme ATGC labélisée par le GiS IBiSA.
1139              
1140             Ce logiciel est régi par la licence CeCILL soumise au droit français et
1141             respectant les principes de diffusion des logiciels libres. Vous pouvez
1142             utiliser, modifier et/ou redistribuer ce programme sous les conditions de
1143             la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA sur
1144             le site "http://www.cecill.info".
1145              
1146             =head2 ENGLISH
1147              
1148             This File is part of the NGS data processing Pipeline of the ATGC
1149             accredited by the IBiSA GiS.
1150              
1151             This software is governed by the CeCILL license under French law and
1152             abiding by the rules of distribution of free software. You can use,
1153             modify and/ or redistribute the software under the terms of the CeCILL
1154             license as circulated by CEA, CNRS and INRIA at the following URL
1155             "http://www.cecill.info".
1156              
1157             =cut
1158              
1159             1;