~ubuntu-branches/ubuntu/trusty/bioperl/trusty-proposed

« back to all changes in this revision

Viewing changes to Bio/AlignIO/stockholm.pm

  • Committer: Bazaar Package Importer
  • Author(s): Charles Plessy
  • Date: 2009-03-10 07:19:11 UTC
  • mfrom: (1.2.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20090310071911-fukqzw54pyb1f0bd
Tags: 1.6.0-2
* Removed patch system (not used):
  - removed instuctions in debian/rules;
  - removed quilt from Build-Depends in debian/control.
* Re-enabled tests:
  - uncommented test command in debian/rules;
  - uncommented previously missing build-dependencies in debian/control.
  - Re-enabled tests and uncommented build-dependencies accordingly.
* Removed libmodule-build-perl and libtest-harness-perl from
  Build-Depends-Indep (provided by perl-modules).
* Better cleaning of empty directories using find -type d -empty -delete
  instead of rmdir in debian/rules (LP: #324001).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# $Id: stockholm.pm,v 1.15.4.6 2006/11/14 15:07:01 cjfields Exp $
 
1
# $Id: stockholm.pm 15093 2008-12-05 02:46:40Z cjfields $
2
2
#
3
3
# BioPerl module for Bio::AlignIO::stockholm
4
 
 
 
4
#
5
5
#   Based on the Bio::SeqIO::stockholm module
6
6
#       by Ewan Birney <birney@ebi.ac.uk>
7
7
#       and Lincoln Stein  <lstein@cshl.org>
47
47
  GF Lines (alignment feature/annotation):
48
48
  #=GF <featurename> <Generic per-file annotation, free text>
49
49
  Placed above the alignment
50
 
  
 
50
 
51
51
  GC Lines (Alignment consensus)
52
52
  #=GC <featurename> <Generic per-column annotation, exactly 1
53
53
       character per column>
76
76
     ID        id  
77
77
     DE        description
78
78
    ----------------------------------------------------------------------
79
 
    
 
79
 
80
80
    Tag        Bio::Annotation   TagName                    Parameters
81
81
               Class
82
82
    ----------------------------------------------------------------------
90
90
     PI        SimpleValue       previous_ids               value
91
91
     DC        Comment           database_comment           comment
92
92
     CC        Comment           alignment_comment          comment
93
 
     DR        DBLink            aln_dblink                 database
 
93
     DR        Target            dblink                     database
94
94
                                                            primary_id
95
95
                                                            comment
96
96
     AM        SimpleValue       build_method               value
139
139
use strict;
140
140
 
141
141
use Bio::Seq::Meta;
142
 
use Bio::Annotation::AnnotationFactory;
 
142
use Bio::AlignIO::Handler::GenericAlignHandler;
143
143
use Data::Dumper;
144
144
use Text::Wrap qw(wrap);
145
145
 
153
153
# and the tagname for, well, the Annotation tagname.  A few are treated differently
154
154
# based on the type of data stored (Reference data in particular).
155
155
 
156
 
our %READMAP = (
157
 
            'AC'   => 'Method/accession', 
158
 
            'ID'   => 'Method/id', 
159
 
            'DE'   => 'Method/description',
160
 
            'AU'   => 'SimpleValue/-value/record_authors',
161
 
            'SE'   => 'SimpleValue/-value/seed_source', 
162
 
            'GA'   => 'SimpleValue/-value/gathering_threshold',
163
 
            'NC'   => 'SimpleValue/-value/noise_cutoff', 
164
 
            'TC'   => 'SimpleValue/-value/trusted_cutoff', 
165
 
            'TP'   => 'SimpleValue/-value/entry_type', 
166
 
            'SQ'   => 'SimpleValue/-value/num_sequences', 
167
 
            'PI'   => 'SimpleValue/-value/previous_ids', 
168
 
            'DC'   => 'Comment/-text/database_comment',
169
 
            'CC'   => 'Comment/-text/alignment_comment',
170
 
            # DBLink, treated differently
171
 
            'DR'   => 'DBLink/-value/aln_dblink',
172
 
            # Pfam-specific
173
 
            'AM'   => 'SimpleValue/-value/build_method', 
174
 
            'NE'   => 'SimpleValue/-value/pfam_family_accession',
175
 
            'NL'   => 'SimpleValue/-value/sequence_start_stop',
176
 
            # Rfam-specific GF lines
177
 
            'SS'   => 'SimpleValue/-value/sec_structure_source',
178
 
            # Reference objects mapped differently
179
 
            'RN'   => '-number',  # reference number is dumped
180
 
            'RC'   => '-comment',
181
 
            'RM'   => '-pubmed', 
182
 
            'RT'   => '-title', 
183
 
            'RA'   => '-authors',
184
 
            'RL'   => '-location',
185
 
            # Build model mapped differently
186
 
            'BM'   => '-value',            
187
 
            );
 
156
my %MAPPING = (
 
157
    'AC'    =>  'ACCESSION',
 
158
    'ID'    =>  'ID',
 
159
    'DE'    =>  ['DESCRIPTION' => 'DESCRIPTION'],
 
160
    'AU'    =>  ['RECORD_AUTHORS' => 'RECORD_AUTHORS'],
 
161
    'SE'    =>  'SEED_SOURCE',
 
162
    'BM'    =>  'BUILD_COMMAND',
 
163
    'GA'    =>  'GATHERING_THRESHOLD',
 
164
    'NC'    =>  'NOISE_CUTOFF',
 
165
    'TC'    =>  'TRUSTED_CUTOFF',
 
166
    'TP'    =>  'ENTRY_TYPE',
 
167
    'SQ'    =>  'NUM_SEQUENCES',
 
168
    'PI'    =>  'PREVIOUS_IDS',
 
169
    'DC'    =>  ['DATABASE_COMMENT' => 'DATABASE_COMMENT'],
 
170
    'DR'    =>  'DBLINK',
 
171
    'RN'    =>  ['REFERENCE' => 'REFERENCE'],
 
172
    'RC'    =>  ['REFERENCE' => 'COMMENT'],
 
173
    'RM'    =>  ['REFERENCE' => 'PUBMED'],
 
174
    'RT'    =>  ['REFERENCE' => 'TITLE'],
 
175
    'RA'    =>  ['REFERENCE' => 'AUTHORS'],
 
176
    'RL'    =>  ['REFERENCE' => 'JOURNAL'],
 
177
    'CC'    =>  ['ALIGNMENT_COMMENT' => 'ALIGNMENT_COMMENT'],
 
178
    #Pfam-specific 
 
179
    'AM'    =>  'BUILD_METHOD',
 
180
    'NE'    =>  'PFAM_FAMILY_ACCESSION',
 
181
    'NL'    =>  'SEQ_START_STOP',
 
182
    # Rfam-specific GF lines
 
183
    #'SS'    =>  'SEC_STRUCTURE_SOURCE',
 
184
    'SEQUENCE' => 'SEQUENCE'
 
185
);
 
186
 
 
187
my %CONCATENATE = map {$_ => 1} qw(DE AU DC RC RT RA RL CC);
188
188
 
189
189
# this is the order that annotations are written
190
190
our @WRITEORDER = qw(accession
205
205
  reference
206
206
  database_comment
207
207
  custom
208
 
  aln_dblink
 
208
  dblink
209
209
  alignment_comment
210
210
  num_sequences
 
211
  seq_annotation
211
212
  );
212
213
 
213
214
# This maps the tagname back to a tagname-annotation value combination.
228
229
            'num_sequences'         =>  'SQ/SimpleValue',
229
230
            'previous_ids'          =>  'PI/SimpleValue',
230
231
            'database_comment'      =>  'DC/SimpleValue',
231
 
            'aln_dblink'            =>  'DR/DBLink',
 
232
            'dblink'                =>  'DR/DBLink',
232
233
            'reference'             =>  'RX/Reference',
233
234
            'ref_number'            =>  'RN/number',
234
235
            'ref_comment'           =>  'RC/comment',
237
238
            'ref_authors'           =>  'RA/authors',
238
239
            'ref_location'          =>  'RL/location',
239
240
            'alignment_comment'     =>  'CC/Comment',
 
241
            'seq_annotation'        =>  'DR/Collection',
240
242
            #Pfam-specific 
241
243
            'build_method'          =>  'AM/SimpleValue',
242
244
            'pfam_family_accession' =>  'NE/SimpleValue',
247
249
            'custom'                =>  'XX/SimpleValue'
248
250
            );
249
251
 
 
252
# This maps the tagname back to a tagname-annotation value combination.
 
253
# Some data is stored using get/set methods ('Methods'), others
 
254
# are mapped b/c of more complex annotation types.
 
255
 
 
256
=head2 new
 
257
 
 
258
 Title   : new
 
259
 Usage   : my $alignio = Bio::AlignIO->new(-format => 'phylip'
 
260
                                          -file   => '>file');
 
261
 Function: Initialize a new L<Bio::AlignIO::phylip> reader or writer
 
262
 Returns : L<Bio::AlignIO> object
 
263
 Args    : -line_length :  length of the line for the alignment block
 
264
           -alphabet    :  symbol alphabet to set the sequences to.  If not set,
 
265
                           the parser will try to guess based on the alignment
 
266
                           accession (if present), defaulting to 'dna'.
 
267
           -spaces      :  (optional, def = 1) boolean to add a space in between
 
268
                           the "# STOCKHOLM 1.0" header and the annotation and
 
269
                           the annotation and the alignment.
 
270
 
 
271
=cut
 
272
 
250
273
sub _initialize {
251
274
    my ( $self, @args ) = @_;
252
275
    $self->SUPER::_initialize(@args);
253
 
    # add arguments to handle build object, interleaved format
 
276
    my ($handler, $linelength, $spaces) = $self->_rearrange([qw(HANDLER LINE_LENGTH SPACES)],@args);
 
277
    $spaces = defined $spaces ? $spaces : 1;
 
278
    $self->spaces($spaces);
 
279
    # hash for functions for decoding keys.
 
280
    $handler ? $self->alignhandler($handler) :
 
281
    $self->alignhandler(Bio::AlignIO::Handler::GenericAlignHandler->new(
 
282
                    -format => 'stockholm',
 
283
                    -verbose => $self->verbose,
 
284
                    ));
 
285
    $linelength && $self->line_length($linelength);
254
286
}
255
287
 
256
288
=head2 next_aln
265
297
 
266
298
sub next_aln {
267
299
    my $self = shift;
268
 
    my $line;
269
 
 
270
 
    my ($start, $end, $id, $name, $seqname, $seq, $count, $tag, $data);
271
 
    my $seen_rc;
272
 
    my ($refct, $bct, $lnkct) = (0,0,0);
273
 
    my @c2name;
274
 
    my (%align, %accession, %desc, %seq_meta, %aln_meta, %annotation);
275
 
 
276
 
    # in stockholm format, every non-blank line that does not start
277
 
    # with '#=' is an alignment segment; the '#=' lines are mark up lines.
278
 
    # Of particular interest are the '#=GF <name/st-ed> AC <accession>'
279
 
    # lines, which give accession numbers for each segment
280
 
 
281
 
    my $aln =  Bio::SimpleAlign->new(-source => 'stockholm');
282
 
    while( defined($line = $self->_readline) ) {
283
 
        next unless $line =~ /\w+/;
284
 
        if ($line =~ /^#\s*STOCKHOLM\s+/) {
 
300
    
 
301
    my $handler = $self->alignhandler;
 
302
    # advance to alignment header
 
303
    while( defined(my $line = $self->_readline) ) {
 
304
        if ($line =~ m{^#\s*STOCKHOLM\s+}xmso) {
285
305
            last;
286
 
        } else {
287
 
            $self->throw("Not Stockholm format: Expecting \"# STOCKHOLM 1.0\"; Found \"$_\"");
288
306
        }
289
307
    }
290
308
    
291
 
    READLINE:
292
 
    while( defined($line = $self->_readline) ) {
293
 
        #skip empty lines
294
 
        next if $line =~ /^\s+$/;
295
 
        
296
 
        # Double slash (//) signals end of file.
297
 
        last if $line =~ m{^//};
298
 
        
299
 
        # GF/GS lines, by convention, should be at the top of the alignment
300
 
        if ($line =~ m{^\#=GF\s+(\S+?)\s+([^\n]*)$}xms) {
301
 
 
302
 
            # alignment annotation
303
 
            ($tag, $data) = ($1, $2);
304
 
            if (exists $READMAP{$tag}) {
305
 
 
306
 
                # reference data (multi line)
307
 
                if (index($tag, 'R') == 0) {
308
 
                    # comments come before numbering, tricky
309
 
                    $refct++ if ( ($tag eq 'RN' && !$seen_rc) || $tag eq 'RC');
310
 
                    $seen_rc = 1 if $tag eq 'RC';
311
 
                    # Don't need
312
 
                    next READLINE if $tag eq 'RN';
313
 
                    #                           # of ref       parameter     
314
 
                    $annotation{ 'reference' }->[$refct-1]->{ $READMAP{$tag} } .= $data.' ';
315
 
 
316
 
                # Build commands (single line)
317
 
                } elsif ($tag eq 'BM') {
318
 
                    #                            # build cmd    parameter     
319
 
                    $annotation{ 'build_command' }->[$bct]->{ $READMAP{$tag} } = $data;
320
 
                    $bct++;
321
 
                    
322
 
                # DBLinks (single line)
323
 
                } elsif ($tag eq 'DR') {
324
 
                    my ($dbase, $uid, $extra) = split /\s*;\s*/ , $data, 3;
325
 
                    my $ref;
326
 
                    $ref->{'-database'} = $dbase;
327
 
                    $ref->{'-primary_id'} = ($dbase eq 'URL') ? $uid : uc $uid;
328
 
                    $ref->{'-comment'} = $extra if $extra;
329
 
                    #                       # dblink       parameter list    
330
 
                    $annotation{ 'aln_dblink' }->[$lnkct] = $ref;
331
 
                    $lnkct++;
332
 
                    
333
 
                # Everything else (single and multi line)
334
 
                } else {
335
 
                    #       # param/-value/tagname 
336
 
                    $annotation{ $READMAP{$tag} } .= $data.' ';
 
309
    $self->{block_line} = 0;
 
310
    # go into main body of alignment
 
311
    my ($data_chunk, $isa_primary, $name, $alphabet);
 
312
    my $last_feat = '';
 
313
    while( defined(my $line = $self->_readline) ) {
 
314
        # only blank lines are in between blocks, so reset block line
 
315
        my ($primary_tag, $secondary_tag, $data, $nse, $feat, $align, $concat);
 
316
        if ($line =~ m{^\s*$}xmso) {
 
317
            $self->{block_line} &&= 0;
 
318
            next;
 
319
        }
 
320
        
 
321
        # End of Record
 
322
        if (index($line, '//') == 0) {
 
323
            # fencepost
 
324
            $handler->data_handler($data_chunk);
 
325
            undef $data_chunk;
 
326
            $handler->data_handler({ALIGNMENT => 1,
 
327
                                    NAME => 'ALPHABET',
 
328
                                    DATA => $self->alphabet})
 
329
                if $self->alphabet;
 
330
            last;
 
331
        }
 
332
        elsif ($line =~ m{^\#=([A-Z]{2})\s+([^\n]+?)\s*$}xmso) {
 
333
            ($primary_tag, $data) = ($1, $2);
 
334
            if ($primary_tag eq 'GS' || $primary_tag eq 'GR') {
 
335
                ($nse, $feat, $data) = split(/\s+/, $data, 3);
 
336
            } else {
 
337
                ($feat, $data) = split(/\s+/, $data, 2);
 
338
            }
 
339
            $align = ($primary_tag eq 'GF' || $primary_tag eq 'GR') ? 1 : 0;
 
340
        }
 
341
        elsif ($line =~ m{^([^\#]\S+)\s+([A-Za-z.\-\*]+)\s*}) {
 
342
            $self->{block_line}++;
 
343
            ($feat, $nse, $data) = ('SEQUENCE', $1, $2);
 
344
        }
 
345
        else {
 
346
            $self->debug("Missed line : $line\n");
 
347
        }
 
348
        $primary_tag ||= ''; # when no #= line is present
 
349
        $align ||= 0;
 
350
 
 
351
        # array refs where the two values are equal indicate the start of a
 
352
        # primary chunk of data, otherwise it is to be folded into the last
 
353
        # data chunk under a secondary tag.  These are also concatenated
 
354
        # to previous values if the 
 
355
 
 
356
        if (exists($MAPPING{$feat}) && ref $MAPPING{$feat} eq 'ARRAY') {
 
357
            ($name, $secondary_tag, $isa_primary) = ( $MAPPING{$feat}->[0] eq $MAPPING{$feat}->[1] ) ?
 
358
                ($MAPPING{$feat}->[0], 'DATA', 1) :
 
359
                (@{ $MAPPING{$feat} }, 0) ;
 
360
            $concat = $last_feat eq $feat ? 1 : 0;
 
361
        } elsif (exists($MAPPING{$feat})) {
 
362
            ($name, $secondary_tag, $isa_primary) = ($MAPPING{$feat}, 'DATA', 1);
 
363
            # catch alphabet here if possible
 
364
            if ($align && $name eq 'ACCESSION' && !$self->alphabet) {
 
365
                if ($data =~ m{^(P|R)F}) {
 
366
                    $self->alphabet($1 eq 'R' ? 'rna' : $1 eq 'P' ? 'protein' : undef );
337
367
                }
338
 
 
339
 
            } else {
340
 
                # unknown or custom data treated with simplevalue objects
341
 
                #$self->debug("Unknown tag: $tag:\t$data\n");
342
 
                $annotation{ 'custom' }->{ $tag } .= $data.' ';
343
 
            }
344
 
 
345
 
        } elsif( $line =~ m{^\#=GS\s+(\S+)\s+(\w{2})\s+(\S+)}xms ) {
346
 
            # sequence annotation and data
347
 
            ($id, $tag, $data) = ($1, $2, $3);
348
 
            if ($tag eq 'AC') {
349
 
                $accession{$id} .= $data;
350
 
            } elsif ($tag eq 'DE') {
351
 
                $desc{$id} .= $data;
352
 
            }
353
 
            # Bio::Seq::Meta is not AnnotationI, so can't add seq-based
354
 
            # Annotations yet; uncomment to see what is passed by
355
 
            #else {
356
 
            #    $self->debug("Missed data: $entry");
357
 
            #}
358
 
        } elsif( $line =~ m{^\#=GR\s+(\S+)\s+(\S+)\s+([^\n]+)} ) {
359
 
            # meta strings per sequence
360
 
            ($name, $tag, $data) = ($1, $2, $3);
361
 
            $seq_meta{$name}->{$tag} .= $data;
362
 
        } elsif( $line =~ m{^\#=GC\s+(\S+)\s+([^\n]+)}xms ) {
363
 
            # meta strings per alignment
364
 
            ($tag, $data) = ($1, $2);
365
 
            $aln_meta{$tag} .= $data;
366
 
        } elsif( $line =~ m{^([^\#]\S+)\s+([A-Za-z.\-\*]+)\s*}xms ) {
367
 
            ($name,$seq) = ($1,$2);
368
 
            if( ! exists $align{$name}  ) {
369
 
                push @c2name, $name;
370
 
            }
371
 
            $align{$name} .= $seq;
372
 
        } else {
373
 
            # debugging to catch missed data; uncomment to turn on
374
 
            #$self->debug("Missed Data: $line");
375
 
        }
376
 
    }
377
 
    
378
 
    # ok... now we can make the sequences
379
 
    
380
 
    for my $name ( @c2name ) {
381
 
        if( $name =~ m{(\S+)/(\d+)-(\d+)}xms ) {
382
 
            ($seqname, $start, $end) = ($1, $2, $3);
383
 
        } else {
384
 
            $seqname=$name;
385
 
            $start = 1;
386
 
            $end = length($align{$name});
387
 
        }
388
 
        $seq = Bio::Seq::Meta->new
389
 
            ('-seq'              => $align{$name},
390
 
             '-display_id'       => $seqname,
391
 
             '-start'            => $start,
392
 
             '-end'              => $end,
393
 
             '-description'      => $desc{$name},
394
 
             '-accession_number' => $accession{$name}
395
 
             );
396
 
        if (exists $seq_meta{$name}) {
397
 
            for my $tag (sort keys %{ $seq_meta{$name} }) {
398
 
                $seq->named_meta($tag, $seq_meta{$name}->{$tag});
399
 
            }
400
 
        }
401
 
        $aln->add_seq($seq);
402
 
    }
403
 
    
404
 
    # add meta strings w/o sequence for consensus meta data
405
 
    my $ameta = Bio::Seq::Meta->new();
406
 
    for my $tag (sort keys %aln_meta) {
407
 
        $ameta->named_meta($tag, $aln_meta{$tag});
408
 
    }
409
 
    
410
 
    $aln->consensus_meta($ameta);
411
 
    
412
 
    # Make the annotation collection...
413
 
    
414
 
    my $coll = Bio::Annotation::Collection->new();
415
 
 
416
 
    for my $tag (sort keys %annotation) {
417
 
        
418
 
        # most annotations
419
 
        if (!ref($annotation{$tag})) {
420
 
            my ($atype, $aparam, $tagname) = split q(/), $tag;
421
 
            # remove trailing newline, convert internal newlines to spaces
422
 
            $annotation{$tag} =~ s{\s+$}{}g;
423
 
            # split the READTYPE map to determine Annotation type, parameters, etc.
424
 
            if ($atype eq 'Method') {
425
 
                $aln->$aparam($annotation{$tag});
426
 
            } else {
427
 
                my $factory = Bio::Annotation::AnnotationFactory->new(
428
 
                    -type => "Bio::Annotation::$atype");
429
 
                $coll->add_Annotation
430
 
                ($tagname, $factory->create_object($aparam  => $annotation{$tag}));
431
 
            }
432
 
            
433
 
        } elsif ($tag eq 'custom') {
434
 
            my $factory = Bio::Annotation::AnnotationFactory->new(
435
 
                        -type => "Bio::Annotation::SimpleValue");
436
 
            for my $key (sort keys %{ $annotation{$tag} }) {
437
 
                $coll->add_Annotation(
438
 
                    $tag, $factory->create_object(-tagname => $key,
439
 
                                                  -value => $annotation{$tag}->{$key}));
440
 
            }
441
 
        
442
 
        # more complex annotations
443
 
        
444
 
        } else {
445
 
            my $atype = #($tag eq 'custom')          ? 'SimpleValue'   :
446
 
                        ($tag eq 'reference')       ? 'Reference'   :
447
 
                        ($tag eq 'aln_dblink')      ? 'DBLink'   :
448
 
                        ($tag eq 'build_command')   ? 'SimpleValue' :
449
 
                        'BadValue'; # this will cause the factory to choke
450
 
            $self->throw("Bad tag value : $tag.") if $atype eq 'BadValue';
451
 
            my $factory = Bio::Annotation::AnnotationFactory->new(
452
 
                -type => "Bio::Annotation::$atype");                
453
 
            while (my $data = shift @{ $annotation{$tag} }) {
454
 
                next unless $data;
455
 
                # remove trailing spaces for concatenated data
456
 
                my %clean_data = map {
457
 
                    $data->{$_} =~ s{\s+$}{}g;
458
 
                    $_ => $data->{$_};
459
 
                    } keys %{ $data };
460
 
                my $ann = $factory->create_object(%clean_data);
461
 
                $coll->add_Annotation($tag, $ann);
462
 
                $refct++;
463
 
            }
464
 
        }
465
 
    }
466
 
 
467
 
    #$self->debug(Dumper($coll));
468
 
 
469
 
    # add annotations
470
 
    $aln->annotation($coll); 
471
 
    
472
 
    #  If $end <= 0, we have either reached the end of
473
 
    #  file in <fh> or we have encountered some other error
474
 
    return if ($end <= 0);
 
368
            }
 
369
        } else {
 
370
            $name = ($primary_tag eq 'GR') ? 'NAMED_META' :
 
371
                    ($primary_tag eq 'GC') ? 'CONSENSUS_META' :
 
372
                    'CUSTOM';
 
373
            ($secondary_tag, $isa_primary) = ('DATA', 1);
 
374
        }
 
375
        
 
376
        # Since we can't determine whether data should be passed into the
 
377
        # Handler until the next round (due to concatenation and combining
 
378
        # data), we always check for the presence of the last chunk when the
 
379
        # occasion calls for it (i.e. when the current data string needs to go
 
380
        # into a new data chunk). If the data needs to be concatenated it is
 
381
        # flagged above and checked below (and passed by if the conditions
 
382
        # warrant it).
 
383
        
 
384
        # We run into a bit of a fencepost problem, (one chunk left over at
 
385
        # the end); that is taken care of above when the end of the record is
 
386
        # found.
 
387
        
 
388
        if ($isa_primary && defined $data_chunk && !$concat) {
 
389
            $handler->data_handler($data_chunk);
 
390
            undef $data_chunk;
 
391
        }
 
392
        $data_chunk->{NAME} = $name;       # used for the handler
 
393
        $data_chunk->{ALIGNMENT} = $align; # flag that determines chunk destination
 
394
        $data_chunk->{$secondary_tag} .= (defined($data_chunk->{$secondary_tag})) ?
 
395
            ' '.$data : $data;
 
396
        $data_chunk->{NSE} = $nse if $nse;
 
397
        if ($name eq 'SEQUENCE' || $name eq 'NAMED_META' || $name eq 'CONSENSUS_META') {
 
398
            $data_chunk->{BLOCK_LINE} = $self->{block_line};
 
399
            $data_chunk->{META_TAG} = $feat if ($name ne 'SEQUENCE');
 
400
        }
 
401
        $last_feat = $feat;
 
402
    }
 
403
    
 
404
    my $aln = $handler->build_alignment;    
 
405
    $handler->reset_parameters;
475
406
    return $aln;
476
407
}
477
408
 
478
 
 
479
409
=head2 write_aln
480
410
 
481
411
 Title   : write_aln
486
416
 
487
417
=cut
488
418
 
 
419
{
 
420
    my %LINK_CB = (
 
421
        'PDB' => sub {join('; ',($_[0]->database,
 
422
                                 $_[0]->primary_id.' '.
 
423
                                 ($_[0]->optional_id || ''),
 
424
                                 $_[0]->start,
 
425
                                 $_[0]->end)).';'},
 
426
        'SCOP' => sub {join('; ',($_[0]->database,
 
427
                                 $_[0]->primary_id || '',
 
428
                                 $_[0]->optional_id)).';'},
 
429
        '_DEFAULT_' => sub {join('; ',($_[0]->database,
 
430
                                 $_[0]->primary_id)).';'},
 
431
    );
 
432
 
489
433
sub write_aln {
490
434
    # enable array of SimpleAlign objects as well (see clustalw write_aln())
491
435
    my ($self, @aln) = @_;
493
437
    $self->throw('Need Bio::Align::AlignI object')
494
438
          if (!$aln || !($aln->isa('Bio::Align::AlignI')));
495
439
 
496
 
    my @anns;
497
440
    my $coll = $aln->annotation;
498
 
    my ($aln_ann, $seq_ann, $aln_meta, $seq_meta) =
499
 
       ('#=GF ', '#=GS ', '#=GC ', '#=GR' );
500
 
    $self->_print("# $STKVERSION\n\n") or return 0;
501
 
    
 
441
    my ($aln_ann, $seq_ann) =
 
442
       ('#=GF ', '#=GS ');
 
443
    $self->_print("# $STKVERSION\n") || return 0;
 
444
    $self->spaces && $self->_print("\n");
502
445
    # annotations first
503
446
    
 
447
    #=GF XX ....
504
448
    for my $param (@WRITEORDER) {
 
449
        my @anns;
505
450
        # no point in going through this if there is no annotation!
506
451
        last if !$coll;
507
452
        # alignment annotations
516
461
        }
517
462
        my $rn = 1;
518
463
        ANNOTATIONS:
519
 
        while (my $ann = shift @anns) {
 
464
        for my $ann (@anns) {
520
465
            # using Text::Wrap::wrap() for word wrap
521
466
            my ($text, $alntag, $data);
522
467
            if ($tag eq 'RX') {
536
481
                }
537
482
                $rn++;
538
483
                next ANNOTATIONS;
539
 
            } elsif ($tag eq 'XX') { # custom
 
484
            }
 
485
            elsif ($tag eq 'XX') { # custom
540
486
                my $newtag = $ann->tagname;
541
487
                $alntag = sprintf('%-10s',$aln_ann.$newtag);
542
 
                $data = $ann;
543
 
            } elsif ($tag eq 'SQ') {
 
488
                $data = $ann->display_text;
 
489
            }
 
490
            elsif ($tag eq 'SQ') {
544
491
                # use the actual number, not the stored Annotation data
545
492
                $alntag = sprintf('%-10s',$aln_ann.$tag);
546
493
                $data = $aln->no_sequences;
547
 
            } else {
548
 
                $alntag = sprintf('%-10s',$aln_ann.$tag);
549
 
                $data = $ann;
550
 
            }
 
494
            }
 
495
            elsif ($tag eq 'DR') {
 
496
                $alntag = sprintf('%-10s',$aln_ann.$tag);
 
497
                my $db = uc $ann->database;
 
498
                my $cb = exists $LINK_CB{$db} ? $LINK_CB{$db} : $LINK_CB{_DEFAULT_};
 
499
                $data = $ann->display_text($cb);
 
500
            }
 
501
            else {
 
502
                $alntag = sprintf('%-10s',$aln_ann.$tag);
 
503
                $data = ref $ann ? $ann->display_text : $ann;
 
504
            }
 
505
            next unless $data;
551
506
            $text = wrap($alntag, $alntag, $data);
552
 
            $self->_print("$text\n") or return 0;
553
 
        }
554
 
    }
555
 
    
556
 
    $self->_print("\n");
557
 
    
 
507
            $self->_print("$text\n") || return 0;
 
508
        }
 
509
    }
 
510
    
 
511
    #=GS <seq-id> AC xxxxxx
 
512
    my $tag = 'AC';
 
513
    for my $seq ($aln->each_seq) {
 
514
        if (my $acc = $seq->accession_number) {
 
515
        my $text = sprintf("%-4s%-22s%-3s%s\n",$seq_ann, $seq->get_nse, $tag, $acc);
 
516
        $self->_print($text) || return 0;
 
517
        }
 
518
    }
 
519
    
 
520
    #=GS <seq-id> DR xxxxxx
 
521
    $tag = 'DR';
 
522
    for my $sf ($aln->get_SeqFeatures) {
 
523
        if (my @links = $sf->annotation->get_Annotations('dblink')) {
 
524
            for my $link (@links) {
 
525
                my $db = uc $link->database;
 
526
                my $cb = exists $LINK_CB{$db} ? $LINK_CB{$db} : $LINK_CB{_DEFAULT_};
 
527
                my $text = sprintf("%-4s%-22s%-3s%s\n",$seq_ann,
 
528
                                   $sf->entire_seq->get_nse,
 
529
                                   $tag,
 
530
                                   $link->display_text($cb));
 
531
                $self->_print($text) || return 0;
 
532
            }
 
533
        }
 
534
    }    
 
535
    
 
536
    $self->spaces && $self->_print("\n");    
558
537
    # now the sequences...
559
538
    
 
539
    my $blocklen = $self->line_length;
 
540
    my $maxlen = $aln->maxdisplayname_length() + 3;
 
541
    my $metalen = $aln->max_metaname_length() || 0;
 
542
    if ($blocklen) {
 
543
        my $blockstart = 1;
 
544
        my $alnlen = $aln->length;
 
545
        while ($blockstart < $alnlen) {
 
546
            my $subaln = $aln->slice($blockstart, $blockstart+$blocklen-1 ,1);
 
547
            $self->_print_seqs($subaln,$maxlen,$metalen);
 
548
            $blockstart += $blocklen;
 
549
            $self->_print("\n") unless $blockstart >= $alnlen;
 
550
        }
 
551
    } else {
 
552
        $self->_print_seqs($aln,$maxlen,$metalen);
 
553
    }
 
554
    
 
555
    $self->_print("//\n") || return 0;
 
556
    }
 
557
    $self->flush() if $self->_flush_on_write && defined $self->_fh;
 
558
    
 
559
    return 1;
 
560
}
 
561
 
 
562
}
 
563
 
 
564
=head2 line_length
 
565
 
 
566
 Title   : line_length
 
567
 Usage   : $obj->line_length($newval)
 
568
 Function: Set the alignment output line length
 
569
 Returns : value of line_length
 
570
 Args    : newvalue (optional)
 
571
 
 
572
=cut
 
573
 
 
574
sub line_length {
 
575
    my ( $self, $value ) = @_;
 
576
    if ( defined $value ) {
 
577
        $self->{'_line_length'} = $value;
 
578
    }
 
579
    return $self->{'_line_length'};
 
580
}
 
581
 
 
582
=head2 alphabet
 
583
 
 
584
 Title   : alphabet
 
585
 Usage   : $obj->alphabet('dna')
 
586
 Function: Set the sequence data alphabet
 
587
 Returns : sequence data type
 
588
 Args    : newvalue (optional)
 
589
 
 
590
=cut
 
591
 
 
592
sub alphabet {
 
593
    my ( $self, $value ) = @_;
 
594
    if ( defined $value ) {
 
595
        $self->throw("Invalid alphabet $value") unless $value eq 'rna' || $value eq 'protein' || $value eq 'dna';
 
596
        $self->{'_alphabet'} = $value;
 
597
    }
 
598
    return $self->{'_alphabet'};
 
599
};
 
600
 
 
601
=head2 spaces
 
602
 
 
603
 Title   : spaces
 
604
 Usage   : $obj->spaces(1)
 
605
 Function: Set the 'spaces' flag, which prints extra newlines between the
 
606
           header and the annotation and the annotation and the alignment
 
607
 Returns : sequence data type
 
608
 Args    : newvalue (optional)
 
609
 
 
610
=cut
 
611
 
 
612
sub spaces {
 
613
    my $self = shift;
 
614
    return $self->{'_spaces'} = shift if @_;
 
615
    return $self->{'_spaces'};
 
616
};
 
617
 
 
618
=head2 alignhandler
 
619
 
 
620
 Title   : alignhandler
 
621
 Usage   : $stream->alignhandler($handler)
 
622
 Function: Get/Set the Bio::HandlerBaseI object
 
623
 Returns : Bio::HandlerBaseI 
 
624
 Args    : Bio::HandlerBaseI 
 
625
 
 
626
=cut
 
627
 
 
628
sub alignhandler {
 
629
    my ($self, $handler) = @_;
 
630
    if ($handler) {
 
631
        $self->throw("Not a Bio::HandlerBaseI") unless
 
632
        ref($handler) && $handler->isa("Bio::HandlerBaseI");
 
633
        $self->{'_alignhandler'} = $handler;
 
634
    }
 
635
    return $self->{'_alignhandler'};
 
636
}
 
637
 
 
638
=head2 alignwriter
 
639
 
 
640
 Title   : alignwriter
 
641
 Usage   : $stream->alignwriter($writer)
 
642
 Function: Get/Set the writer object
 
643
 Returns : 
 
644
 Args    : 
 
645
 
 
646
=cut
 
647
 
 
648
sub alignwriter {
 
649
    shift->throw_not_implemented;
 
650
}
 
651
 
 
652
############# PRIVATE INIT/HANDLER METHODS #############
 
653
 
 
654
sub _print_seqs {
 
655
    my ($self, $aln, $maxlen, $metalen) = @_;
 
656
    
 
657
    my ($seq_meta, $aln_meta) = ('#=GR','#=GC');
560
658
    # modified (significantly) from AlignIO::pfam
561
659
    
562
660
    my ($namestr,$seq,$add);
563
661
    
564
662
    # pad extra for meta lines
565
 
    my $maxlen = $aln->maxdisplayname_length() + 5;
566
 
    my $metalen = $aln->max_metaname_length() || 0;
567
 
    
 
663
 
568
664
    for $seq ( $aln->each_seq() ) {
569
 
        $namestr = $aln->displayname($seq->get_nse());
570
 
        $self->_print(sprintf("%-*s  %s\n",$maxlen+$metalen, $namestr, $seq->seq())) or return 0;
 
665
        my ($s, $e, $str) = ($seq->start, $seq->end, $seq->strand);
 
666
        $namestr = $seq->get_nse();
 
667
        $self->_print(sprintf("%-*s%s\n",$maxlen+$metalen,
 
668
                              $namestr,
 
669
                              $seq->seq())) || return 0;
571
670
        if ($seq->isa('Bio::Seq::MetaI')) {
572
671
            for my $mname ($seq->meta_names) {
573
 
                 $self->_print(sprintf("%-*s%*s  %s\n",$maxlen, $seq_meta.' '.$namestr, $metalen,
574
 
                                       $mname, $seq->named_meta($mname))) or return 0;
 
672
                 $self->_print(sprintf("%-*s%s\n",$maxlen+$metalen,
 
673
                                       $seq_meta.' '.$namestr.' '.$mname,
 
674
                                       $seq->named_meta($mname))) || return 0;
575
675
            }
576
676
        }
577
677
    }
579
679
    my $ameta = $aln->consensus_meta;
580
680
    if ($ameta) {
581
681
        for my $mname ($ameta->meta_names) {
582
 
            $self->_print(sprintf("%-*s%*s  %s\n",$maxlen, $aln_meta, $metalen,
583
 
                                  $mname, $ameta->named_meta($mname))) or return 0; 
 
682
            $self->_print(sprintf("%-*s%s\n",$maxlen+$metalen,
 
683
                                  $aln_meta.' '.$mname,
 
684
                                  $ameta->named_meta($mname))) || return 0; 
584
685
        }
585
686
    }
586
 
    $self->_print("//\n") or return 0;
587
 
    }
588
 
    $self->flush() if $self->_flush_on_write && defined $self->_fh;
589
 
    
590
 
    return 1;
591
687
}
592
688
 
593
 
1;
 
 
b'\\ No newline at end of file'
 
689
1;