File Coverage

blib/lib/Bio/Polloc/LocusIO/gff3.pm
Criterion Covered Total %
statement 136 146 93.1
branch 49 84 58.3
condition 19 42 45.2
subroutine 10 11 90.9
pod 2 2 100.0
total 216 285 75.7


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Bio::Polloc::LocusIO::gff3 - A LocusIO for Gff3
4              
5             =head1 DESCRIPTION
6              
7             A repeatitive locus.
8              
9             =head1 IMPLEMENTS OR EXTENDS
10              
11             =over
12              
13             =item *
14              
15             L<Bio::Polloc::LocusIO>
16              
17             =back
18              
19             =head1 AUTHOR - Luis M. Rodriguez-R
20              
21             Email lmrodriguezr at gmail dot com
22              
23             =cut
24              
25             package Bio::Polloc::LocusIO::gff3;
26 2     2   10 use base qw(Bio::Polloc::LocusIO);
  2         3  
  2         199  
27 2     2   14 use strict;
  2         70  
  2         80  
28 2     2   1702 use Bio::Polloc::LociGroup;
  2         6  
  2         72  
29 2     2   750 use Bio::Polloc::LocusI;
  2         5  
  2         4197  
30             our $VERSION = 1.0503; # [a-version] from Bio::Polloc::Polloc::Version
31              
32              
33             =head1 APPENDIX
34              
35             Methods provided by the package
36              
37             =head2 new
38              
39             Creates a B<Bio::Polloc::LocusIO::gff3> object.
40              
41             =head3 Returns
42              
43             A L<Bio::Polloc::LocusIO::gff3> object.
44              
45             =cut
46              
47             sub new {
48 0     0 1 0 my($caller,@args) = @_;
49 0         0 my $self = $caller->SUPER::new(@args);
50 0         0 $self->_initialize(@args);
51 0         0 return $self;
52             }
53              
54             =head2 gff3_line
55              
56             Formats the locus as a GFF3 line and returns it.
57              
58             =head3 Arguments
59              
60             =over
61              
62             =item -force
63              
64             Boolean (1 or 0)
65              
66             =back
67              
68             =head3 Returns
69              
70             The GFF3-formatted line (str)
71             =head3 Note
72              
73             This function stores the line in cache. If it is called twice, the second
74             time will return the cached line unless the C<-force=>1> flag is passed.
75              
76             =cut
77              
78             sub gff3_line {
79 3     3 1 6 my($self,@args) = @_;
80 3         14 my($locus, $force) = $self->_rearrange([qw(LOCUS FORCE)], @args);
81 3 50 33     37 defined $locus and UNIVERSAL::can($locus, 'isa') and $locus->isa('Bio::Polloc::LocusI')
      33        
82             or $self->throw("Undefined locus or bad type", $locus);
83 3 50 33     15 return $locus->{'_gff3_line'} if defined $locus->{'_gff3_line'} and not $force;
84 3         4 my @out;
85 3 50       11 push @out, defined $locus->seq_name ? $locus->seq_name : ".";
86 3         8 $out[0] =~ s/^>/{{%}}3E/;
87 3         13 push @out, $locus->source; #defined $locus->rule ? $locus->rule->source : 'bme';
88 3         10 push @out, $locus->family;
89 3         22 push @out, $locus->from , $locus->to;
90 3 50       11 push @out, defined $locus->score ? $locus->score : ".";
91 3         9 push @out, $locus->strand, "0";
92 3         6 my %atts;
93 3 50       8 $atts{'ID'} = $locus->id if defined $locus->id;
94 3 50       11 $atts{'Name'} = $locus->name if defined $locus->name;
95 3 50       17 $atts{'Alias'} = $locus->aliases if defined $locus->aliases;
96 3 50       14 $atts{'Parent'} = $locus->parents if defined $locus->parents;
97 3 50 33     10 $atts{'organism_name'} = $locus->genome->name
98             if defined $locus->genome and defined $locus->genome->name;
99 3 50       12 if(defined $locus->target){
100 0         0 my $tid = $locus->target->{'id'};
101 0         0 $tid =~ s/\s/{{%}}20/g;
102 0         0 $atts{'Target'} = $tid . " " . $locus->target->{'from'} . " " . $locus->target->{'to'};
103             }
104             # TODO Gap
105 3 50       10 $atts{'Note'} = [split /[\n\r]+/, $locus->comments] if defined $locus->comments;
106 3 50       15 $atts{'Dbxref'} = $locus->xrefs if defined $locus->xrefs;
107 3 50       12 $atts{'Ontology_term'} = $locus->ontology_terms_str if defined $locus->ontology_terms_str;
108 3         17 my $o = "";
109 3         6 for my $v (@out){
110 24         44 $o.= $self->_gff3_value($v)."\t";
111             }
112 3         5 $a = "";
113 3         8 for my $k (keys %atts){
114 9         25 $a .= "$k=" . $self->_gff3_attribute($atts{$k}).";";
115             }
116 3 50       12 $a = substr($a,0,-1) if $a;
117 3         13 $locus->{'_gff3_line'} = $o . $a . "\n";
118 3         18 return $locus->{'_gff3_line'};
119             }
120              
121              
122             =head1 INTERNAL METHODS
123              
124             Methods intended to be used only within the scope of Bio::Polloc::*
125              
126             =head2 _next_locus_impl
127              
128             =cut
129              
130             sub _next_locus_impl {
131 90     90   121 my ($self, @args) = @_;
132 90         304 my($genomes) = $self->_rearrange([qw(GENOMES)], @args);
133 90         150 my $ln;
134 90         306 while($ln = $self->_readline){
135 94 100 100     577 last unless $ln =~ /^\s*#/ or $ln =~ /^\s*$/;
136             }
137 90 100       166 return unless $ln;
138 87         325 $self->debug("Parsing: $ln");
139 87         443 my @row = split /\t/, $ln;
140 87         217 my $seqid = $self->_gff3_decode($row[0]);
141 87         172 my $source = $self->_gff3_decode($row[1]);
142 87         178 my $family = $self->_gff3_decode($row[2]);
143 87         176 my $from = $self->_gff3_decode($row[3]);
144 87         175 my $to = $self->_gff3_decode($row[4]);
145 87         168 my $score= $self->_gff3_decode($row[5]);
146 87         180 my $strand = $self->_gff3_decode($row[6]);
147 87         155 my $frame = $self->_gff3_decode($row[7]);
148 87         257 my @compl = split /;/, $row[8];
149 87         127 my %atts = ();
150 87         124 for my $c (@compl){
151 229 50       947 $c =~ /(.+?)=(.*)/ or next;
152 229         488 my ($k,$v) = ($1, $2);
153 229         406 $atts{lc $k} = $self->_gff3_decode($v);
154             }
155 87         140 my $id = $atts{'id'};
156 87         112 my $name = $atts{'name'};
157 87         116 my $genome_name = $atts{'organism_name'};
158 87         115 my @comments = ();
159 87 50       361 @comments = split /,/, $atts{'note'} if defined $atts{'note'};
160 87         317 my $f = Bio::Polloc::LocusI->_qualify_type($family);
161 87 50 33     1138 my $type = ($f eq 'amplicon' or $f eq 'amplification') ? 'amplicon' :
    50 33        
    50 0        
    50          
    50          
162             ($f eq 'composition') ? 'composition' :
163             ($f eq 'crispr') ? 'crispr' :
164             ($f eq 'pattern' or $f eq 'domain') ? 'pattern' :
165             ($f eq 'repeat' or $f eq 'vntr' or $f =~ /tandem.?repeat/
166             or lc $source eq 'trf' or lc $source eq 'mreps') ? 'repeat' :
167             'generic';
168 87 100       139 $type = "extend" if grep{ /Extended feature/ } @comments;
  298         615  
169 87         583 my $locus = Bio::Polloc::LocusI->new(
170             -id=>$id, -name=>$name,
171             -type=>$type,
172             -from=>$from, -to=>$to, -strand=>$strand,
173             -source=>$source, -family=>$family,
174             -score=>$score,
175             -seqname=>$seqid);
176             # Parse comments
177 87         201 for my $comm (@comments){
178 298 100 66     1289 if($comm =~ m/^(.+?)=(.+)$/){
    100          
179 215         450 my ($k, $v) = (lc $1, $2);
180 215 100 66     1367 if($k and $v and $locus->can($k)){
      66        
181 207         710 $self->debug("Setting $k to $v");
182 207         559 $locus->$k($v);
183 207         353 next;
184             }
185 8 50 33     82 $genome_name = $v if not defined $genome_name and
      33        
186             ($k =~ /^organism(?:_name)?$/ or $k =~ /^genome(?:_name)?$/);
187             }elsif($type eq 'extend' and $comm =~ m/Based on group [^:]+: (.*)/){
188 34         120 for my $b (split /\s/, $1){
189 34 50       103 $locus->basefeature($self->_locus_by_id($b)) if defined $self->_locus_by_id($b);
190             }
191             }
192             }
193 87 100       176 if(defined $genomes){
194 2         3 my $genome;
195             # Search the genome by name:
196 2 50       6 if(defined $genome_name){
197 2         4 for my $g (@$genomes){
198 2 50       8 $genome = $g if $g->name eq $genome_name;
199 2 50       6 last if defined $genome;
200             }
201             }
202             # Search the genome by sequence name (prone to errors, but it's a guess):
203 2 50       11 unless(defined $genome){
204 0         0 for my $g (@$genomes){
205 0 0       0 $genome = $g if defined $g->search_sequence($seqid);
206 0 0       0 last if defined $genome;
207             }
208             }
209 2         7 $locus->genome($genome);
210             }
211 87         239 $locus->comments(@comments);
212 87         271 return $self->_save_locus($locus);
213             }
214              
215             =head2 _write_locus_impl
216              
217             =cut
218              
219             sub _write_locus_impl {
220 3     3   5 my $self = shift;
221 3         10 my $line = $self->gff3_line(@_);
222 3 100       9 unless($self->{'_header'}){
223 1         30 $self->_print("##gff-version 3\n\n");
224 1         3 $self->{'_header'} = 1;
225             }
226 3         11 $self->_print($line);
227             }
228              
229             =head2 _gff3_attribute
230              
231             Properly escapes an attribute for GFF3 (an attribute the value of one of
232             the colon-separated entries in the ninth column)
233              
234             =head3 Purpose
235              
236             To simplify the code of L<Bio::Polloc::LocusI::gff3_line>
237              
238             =head3 Arguments
239              
240             The value to escape
241              
242             =head3 Returns
243              
244             The escaped value
245              
246             =cut
247              
248             sub _gff3_attribute {
249 9     9   13 my($self,$value) = @_;
250 9 50       19 return unless defined $value;
251 9 100 66     39 if(ref($value) && ref($value) =~ m/array/i){
252 3         5 my $out = "";
253 3         4 for my $att (@{$value}){
  3         7  
254 15         29 $out.= "," . $self->_gff3_value($att);
255             }
256 3 50       12 $out = substr($out, 1) if $out;
257 3         14 return $out;
258             }
259 6         13 return $self->_gff3_value($value);
260             }
261              
262             =head2 _gff3_value
263              
264             Properly escapes a value on the GFF3 line. I.e., the content of one column.
265             Not to be used with the ninth column, because scapes the colon. the comma and
266             the equals signs. Use instead the L<_gff3_attribute> function attribute by
267             attribute
268              
269             =head3 Purpose
270              
271             To simplify the code of L<gff3_line>
272              
273             =head3 Arguments
274              
275             The value to escape
276              
277             =head3 Returns
278              
279             The escaped value
280              
281             =cut
282              
283             sub _gff3_value {
284 45     45   55 my ($self,$value) = @_;
285 45 50       77 return unless defined $value;
286 45         61 $value =~ s/%/%25/g;
287 45         45 $value =~ s/\{\{%25\}\}/%/g;
288 45         43 $value =~ s/\t/%9/g;
289 45         48 $value =~ s/\n/\%D/g;
290 45         43 $value =~ s/\r/\%A/g;
291 45         46 $value =~ s/;/%3B/g;
292 45         106 $value =~ s/=/%3D/g;
293 45         47 $value =~ s/&/%26/g;
294 45         51 $value =~ s/,/%2C/g;
295 45         49 $value =~ s/ /%20/g;
296 45         135 return $value;
297             }
298              
299             =head2 _gff_decode
300              
301             Decodes the URI-fashioned values on GFF3
302              
303             =head3 Arguments
304              
305             The value to decode (str)
306              
307             =head3 Returns
308              
309             The decoded value (str)
310              
311             =cut
312              
313             sub _gff3_decode {
314 925     925   1088 my($self,$value) = @_;
315 925 50       1426 return unless defined $value;
316 925         1014 $value =~ s/%25/%/g;
317 925         917 $value =~ s/%9/\t/g;
318 925         875 $value =~ s/\%D/\n/g;
319 925         931 $value =~ s/\%A/\r/g;
320 925         885 $value =~ s/%3B/;/g;
321 925         1062 $value =~ s/%3D/=/g;
322 925         892 $value =~ s/%26/&/g;
323 925         862 $value =~ s/%2C/,/g;
324 925         1019 $value =~ s/%20/ /g;
325 925         1970 return $value;
326             }
327              
328             1;