[Bio] / FigKernelPackages / FigGFF.pm Repository:
ViewVC logotype

Annotation of /FigKernelPackages/FigGFF.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (view) (download) (as text)

1 : olson 1.1 #
2 :     # FIG GFF utilities.
3 :     #
4 :    
5 :     #
6 :     # A GFFWriter handles the generation of GFF files from SEED data structures.
7 :     #
8 :    
9 :    
10 :     package GFFWriter;
11 :    
12 :     use strict;
13 :     use FIG;
14 :    
15 :     use Carp;
16 :     use URI::Escape;
17 :     use Data::Dumper;
18 :    
19 :     sub new
20 :     {
21 :     my($class, $fig, %options) = @_;
22 :    
23 :     my $default_options = {
24 :     escapespace => 0,
25 :     outputfasta => 1,
26 :     linelength => 60,
27 :     };
28 :    
29 :     map { $default_options->{$_} = $options{$_} } keys(%options);
30 :    
31 :     my $self = {
32 :     options => $default_options,
33 :     contig_length_cache => {},
34 :     fig => $fig,
35 :     };
36 :    
37 :     return bless $self, $class;
38 :     }
39 :    
40 :    
41 :     =head1 gff3_for_feature
42 :    
43 :     Returns the GFF3 information for a given feature.
44 :    
45 :     The return is a pair ($contig_data, $fasta_sequences) that can be passed
46 :     into write_gff3().
47 :    
48 :     $contig_data is a hashref mapping a contig name to a list of GFF3 file lines
49 :     for the sequences in that contig.
50 :    
51 :     =cut
52 :    
53 :     sub gff3_for_feature
54 :     {
55 : olson 1.2 my($self, $fid, $user, $user_note) = @_;
56 : olson 1.1
57 :     #
58 :     # Options we need to figure out somehow.
59 :     #
60 :     my $options = $self->{options};
61 :    
62 :     my $escapespace = $options->{escapespace};
63 :     my $outputfasta = $options->{outputfasta};
64 : olson 1.2
65 :     my %outputtype;
66 :     map { $outputtype{$_} = 1 } @{$options->{outputtype}};
67 : olson 1.1
68 :     my $fastasequences = '';
69 :     my $contig_data;
70 :     my $linelength = $options->{linelength};
71 :    
72 :     my $fig = $self->{fig};
73 :    
74 :     #
75 :     # Do this first to make sure that we really have a feature.
76 :     #
77 :     my @location = $fig->feature_location($fid);
78 :     if (@location == 0 or !defined($location[0]))
79 :     {
80 :     warn "No location found for feature $fid\n";
81 :     return ({}, "");
82 :     }
83 :    
84 :     ###########
85 :     #
86 :     # Begin figuring out the column 9 information about notes and aliases and GO terms
87 :     # All the information is temporarily stored in @alias or @note, and at the end is joined
88 :     # into $allnote
89 :     #
90 :     ###########
91 :    
92 :     #
93 :     # the notes for the last column
94 :     #
95 :     my $note;
96 :     #
97 :     # all the aliases we are going to use
98 :     #
99 :     my @alias;
100 :    
101 : olson 1.2 if ($options->{with_assignments})
102 : olson 1.1 {
103 : olson 1.2 my $func = $fig->function_of($fid, $user);
104 :     if ($func)
105 :     {
106 :     push @$note, ("Note=\"" . uri_escape($func) . '"');
107 :     }
108 : olson 1.1 }
109 : olson 1.2
110 :     if ($options->{with_aliases})
111 :     {
112 : olson 1.1 # now find aliases
113 : olson 1.2 foreach my $alias ($fig->feature_aliases($fid))
114 : olson 1.1 {
115 : olson 1.2 if ($alias =~ /^NP/)
116 :     {
117 :     push @$note, "Dbxref=\"NCBI_genpept:$alias\"";
118 :     }
119 :     elsif ($alias =~ /gi\|/)
120 :     {
121 :     $alias =~ s/^gi\|//;
122 :     push @$note, "Dbxref=\"NCBI_gi:$alias\"";
123 :     }
124 :     elsif ($alias =~ /^kegg/i)
125 :     {
126 :     $alias =~ s/kegg\|/KEGG:/i;
127 :     $alias =~ s/^(.*):/$1+/;
128 :     push @$note, "Dbxref=\"$alias\"";
129 :     }
130 :     elsif ($alias =~ /^uni/)
131 :     {
132 :     $alias =~ s/uni\|/UniProt:/;
133 :     # $note = check_go($note, $alias) if ($go);
134 :     push @$note, "Dbxref=\"$alias\"";
135 :     }
136 :     elsif ($alias =~ /^sp\|/)
137 :     {
138 :     $alias =~ s/sp\|/Swiss-Prot:/;
139 :     push @$note, "Dbxref=\"$alias\"";
140 :     }
141 :     else
142 :     {
143 :     # don't know what it is so keep it as an alias
144 :     $alias = uri_escape($alias); # just in case!
145 :     push @alias, $alias;
146 :     }
147 : olson 1.1 }
148 : olson 1.2 }
149 : olson 1.1
150 :     # now just join all the aliases and put them into @$note so we can add it to the array
151 :     if (@alias)
152 :     {
153 :     push @$note, "Alias=\"". join (",", @alias) . '"';
154 :     }
155 : olson 1.2
156 :     #
157 :     # If we have user note passed in, add it.
158 :     #
159 :    
160 :     if ($user_note)
161 :     {
162 :     push @$note, $user_note;
163 :     }
164 : olson 1.1
165 :     # the LAST thing I am going to add as a note is the FIG id so that I can grep it out easily
166 :     push @$note, "Dbxref=\"SEED:$fid\"";
167 :    
168 :     # finally join all the notes into a long string that can be added as column 9
169 :     my $allnotes;
170 :     $allnotes = join ";", @$note;
171 :    
172 :     # do we want to convert '%20' to ' '
173 :     unless ($escapespace)
174 :     {
175 :     $allnotes =~ s/\%20/ /g;
176 :     }
177 :    
178 :     ###########
179 :     #
180 :     # End figuring out the column 9 information about notes and aliases and GO terms
181 :     #
182 :     ###########
183 :    
184 :     #
185 :     # Cache contig lengths.
186 :     #
187 :     my $len = $self->{contig_length_cache};
188 :    
189 :     my $genome = $fig->genome_of($fid);
190 :    
191 :     foreach my $loc (@location)
192 :     {
193 :     $loc =~ /^(.*)\_(\d+)\_(\d+)$/;
194 :     my ($contig, $start, $stop) = ($1, $2, $3);
195 :     my $original_contig=$contig;
196 :    
197 :     #
198 :     # the contig name must be escaped
199 :     #
200 :     $contig = uri_escape($contig);
201 :    
202 :     #my $contig_key = "$genome:$contig";
203 :     my $contig_key = $contig;
204 :    
205 :     unless (defined $len->{$contig})
206 :     {
207 :     $len->{$contig}=$fig->contig_ln($genome, $original_contig);
208 :     }
209 :     my $strand='+';
210 :    
211 :     #
212 :     # These were bounds-checking for dumping all of a genome.
213 :     #
214 :     #next if (defined $beg && ($start < $beg || $stop < $beg));
215 :     #next if (defined $end && ($start > $end || $stop > $end));
216 :    
217 :     if ($start > $stop)
218 :     {
219 :     ($start, $stop, $strand)=($stop, $start, '-');
220 :     }
221 :     elsif ($start == $stop)
222 :     {
223 :     $strand=".";
224 :     }
225 :    
226 :     my $type=$fig->ftype($fid);
227 :    
228 :     if ($type eq "peg")
229 :     {
230 :     # it is a protein coding gene
231 :     # create an artificial id that is just the fid.(\d+) information
232 :     # we will use this to create ids in the form cds.xxx; trn.xxxx; pro.xxx; gen.xxx;
233 :     $fid =~ /\.peg\.(\d+)/;
234 :     my $geneid=$1;
235 :    
236 :     ############## KLUDGE
237 :     #
238 :     # At the moment the outputs for transcript, gene, CDS, and pro are all the same.
239 :     # This is clearly a kludge and wrong, but it will work at the moment
240 :     #
241 :    
242 :     # defined some truncations
243 :     my %trunc=(
244 :     "transcript" => "trn",
245 :     "gene" => "gen",
246 :     "protein" => "pro",
247 :     "cds" => "cds",
248 :     );
249 :    
250 :     # SO terms:
251 :     # transcript: SO:0000673
252 :     # gene: SO:0000704
253 :     # cds: SO:0000316
254 :     # protein: NOT VALID: should be protein_coding_primary_transcript SO:0000120
255 :     foreach my $type (keys %outputtype)
256 :     {
257 :     my ($id, $type)=($trunc{$type} . "." . $geneid, $type);
258 :     # we want to store some sequences to be output
259 :     if ($outputfasta && $type eq "protein")
260 :     {
261 :     my $addseq = $fig->get_translation($fid);
262 :    
263 :     #
264 :     # the chomp is so that we know for sure to add the line back
265 :     #
266 :     $addseq =~ s/(.{$linelength})/$1\n/g;
267 :     chomp($addseq);
268 :     $fastasequences .= ">$id\n$addseq\n";
269 :     }
270 :     if ($outputfasta && $type eq "cds")
271 :     {
272 :     my $addseq = uc($fig->dna_seq($genome, $fig->feature_location($fid)));
273 :     $addseq =~ s/(.{$linelength})/$1\n/g; chomp($addseq);
274 :     $fastasequences .= ">$id\n$addseq\n";
275 :     }
276 :     # correct the incorrect sofa term
277 :     if ($type eq "protein")
278 :     {
279 :     $type = "SO:0000120";
280 :     }
281 :    
282 :     push (@{$contig_data->{$contig_key}},
283 :     (join "\t",
284 : olson 1.2 ($contig, "The SEED", $type, $start, $stop, ".", $strand, ".", "ID=$id;$allnotes")));
285 : olson 1.1 } # end the foreach my $type
286 :     } # end the if type==peg
287 :     elsif ($type eq "rna")
288 :     {
289 :     $fid =~ /\.rna\.(\d+)/;
290 :     my $geneid=$1;
291 :     #
292 :     # tRNA is a valid SOFA term == SO:0000253
293 :     #
294 :     my ($id, $type)=("rna.$geneid", "tRNA");
295 :     if ($outputfasta)
296 :     {
297 :     my $addseq = $fig->dna_seq($genome, $fig->feature_location($fid));
298 :     $addseq =~ s/(.{$linelength})/$1\n/g; chomp($addseq);
299 :     $fastasequences .= ">$id\n$addseq\n";
300 :     }
301 : olson 1.2 push (@{$contig_data->{$contig_key}}, (join "\t", ($contig, "The SEED", $type, $start, $stop, ".", $strand, ".", "ID=$id;$allnotes")));
302 : olson 1.1 } # end the if type == rna
303 :     else
304 :     {
305 :     die "Don't know what type: |$type| is";
306 :     }
307 :     }
308 :     return ($contig_data, $fastasequences);
309 :     }
310 :    
311 :     =head1 write_gff3
312 :    
313 :     Write a set of gff3 per-contig data and fasta sequence data to a file or filehandle.
314 :    
315 :     $genome is the genome these contigs are a part of.
316 :     $contig_list is a list of contig-data hashes as returned by gff_for_feature.
317 :     $fast_list is a list of fasta data strings.
318 :    
319 :     =cut
320 :    
321 :     sub write_gff3
322 :     {
323 :     my($self, $output, $genome, $contig_list, $fasta_list) = @_;
324 :    
325 :     my $fig = $self->{fig};
326 :    
327 :     my $len = $self->{contig_length_cache};
328 :     my $fh;
329 :    
330 :     my $beg = $self->{options}->{beg};
331 :     my $end = $self->{options}->{end};
332 :    
333 :     my $close_output;
334 :    
335 :     if (ref($output))
336 :     {
337 :     $fh = $output;
338 :     }
339 :     else
340 :     {
341 :     open($fh, ">$output") or confess "Cannot open output '$output': $!";
342 :     $close_output = 1;
343 :     }
344 :    
345 :     #
346 :     # Build a data structure from the list of contigs
347 :     # that has a list of lists of data per contig name.
348 :     # (Do this so we don't copy all of the contig data itself, as it
349 :     # could be quite large).
350 :     #
351 :     my %contigs;
352 :    
353 :     #
354 :     # iterate over the given list of contig hashes.
355 :     #
356 :     for my $chash (@$contig_list)
357 :     {
358 :     #
359 :     # Then for each contig in the individual contig hashes,
360 :     # add the data list to %contigs.
361 :     #
362 :     for my $contig (keys %$chash)
363 :     {
364 :     push(@{$contigs{$contig}}, $chash->{$contig});
365 :     }
366 :     }
367 :    
368 :     foreach my $contig (sort keys %contigs)
369 :     {
370 :     print $fh "##sequence-region\t$contig\t";
371 :     if (defined $beg) {
372 :     print $fh "$beg\t";
373 :     } else {
374 :     print $fh "1\t";
375 :     }
376 :     if (defined $end) {
377 :     print $fh "$end\n";
378 :     } else {
379 :     print $fh "$len->{$contig}\n";
380 :     }
381 :     for my $list (@{$contigs{$contig}})
382 :     {
383 : olson 1.2 print $fh join("\n", @$list), "\n";
384 : olson 1.1 }
385 :     }
386 :    
387 :     print $fh "##FASTA\n";
388 :     # print out the cds and pro if we need them
389 :    
390 :     if ($self->{options}->{outputfasta})
391 :     {
392 :     for my $fastasequences (@$fasta_list)
393 :     {
394 :     print $fh $fastasequences;
395 :     }
396 :     }
397 :    
398 :     my $ll = $self->{options}->{linelength};
399 :     foreach my $contig (sort keys %contigs)
400 :     {
401 :     my $len=$fig->contig_ln($genome, $contig);
402 :     my $dna_seq=$fig->dna_seq($genome, $contig . "_1_". $len);
403 :     if (defined $beg)
404 :     {
405 :     unless (defined $end) {
406 :     $end=$len;
407 :     }
408 :     $dna_seq = substr($dna_seq, $beg, $end);
409 :     }
410 :     elsif (defined $end)
411 :     {
412 :     $beg=1;
413 :     $dna_seq = substr($dna_seq, $beg, $end);
414 :     }
415 :    
416 :     my $contig=uri_escape($contig);
417 :    
418 :     $dna_seq =~ s/(.{$ll})/$1\n/g;
419 :     chomp($dna_seq); # just remove the last \n if there is one
420 :     print $fh ">$contig\n$dna_seq\n";
421 :     }
422 :    
423 :     close($fh) if $close_output;
424 :     }
425 :    
426 : olson 1.2 package GFFParser;
427 :    
428 :     use strict;
429 :     use URI::Escape;
430 :     use Carp;
431 :     use Data::Dumper;
432 :    
433 :     use base qw(Class::Accessor);
434 :    
435 :     __PACKAGE__->mk_accessors(qw(fig current_file));
436 :    
437 :     my $count;
438 :    
439 :    
440 :     #
441 :     # GFF file parser. Creates GFFFiles.
442 :     #
443 :    
444 :     sub new
445 :     {
446 :     my($class, $fig) = @_;
447 :    
448 :     my $self = {
449 :     fig => $fig,
450 :     };
451 :    
452 :     return bless($self, $class);
453 :     }
454 :    
455 :     sub parse
456 :     {
457 :     my($self, $file) = @_;
458 :    
459 :     my($fh, $close_handle);
460 :    
461 :     my $fobj = GFFFile->new($self->fig);
462 :     $self->current_file($fobj);
463 :    
464 :     if (ref($file) ? (ref($file) eq 'GLOB'
465 :     || UNIVERSAL::isa($file, 'GLOB')
466 :     || UNIVERSAL::isa($file, 'IO::Handle'))
467 :     : (ref(\$file) eq 'GLOB'))
468 :     {
469 :     $fh = $file;
470 :     }
471 :     else
472 :     {
473 :     open($fh, "<$file") or confess "Cannot open $file: $!";
474 :     $fobj->filename($file);
475 :     $close_handle = 1;
476 :     }
477 :    
478 :     #
479 :     # Start parsing by verifying this is a gff3 file.
480 :     #
481 :    
482 :     $_ = <$fh>;
483 :    
484 :     if (m,^\#gff-version\t(\S+),)
485 :     {
486 :     if ($1 != 3)
487 :     {
488 :     confess "Invalid GFF File: version is not 3";
489 :     }
490 :     }
491 :    
492 :     #
493 :     # Now parse.
494 :     #
495 :    
496 :     while (<$fh>)
497 :     {
498 :     chomp;
499 :     #
500 :     # Check first for the fasta directive so we can run off and parse that
501 :     # separately.
502 :     #
503 :    
504 :     if (/^>/)
505 :     {
506 :     $self->parse_fasta($fh, $_);
507 :     last;
508 :     }
509 :     elsif (/^\#\#FASTA/)
510 :     {
511 :     print "Got fasta directive\n";
512 :     $_ = <$fh>;
513 :     chomp;
514 :     $self->parse_fasta($fh, $_);
515 :     last;
516 :     }
517 :     elsif (/^\#\s/)
518 :     {
519 :     #
520 :     # comment.
521 :     #
522 :     next;
523 :     }
524 :     elsif (/^\#\#(\S+)(?:\t(.*))?/)
525 :     {
526 :     #
527 :     # GFF3 directive.
528 :     #
529 :    
530 :     $self->parse_gff3_directive($1, $2);
531 :    
532 :     }
533 :     elsif (/^\#(\S+)(?:\t(.*))?/)
534 :     {
535 :     #
536 :     # Directive.
537 :     #
538 :    
539 :     if ($1 eq "seed")
540 :     {
541 :     $self->parse_seed_directive($2);
542 :     }
543 :     else
544 :     {
545 :     $self->parse_local_directive($1, $2);
546 :     }
547 :    
548 :     }
549 :     elsif (/^([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)$/)
550 :     {
551 :     $self->parse_feature($1, $2, $3, $4, $5, $6, $7, $8, $9);
552 :     }
553 :     else
554 :     {
555 :     die "bad line: '$_'\n";
556 :     }
557 :     }
558 :    
559 :     return $fobj;
560 :     }
561 :    
562 :     sub parse_gff3_directive
563 :     {
564 :     my($self, $directive, $rest) = @_;
565 :    
566 :     print "Have gff3 directive '$directive' rest='$rest'\n";
567 :     }
568 :    
569 :     sub parse_seed_directive
570 :     {
571 :     my($self, $rest) = @_;
572 :    
573 :     my($verb, @rest) = split(/\t/, $rest);
574 :    
575 :     if ($verb eq "anno_start")
576 :     {
577 :     $self->current_file->anno_start($rest[0]);
578 :     }
579 :     elsif ($verb eq "anno_end")
580 :     {
581 :     $self->current_file->anno_start($rest[0]);
582 :     }
583 :     elsif ($verb eq "genome_md5")
584 :     {
585 :     $self->current_file->set_genome_checksum(@rest[0,1]);
586 :     }
587 :     elsif ($verb eq "contig_md5")
588 :     {
589 :     $self->current_file->set_contig_checksum(@rest[0,1,2]);
590 :     }
591 :     }
592 :    
593 :     sub parse_local_directive
594 :     {
595 :     my($self, $directive, $rest) = @_;
596 :    
597 :     print "Have local directive '$directive' rest='$rest'\n";
598 :     }
599 :    
600 :     sub parse_feature
601 :     {
602 :     my($self, $seqid, $source, $type, $start, $end, $score, $strand, $phase, $attributes) = @_;
603 :    
604 :     #print "data: seqid=$seqid source=$source type=$type start=$start end=$end\n";
605 :     #print " score=$score strand=$strand phase=$phase\n";
606 :     #print " $attributes\n";
607 :    
608 :     #
609 :     # Parse this feature, creating a GFFFeature object for it.
610 :     #
611 :    
612 :     my $feature = GFFFeature->new($self->fig);
613 :    
614 :     $feature->seqid($seqid);
615 :     $feature->source($source);
616 :     $feature->type($type);
617 :     $feature->start($start);
618 :     $feature->end($end);
619 :     $feature->score($score);
620 :     $feature->strand($strand);
621 :     $feature->phase($phase);
622 :    
623 :     my $athash = {};
624 :    
625 :     for my $attr (split(/;/, $attributes))
626 :     {
627 :     my($name, $value) = split(/=/, $attr);
628 :    
629 :     #
630 :     # Handle the GFF3-defined attributes
631 :     #
632 :    
633 :     my @values = split(/,/, $value);
634 :    
635 :     if (@values > 1)
636 :     {
637 :     my $vlist = [];
638 :     for my $value (@values)
639 :     {
640 :     $value = uri_unescape($value);
641 :     push(@$vlist, $value);
642 :     }
643 :     $value = $vlist;
644 :     }
645 :    
646 :     $athash->{$name} = $value;
647 :    
648 :     if ($GFFFeature::GFF_Tags{$name})
649 :     {
650 :     $feature->set($name, $value);
651 :     }
652 :    
653 :     }
654 :     $feature->attributes($athash);
655 :    
656 :     $self->current_file->add_feature($feature);
657 :     }
658 :    
659 :     #
660 :     # We come in here with the first line of the fasta already read
661 :     # in order to support the backward-compatiblity syntax that
662 :     # lets a file skip the ##FASTA directive if it wishes.
663 :     #
664 :     sub parse_fasta
665 :     {
666 :     my($self, $fh, $first_line) = @_;
667 :     my($cur, $cur_id);
668 :    
669 :     for ($_ = $first_line; $_; $_ = <$fh>, chomp)
670 :     {
671 :     if (/^>\s*(\S+)/)
672 :     {
673 :     if ($cur)
674 :     {
675 :     $self->handle_fasta_block($cur_id, $cur);
676 :     }
677 :    
678 :     $cur = '';
679 :     $cur_id = $1;
680 :     }
681 :     else
682 :     {
683 :     s/^\s*$//;
684 :     s/\s*$//;
685 :     if (/\s/)
686 :     {
687 :     die "FASTA data had embedded space: $_\n";
688 :     }
689 :     $cur .= $_;
690 :     }
691 :     }
692 :     if ($cur)
693 :     {
694 :     $self->handle_fasta_block($cur_id, $cur);
695 :     }
696 :     }
697 :    
698 :     sub handle_fasta_block
699 :     {
700 :     my($self, $id, $data) = @_;
701 :    
702 :     my $len = length($data);
703 :     $self->current_file->set_fasta_data($id, $data);
704 :     }
705 :    
706 :     package GFFFeature;
707 :    
708 :     use strict;
709 :     use base qw(Class::Accessor);
710 :    
711 :     our @GFF_Tags = qw(ID Name Alias Parent Target Gap Note Dbxref Ontology_term);
712 :     our %GFF_Tags;
713 :    
714 :     map { $GFF_Tags{$_} = 1 } @GFF_Tags;
715 :    
716 :     __PACKAGE__->mk_accessors(qw(fig seqid source type start end score strand phase attributes),
717 :     @GFF_Tags);
718 :    
719 :    
720 :     sub new
721 :     {
722 :     my($class, $fig) = @_;
723 :    
724 :     my $self = {
725 :     fig => $fig,
726 :     };
727 :    
728 :     return bless($self, $class);
729 :     }
730 :    
731 :     package GFFFile;
732 :    
733 :     use strict;
734 :     use base qw(Class::Accessor);
735 :    
736 :     __PACKAGE__->mk_accessors(qw(fig filename features feature_index anno_start anno_end));
737 :    
738 :     #
739 :     # Package to hold the contents of a GFF file, and to hold the code
740 :     # for mapping its contents to the local SEED.
741 :     #
742 :     # Created by GFFParser->parse.
743 :     #
744 :    
745 :     sub new
746 :     {
747 :     my($class, $fig) = @_;
748 :    
749 :     my $self = {
750 :     fig => $fig,
751 :     features => [],
752 :     feature_index => {},
753 :     };
754 :     return bless($self, $class);
755 :     }
756 :    
757 :     sub add_feature
758 :     {
759 :     my($self, $feature) = @_;
760 :    
761 :     push(@{$self->features}, $feature);
762 :     $self->feature_index->{$feature->ID} = $feature;
763 :     }
764 :    
765 :     sub set_genome_checksum
766 :     {
767 :     my($self, $genome, $md5sum) = @_;
768 :     $self->{genome_checksum}->{$genome} = $md5sum;
769 :     }
770 :    
771 :     sub set_contig_checksum
772 :     {
773 :     my($self, $genome, $contig, $md5sum) = @_;
774 :     $self->{contig_checksum}->{$genome}->{$contig} = $md5sum;
775 :     }
776 :    
777 :     sub set_fasta_data
778 :     {
779 :     my($self, $id, $data) = @_;
780 :    
781 :     $self->{fasta_data}->{$id} = $data;
782 :     }
783 :    
784 :    
785 : olson 1.1 1;

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3