~ubuntu-branches/ubuntu/lucid/bioperl/lucid

« back to all changes in this revision

Viewing changes to Bio/SeqIO/game/seqHandler.pm

  • Committer: Bazaar Package Importer
  • Author(s): Matt Hope
  • Date: 2004-04-18 14:24:11 UTC
  • mfrom: (1.2.1 upstream) (2.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20040418142411-gr92uexquw4w8liq
Tags: 1.4-1
* New upstream release
* Examples and working code are installed by default to usr/bin,
  this has been moved to usr/share/doc/bioperl/bin

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# $Id: seqHandler.pm,v 1.13 2001/10/22 08:22:53 heikki Exp $
 
1
# $Id: seqHandler.pm,v 1.18 2003/12/16 16:58:51 smckay Exp $
2
2
#
3
3
# BioPerl module for Bio::SeqIO::game::seqHandler
4
4
#
5
 
# Cared for by Brad Marshall <bradmars@yahoo.com>
6
 
#         
7
 
# Copyright Brad Marshall
 
5
# Cared for by Sheldon McKay <smckay@bcgsc.bc.ca>
 
6
#
 
7
# Copyright Sheldon McKay
8
8
#
9
9
# You may distribute this module under the same terms as perl itself
10
 
# _history
11
 
# June 25, 2000     written by Brad Marshall
12
10
#
 
11
 
13
12
# POD documentation - main docs before the code
14
13
 
15
14
=head1 NAME
16
15
 
17
 
Bio::SeqIO::game::seqHandler - GAME helper via PerlSAX helper.
 
16
Bio::SeqIO::game::seqHandler -- a class for handling game-XML sequences
18
17
 
19
18
=head1 SYNOPSIS
20
19
 
21
 
GAME helper for parsing new Sequence objects from GAME XML. Do not use directly
 
20
This modules is not used directly
 
21
 
 
22
=head1 DESCRIPTION
 
23
 
 
24
Bio::SeqIO::game::seqHandler processes all of the sequences associated with a game record
 
25
and, via feature handlers, processes the associated annotations
22
26
 
23
27
=head1 FEEDBACK
24
28
 
25
29
=head2 Mailing Lists
26
30
 
27
 
User feedback is an integral part of the evolution of this and 
28
 
other Bioperl modules. Send your comments and suggestions preferably 
29
 
to one of the Bioperl mailing lists.  Your participation is much appreciated.
30
 
 
31
 
  bioperl-l@bioperl.org        - Bioperl list
32
 
  bioxml-dev@bioxml.org        - Technical discussion - Moderate volume
33
 
  bioxml-announce@bioxml.org   - General Announcements - Pretty dead
34
 
  http://www.bioxml.org/MailingLists/         - About the mailing lists
35
 
 
36
 
=head1 AUTHOR - Brad Marshall
37
 
 
38
 
Email: bradmars@yahoo.com
 
31
User feedback is an integral part of the evolution of this
 
32
and other Bioperl modules. Send your comments and suggestions preferably
 
33
to one of the Bioperl mailing lists.
 
34
 
 
35
Your participation is much appreciated.
 
36
 
 
37
  bioperl-l@bioperl.org                  - General discussion
 
38
  http://bioperl.org/MailList.shtml      - About the mailing lists
 
39
 
 
40
=head2 Reporting Bugs
 
41
 
 
42
Report bugs to the Bioperl bug tracking system to help us keep track
 
43
of the bugs and their resolution.
 
44
 
 
45
Bug reports can be submitted via email or the web:
 
46
 
 
47
  bioperl-bugs@bioperl.org
 
48
  http://bugzilla.bioperl.org/
 
49
 
 
50
=head1 AUTHOR - Sheldon McKay
 
51
 
 
52
Email smckay@bcgsc.bc.ca
39
53
 
40
54
=head1 APPENDIX
41
55
 
44
58
 
45
59
=cut
46
60
 
47
 
# This template file is in the Public Domain.
48
 
# You may do anything you want with this file.
49
 
#
50
 
 
51
61
package Bio::SeqIO::game::seqHandler;
52
 
use vars qw{ $AUTOLOAD @ISA };
53
 
 
54
 
use XML::Handler::Subs;
55
 
use Bio::PrimarySeq;
56
 
 
57
 
@ISA = qw(XML::Handler::Subs);
 
62
 
 
63
use Bio::SeqIO::game::featHandler;
 
64
use Bio::SeqIO::game::gameSubs;
 
65
use Bio::SeqFeature::Generic;
 
66
use Bio::Seq::RichSeq;
 
67
use Bio::Species;
 
68
use strict;
 
69
 
 
70
use vars qw { @ISA };
 
71
 
 
72
@ISA = qw{ Bio::SeqIO::game::gameSubs };
 
73
 
 
74
=head2 new
 
75
 
 
76
 Title   : new
 
77
 Usage   : my $seqHandler = Bio::SeqIO::game::seqHandler->new($seq, $ann, $comp, $map, $src )
 
78
 Function: constructor method to create a sequence handler
 
79
 Returns : a sequence handler object
 
80
 Args    : $seq  -- an XML sequence element
 
81
           $ann  -- a ref. to a list of <annotation> elements
 
82
           $comp -- a ref. to a list of <computational_analysis> elements (not used yet)
 
83
           $map  -- a <map_position> element
 
84
           $src  -- a flag to indicate that the sequence already has a source feature
 
85
 
 
86
=cut
58
87
 
59
88
sub new {
60
 
    my ($caller,$seq) = @_;
 
89
    my ($caller, $seq, $ann, $comp, $map, $src ) =  @_;
 
90
 
61
91
    my $class = ref($caller) || $caller;
62
 
    my $self = bless ( {
63
 
        string => '',
64
 
        seq  => $seq,
65
 
    }, $class);
 
92
 
 
93
    my $self = bless ( { 
 
94
        seqs     => $seq,
 
95
        anns     => $ann,
 
96
        comps    => $comp,
 
97
        map_pos  => $map,
 
98
        has_source => $src,
 
99
        seq_h    => {},
 
100
        ann_l    => []
 
101
    }, $class );
 
102
 
66
103
    return $self;
67
104
}
68
105
 
69
 
=head2 start_document
70
 
 
71
 
 Title   : start_document
72
 
 Usage   : $obj->start_document
73
 
 Function: PerlSAX method called when a new document is initialized
74
 
 Returns : nothing
75
 
 Args    : document name
76
 
 
77
 
=cut
78
 
 
79
 
# Basic PerlSAX
80
 
sub start_document            {
81
 
    my ($self, $document) = @_;
82
 
    $self->{'in_current_seq'} = 'false';    
83
 
    $self->{'Names'} = [];
84
 
    $self->{'string'} = '';
85
 
}
86
 
 
87
 
=head2 end_document
88
 
 
89
 
 Title   : end_document
90
 
 Usage   : $obj->end_document
91
 
 Function: PerlSAX method called when a document is finished for cleaning up
92
 
 Returns : list of sequences seen
93
 
 Args    : document name
94
 
 
95
 
=cut
96
 
 
97
 
sub end_document              {
98
 
    my ($self, $document) = @_;
99
 
    delete $self->{'Names'};
100
 
    return new Bio::PrimarySeq( -seq => $self->{'residues'},
101
 
                                -alphabet => $self->{'alphabet'},
102
 
                                -id => $self->{'seq'},
103
 
                                -accession => $self->{'accession'},
104
 
                                -desc => $self->{'desc'},
105
 
                                -length => $self->{'length'},
106
 
                                );
107
 
 
108
 
}
109
 
 
110
 
 
111
 
=head2 start_element
112
 
 
113
 
 Title   : start_element
114
 
 Usage   : $obj->start_element
115
 
 Function: PerlSAX method called when a new element is reached
116
 
 Returns : nothing
117
 
 Args    : element object
118
 
 
119
 
=cut
120
 
 
121
 
sub start_element             {
122
 
    my ($self, $element) = @_;
123
 
 
124
 
    push @{$self->{'Names'}}, $element->{'Name'};
125
 
    $self->{'string'} = '';
126
 
 
127
 
    if ($element->{'Name'} eq 'bx-seq:seq') {
128
 
        if ($element->{'Attributes'}->{'bx-seq:id'} eq $self->{'seq'}) {
129
 
            $self->{'in_current_seq'} = 'true';
130
 
            $self->{'alphabet'} = $element->{'Attributes'}->{'bx-seq:type'};
131
 
            $self->{'length'} =  $element->{'Attributes'}->{'bx-seq:length'};
132
 
        } else {
133
 
            #This is not the sequence we want to import, but that's ok
134
 
        }
135
 
    }
136
 
    return 0;
137
 
}
138
 
 
139
 
=head2 end_element
140
 
 
141
 
 Title   : end_element
142
 
 Usage   : $obj->end_element
143
 
 Function: PerlSAX method called when an element is finished
144
 
 Returns : nothing
145
 
 Args    : element object
146
 
 
147
 
=cut
148
 
 
149
 
sub end_element               {
150
 
    my ($self, $element) = @_;
151
 
 
152
 
    if ($self->{'in_current_seq'} eq 'true') {      
153
 
        if ($self->in_element('bx-seq:residues')) {
154
 
            while ($self->{'string'} =~ s/\s+//) {};
155
 
            $self->{'residues'} = $self->{'string'};
156
 
        }
157
 
 
158
 
 
159
 
        if ($self->in_element('bx-seq:name')) {
160
 
            $self->{'string'} =~ s/^\s+//g;
161
 
            $self->{'string'} =~ s/\s+$//;
162
 
            $self->{'string'} =~ s/\n//g;
163
 
            $self->{'name'} = $self->{'string'};
164
 
        }
165
 
 
166
 
 
167
 
        if ($self->in_element('bx-link:id')  && $self->within_element('bx-link:dbxref')) {
168
 
            $self->{'string'} =~ s/^\s+//g;
169
 
            $self->{'string'} =~ s/\s+$//;
170
 
            $self->{'string'} =~ s/\n//g;
171
 
            $self->{'accession'} = $self->{'string'};
172
 
        }
173
 
 
174
 
        if ($self->in_element('bx-seq:description')) {
175
 
            $self->{'desc'} = $self->{'string'};
176
 
        }
177
 
 
178
 
        if ($self->in_element('bx-seq:seq')) {
179
 
            $self->{'in_current_seq'} = 'false';
180
 
        }
181
 
    }
182
 
 
183
 
    pop @{$self->{'Names'}};
184
 
 
185
 
}
186
 
 
187
 
=head2 characters
188
 
 
189
 
 Title   : characters
190
 
 Usage   : $obj->end_element
191
 
 Function: PerlSAX method called when text between XML tags is reached
192
 
 Returns : nothing
193
 
 Args    : text
194
 
 
195
 
=cut
196
 
 
197
 
sub characters   {
198
 
    my ($self, $text) = @_;
199
 
    $self->{'string'} .= $text->{'Data'};
200
 
}
201
 
 
202
 
=head2 in_element
203
 
 
204
 
 Title   : in_element
205
 
 Usage   : $obj->in_element
206
 
 Function: PerlSAX method called to test if state is in a specific element
207
 
 Returns : boolean
208
 
 Args    : name of element
209
 
 
210
 
=cut
211
 
 
212
 
sub in_element {
213
 
    my ($self, $name) = @_;
214
 
 
215
 
    return ($self->{'Names'}[-1] eq $name);
216
 
}
217
 
 
218
 
=head2 within_element
219
 
 
220
 
 Title   : within_element
221
 
 Usage   : $obj->within_element
222
 
 Function: PerlSAX method called to list depth within specific element
223
 
 Returns : boolean
224
 
 Args    : name of element
225
 
 
226
 
=cut
227
 
 
228
 
sub within_element {
229
 
    my ($self, $name) = @_;
230
 
 
231
 
    my $count = 0;
232
 
    foreach my $el_name (@{$self->{'Names'}}) {
233
 
        $count ++ if ($el_name eq $name);
234
 
    }
235
 
 
236
 
    return $count;
237
 
}
238
 
 
239
 
=head2 AUTOLOAD
240
 
 
241
 
 Title   : AUTOLOAD
242
 
 Usage   : do not use directly
243
 
 Function: autoload handling of missing DESTROY method
244
 
 Returns : nothing
245
 
 Args    : text
246
 
 
247
 
=cut
248
 
 
249
 
# Others
250
 
sub AUTOLOAD {
251
 
    my $self = shift;
252
 
 
253
 
    my $method = $AUTOLOAD;
254
 
    $method =~ s/.*:://;
255
 
    return if $method eq 'DESTROY';
256
 
 
257
 
    print "UNRECOGNIZED $method\n";
 
106
=head2 convert
 
107
 
 
108
 Title   : convert
 
109
 Usage   : @seqs = $seqHandler->convert
 
110
 Function: converts the main XML sequence element and associated annotations to Bio::
 
111
 Returns : a ref. to a an array containing the sequence object and a ref. to a list of  features
 
112
 Args    : none
 
113
 
 
114
 Note    : The features and sequence are kept apart to facilitate downstream filtering of features 
 
115
 
 
116
=cut
 
117
 
 
118
sub convert {
 
119
    my $self = shift;
 
120
    my @ann  = @{$self->{anns}};
 
121
    my @seq  = @{$self->{seqs}};
 
122
    
 
123
    # not used yet
 
124
    my @comp;
 
125
    if ( $self->{comps} ) {
 
126
        @comp = @{$self->{comps}}    
 
127
    }
 
128
    
 
129
    # process the sequence elements
 
130
    for ( @seq ) {
 
131
        $self->_add_seq( $_ );
 
132
    }
 
133
    
 
134
    # process the annotation elements
 
135
    for ( @ann ) {
 
136
        $self->_annotation( $_ );
 
137
    }
 
138
    
 
139
    return $self->_order_feats( $self->{seq_h} );
 
140
}
 
141
 
 
142
=head2 _order_feats
 
143
 
 
144
 Title   : _order_feats
 
145
 Usage   : $self->_order_feats( $self->{seq_h} )
 
146
 Function: an internal method to ensure the source feature comes first 
 
147
 Returns : a ref. to a an array containing the sequence object and a ref. to a list of  features 
 
148
 Args    : a ref. to a hash of sequences
 
149
 
 
150
=cut
 
151
 
 
152
sub _order_feats {
 
153
    my ($self, $seqs) = @_;
 
154
    my $seq = $self->{main_seq};
 
155
    my $id  = $seq->id;
 
156
    my $ann = $self->{ann_l};
 
157
 
 
158
    # make sure source comes first
 
159
    my @src = grep { $_->primary_tag =~ /source|origin|\bregion\b/ } @$ann;
 
160
    my @other = grep { $_->primary_tag !~ /source|origin|\bregion\b/ } @$ann;
 
161
 
 
162
    return [$seq, [@src, @other]];
 
163
}
 
164
 
 
165
=head2 _add_seq
 
166
 
 
167
 Title   : _add_seq
 
168
 Usage   : $self->_add_seq($seq_element)
 
169
 Function: an internal method to process the sequence elements
 
170
 Returns : nothing
 
171
 Args    : a sequence element
 
172
 
 
173
=cut
 
174
 
 
175
sub _add_seq {
 
176
    my ($self, $el) = @_;
 
177
    my $residues = '';
 
178
 
 
179
    if ($el->{_residues}) {
 
180
        $residues = $el->{_residues}->{Characters};
 
181
        $residues =~ s/[ \n\r]//g;
 
182
        $residues =~ s/\!//g;
 
183
        $residues =~ tr/a-z/A-Z/;
 
184
    } 
 
185
    else {
 
186
        return 0;
 
187
    }
 
188
 
 
189
    my $id   = $el->{Attributes}->{id};
 
190
    my $ver  = $el->{Attributes}->{version};
 
191
    my $name = $el->{_name}->{Characters};
 
192
    
 
193
    if ($name && $name ne $id) {
 
194
        $self->complain("The sequence name and unique ID do not match.  Using ID");
 
195
    }
 
196
    
 
197
    # get/set the sequence object
 
198
    my $seq = $self->_seq($id);
 
199
    
 
200
    # get/set the feature handler
 
201
    my $featHandler = $self->_feat_handler;
 
202
    
 
203
    # populate the sequence object
 
204
    $seq->seq($residues);
 
205
    $seq->seq_version($ver) if $ver;
 
206
    
 
207
    # assume the id is the accession number
 
208
    if ( $id =~ /^\w+$/ ) {
 
209
        $seq->accession($id);
 
210
    }
 
211
    
 
212
    # If the focus attribute is set to "true", this is the main
 
213
    # sequence
 
214
    my $focus = 0;
 
215
    if ( defined $el->{Attributes}->{focus} ) {
 
216
        $self->{main_seq} = $seq;
 
217
        $focus++;
 
218
    }
 
219
 
 
220
    # make sure real and annotated lengths match
 
221
    my $length = $el->{Attributes}->{'length'};
 
222
    $length && $seq->length(int($length));
 
223
    if ( $seq->seq && defined($length) && $seq->length != int($length) ) {
 
224
        $self->complain("The specified sequence has length ", $seq->length(),
 
225
                        " but the length attribute= ", $length);
 
226
        $seq->seq( undef );
 
227
        $seq->length( int($length) );
 
228
    }
 
229
 
 
230
    # deal with top-level annotations
 
231
    my $tags = {};
 
232
    if ( $el->{Attributes}->{md5checksum} ) {
 
233
        $tags->{md5checksum} = [$el->{Attributes}->{md5checksum}];
 
234
    }
 
235
    if ($el->{_dbxref}) {
 
236
        $tags->{dbxref} ||= [];
 
237
        push @{$tags->{dbxref}}, $self->dbxref( $el->{_dbxref} );
 
238
    }
 
239
    if ($el->{_description}) {
 
240
        my $desc = $el->{_description}->{Characters};
 
241
        $seq->description( $desc );
 
242
    } 
 
243
    if ($el->{_organism}) {
 
244
        my @organism = split /\s+/, $el->{_organism}->{Characters};
 
245
        if (@organism < 2) {
 
246
            $self->complain("Species name should have at least two words");
 
247
        }
 
248
        else {
 
249
            my $species = Bio::Species->new( -classification => [reverse @organism] );
 
250
            $seq->species($species);
 
251
        }
 
252
    }
 
253
    if ( defined($seq->species) ) {
 
254
        $tags->{organism} = [$seq->species->binomial];
 
255
    }
 
256
    elsif ($seq eq $self->{main_seq}) {
 
257
        $self->warn("The source organism for this sequence was\n" .
 
258
                    "not specified.  I will guess Drosophila melanogaster.\n" .
 
259
                    "Otherwise, add <organism>Genus species</organism>\n" .
 
260
                    "to the main sequence element");
 
261
        my @class = qw/ Eukaryota Metazoa Arthropoda Insecta Pterygota
 
262
                        Neoptera Endopterygota Diptera Brachycera 
 
263
                        Muscomorpha Ephydroidea Drosophilidae Drosophila melanogaster/;
 
264
        my $species = Bio::Species->new( -classification => [ reverse @class ],
 
265
                                         -common_name    => 'fruit fly' );
 
266
        $seq->species( $species );
 
267
    }
 
268
    
 
269
    # convert GAME to bioperl molecule types
 
270
    my $alphabet = $el->{Attributes}->{type};
 
271
    if ( $alphabet ) {
 
272
        $alphabet =~ s/aa/protein/;
 
273
        $alphabet =~ s/cdna/rna/;
 
274
        $seq->alphabet($alphabet);
 
275
    }
 
276
 
 
277
    # add a source feature if req'd
 
278
    if ( !$self->{has_source} && $focus ) {
 
279
        $self->{source} = $featHandler->add_source($seq->length, $tags);
 
280
    }
 
281
    
 
282
    if ( $focus ) {
 
283
        # add the map position
 
284
        $self->_map_position( $self->{map_pos}, $seq );
 
285
        $featHandler->{offset} = $self->{offset};
 
286
    }
 
287
    
 
288
    # prune the sequence from the parse tree
 
289
    $self->flush;
 
290
}
 
291
 
 
292
=head2 _map_position
 
293
 
 
294
 Title   : _map_position
 
295
 Usage   : $self->_map_position($map_posn_element)
 
296
 Function: an internal method to process the <map_position> element
 
297
 Returns : nothing
 
298
 Args    : a map_position element
 
299
 
 
300
=cut
 
301
 
 
302
sub _map_position {
 
303
    my ($self, $el) = @_;
 
304
 
 
305
    # chromosome and coordinates
 
306
    my $arm   = $el->{_arm}->{Characters};
 
307
    my $type  = $el->{Attributes}->{type};
 
308
    my $loc   = $el->{_span};
 
309
    my $start = $loc->{_start}->{Characters};
 
310
    my $end   = $loc->{_end}->{Characters};
 
311
    
 
312
    # define the offset (may be a partial sequence)
 
313
    # The coordinates will be relative but the CDS description
 
314
    # coordinates may be absolute if the game-XML comes from apollo 
 
315
    # or gadfly
 
316
    $self->{offset} = $start - 1;
 
317
 
 
318
    my $seq_id = $el->{Attributes}->{seq};
 
319
    my $seq = $self->{seq_h}->{$seq_id};
 
320
    
 
321
    unless ( $seq ) {
 
322
        $self->throw("Map position with no corresponding sequence object");
 
323
    }
 
324
    unless ($seq eq $self->{main_seq}){
 
325
        $self->throw("Map position does not correspond to the main sequence");
 
326
    }
 
327
    
 
328
    my $species = '';
 
329
    
 
330
    # create/update the top-level sequence feature if req'd
 
331
    if ( $self->{source} ) {
 
332
        my $feat = $self->{source};
 
333
    
 
334
        unless ($feat->has_tag('organism')) {
 
335
            $species = eval {$seq->species->binomial} || 'unknown species';
 
336
            $feat->add_tag_value( organism => $species );
 
337
        }
 
338
    
 
339
        my %tags = ( mol_type   => "genomic dna",
 
340
                     chromosome => $arm,
 
341
                     location   => "$start..$end",
 
342
                     type       => $type
 
343
                     );
 
344
    
 
345
        for (keys %tags) {
 
346
            $feat->add_tag_value( $_ => $tags{$_} );
 
347
        }
 
348
        
 
349
        $seq->add_SeqFeature($feat);
 
350
    }
 
351
 
 
352
    # come up with a description if there is none
 
353
    my $desc = $seq->description;
 
354
    if ( $species && $arm && $start && $end && !$desc) {
 
355
        $seq->description("$species chromosome $arm $start..$end " .
 
356
                          "segment of complete sequence");
 
357
    }
 
358
    
 
359
    $self->flush;
 
360
}
 
361
 
 
362
=head2 _annotation
 
363
 
 
364
 Title   : _annotation
 
365
 Usage   : $self->_annotation($annotation_element)
 
366
 Function: an internal method to process <annotation> elements
 
367
 Returns : nothing
 
368
 Args    : an annotation element
 
369
 
 
370
=cut
 
371
 
 
372
sub _annotation {
 
373
    my ($self, $el) = @_;
 
374
 
 
375
    my $id      = $el->{Attributes}->{id};
 
376
    my $type    = $el->{_type}->{Characters};
 
377
    my $tags    = {};
 
378
    my $gname   = $el->{_name}->{Characters} eq $id ? '' : $el->{_name}->{Characters};
 
379
 
 
380
    # 'transposable element' is too long (breaks Bio::SeqIO::GenBank)
 
381
    $type =~ s/transposable_element/repeat_region/;
 
382
    
 
383
    # annotations must be on the main sequence
 
384
    my $seqid = $self->{main_seq}->id;
 
385
    my $featHandler = $self->_feat_handler;
 
386
    
 
387
    my @feats = ();
 
388
    
 
389
    for my $child ( @{$el->{Children}} ) {
 
390
        my $name = $child->{Name};
 
391
        
 
392
        # these elements require special handling
 
393
        if ( $name eq 'dbxref' ) {
 
394
            $tags->{dbxref} ||= [];
 
395
            push @{$tags->{dbxref}}, $self->dbxref( $child );
 
396
        }
 
397
        elsif ( $name eq 'aspect' ) {
 
398
            $tags->{dbxref} ||= [];
 
399
            push @{$tags->{dbxref}}, $self->dbxref( $child->{_dbxref} );
 
400
        }
 
401
        elsif ( $name eq 'feature_set' ) {
 
402
            push @feats, $featHandler->feature_set( $id, $gname, $child, $type );
 
403
        }
 
404
        elsif ( $name eq 'comment' ) {
 
405
            $tags->{comment} = [$self->comment( $child )];
 
406
        }
 
407
        elsif ( $name eq 'property' ) {
 
408
            $self->property( $child, $tags );
 
409
        }
 
410
        elsif ( $name eq 'gene' ) {
 
411
            # we may be dealing with an annotation that is not
 
412
            # a gene, so we have to nest the gene inside it
 
413
            $featHandler->has_gene( $child, $gname, $id )
 
414
        }
 
415
        
 
416
        # otherwise, tag/value pairs
 
417
        # -- mild dtd enforcement
 
418
        # synonym is not in the dtd but shows up in gadfly
 
419
        # annotations   
 
420
        elsif ( $name =~ /type|synonym/ ) {
 
421
            $tags->{$name} = [$child->{Characters}];
 
422
        }
 
423
        elsif ( $name ne 'name' ) {
 
424
            $self->complain("Unrecognized element '$name'. I don't " .
 
425
                            "know what to do with $name elements in " .
 
426
                            "top-level sequence annotations." );
 
427
        }
 
428
 
 
429
    }
 
430
        
 
431
    if ( $tags->{symbol} ) {
 
432
        if ( !$tags->{gene} ) {
 
433
           $tags->{gene} = $tags->{symbol};
 
434
        }
 
435
        delete $tags->{symbol};
 
436
    }
 
437
    
 
438
    
 
439
    $featHandler->add_annotation( $self->{main_seq}, $type, $id, $tags, \@feats );
 
440
    $self->flush;
 
441
}
 
442
 
 
443
# get/set the sequence object
 
444
=head2 _seq
 
445
 
 
446
 Title   : _seq
 
447
 Usage   : my $seq = $self->_seq
 
448
 Function: an internal sequence getter/setter
 
449
 Returns : a Bio::RichSeq object
 
450
 Args    : a sequence ID
 
451
 
 
452
=cut
 
453
 
 
454
sub _seq {
 
455
    my ($self, $id) = @_;
 
456
    $id || $self->throw("A unique id must be provided for the sequence");
 
457
    
 
458
    my $seq = {};
 
459
    
 
460
    if ( defined $self->{seq_h}->{$id}) {
 
461
        $seq = $self->{seq_h}->{$id};
 
462
    } else {
 
463
        $seq = Bio::Seq::RichSeq->new( -id => $id );
 
464
        $self->{seq_h}->{$id} = $seq; # store it
 
465
    }
 
466
    
 
467
    return $seq;
 
468
}
 
469
 
 
470
#get/set the feature handler
 
471
=head2 _feat_handler
 
472
 
 
473
 Title   : _feat_handler
 
474
 Usage   : my $featHandler = $self->_featHandler
 
475
 Function: an internal getter/setter for feature handling objects 
 
476
 Returns : a Bio::SeqIO::game::featHandler object
 
477
 Args    : none
 
478
 
 
479
=cut
 
480
 
 
481
sub _feat_handler {
 
482
    my $self = shift;
 
483
    
 
484
    my $handler = {};
 
485
    my $seq = $self->{main_seq};
 
486
    
 
487
    if ( defined $self->{feat_handler} ) {
 
488
        $handler = $self->{feat_handler};
 
489
    }
 
490
    else {
 
491
        my @args = ( $seq, $self->{seq_h}, $self->{ann_l} );
 
492
        $handler = Bio::SeqIO::game::featHandler->new( @args );
 
493
        $self->{feat_handler} = $handler;
 
494
    }
 
495
 
 
496
    return $handler;
258
497
}
259
498
 
260
499
1;
261
500
 
262
 
__END__
 
501