~ubuntu-branches/ubuntu/oneiric/bioperl/oneiric

« back to all changes in this revision

Viewing changes to Bio/DB/GFF/Adaptor/memory.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
1
package Bio::DB::GFF::Adaptor::memory;
 
2
 
 
3
=head1 NAME
 
4
 
 
5
Bio::DB::GFF::Adaptor::dbi::mysql -- Database adaptor for a specific mysql schema
 
6
 
 
7
=head1 SYNOPSIS
 
8
 
 
9
  use Bio::DB::GFF;
 
10
  my $db = Bio::DB::GFF->new(-adaptor=> 'memory',
 
11
                             -gff    => 'my_features.gff',
 
12
                             -fasta  => 'my_dna.fa'
 
13
                            );
 
14
 
 
15
See L<Bio::DB::GFF> for other methods.
 
16
 
 
17
=head1 DESCRIPTION
 
18
 
 
19
This adaptor implements an in-memory version of Bio::DB::GFF.  It can be used to
 
20
store and retrieve SHORT GFF files. It inherits from Bio::DB::GFF.
 
21
 
 
22
=head1 CONSTRUCTOR
 
23
 
 
24
Use Bio::DB::GFF-E<gt>new() to construct new instances of this class.
 
25
Three named arguments are recommended:
 
26
 
 
27
   Argument         Description
 
28
 
 
29
   -adaptor         Set to "memory" to create an instance of this class.
 
30
   -gff             Read the indicated file or directory of .gff file.
 
31
   -fasta           Read the indicated file or directory of fasta files.
 
32
   -dir             Indicates a directory containing .gff and .fa files
 
33
 
 
34
If you use the -dsn option and the indicated directory is writable by
 
35
the current process, then this library will create a FASTA file index
 
36
that greatly diminishes the memory usage of this module.
 
37
 
 
38
=head1 METHODS
 
39
 
 
40
See L<Bio::DB::GFF> for inherited methods.
 
41
 
 
42
=head1 BUGS
 
43
 
 
44
none ;-)
 
45
 
 
46
=head1 SEE ALSO
 
47
 
 
48
L<Bio::DB::GFF>, L<bioperl>
 
49
 
 
50
=head1 AUTHOR
 
51
 
 
52
Shuly Avraham E<lt>avraham@cshl.orgE<gt>.
 
53
 
 
54
Copyright (c) 2002 Cold Spring Harbor Laboratory.
 
55
 
 
56
This library is free software; you can redistribute it and/or modify
 
57
it under the same terms as Perl itself.
 
58
 
 
59
=cut
 
60
 
2
61
use strict;
3
 
 
 
62
# $Id: memory.pm,v 1.21 2003/12/17 18:25:42 scain Exp $
 
63
# AUTHOR: Shulamit Avraham
 
64
# This module needs to be cleaned up and documented
 
65
 
 
66
# Bio::DB::GFF::Adaptor::memory --  in-memory db adaptor
 
67
# implements the low level handling of data which stored in memory.
 
68
# This adaptor implements a specific in memory schema that is compatible with Bio::DB::GFF.
 
69
# Inherits from Bio::DB::GFF.
 
70
 
 
71
 
 
72
#use lib './blib/lib';
 
73
#use lib '/u/swiss/shuly/bioperl-live';
 
74
# use lib '/a/swiss/export/home/shuly/bioperl-live';
4
75
use Bio::DB::GFF;
5
76
use Bio::DB::GFF::Util::Rearrange; # for rearrange()
6
 
use vars qw($VERSION @ISA);
 
77
use Bio::DB::GFF::Adaptor::memory_iterator;
 
78
use File::Basename 'dirname';
 
79
 
 
80
use vars qw(@ISA);
7
81
 
8
82
use constant MAX_SEGMENT => 100_000_000;  # the largest a segment can get
9
83
 
10
84
@ISA =  qw(Bio::DB::GFF);
11
 
$VERSION = '0.01';
12
85
 
13
86
sub new {
14
87
  my $class = shift ;
15
 
  my ($file) = rearrange([
16
 
                          [qw(FILE DIRECTORY)]
17
 
                         ],@_);
 
88
  my ($file,$fasta,$dbdir,$preferred_groups) = rearrange([
 
89
                                                          [qw(GFF FILE)],
 
90
                                                          'FASTA',
 
91
                                                          [qw(DSN DB DIR DIRECTORY)],
 
92
                                                          'PREFERRED_GROUPS',
 
93
                                                         ],@_);
18
94
 
19
95
  # fill in object
20
96
  my $self = bless{ data => [] },$class;
21
 
  $self->load($file) if $file;
 
97
  $self->preferred_groups($preferred_groups) if defined $preferred_groups;
 
98
  $file  ||= $dbdir;
 
99
  $fasta ||= $dbdir;
 
100
  $self->load_gff($file)             if $file;
 
101
  $self->load_or_store_fasta($fasta) if $fasta;
22
102
  return $self;
23
103
}
24
104
 
 
105
sub load_or_store_fasta {
 
106
  my $self  = shift;
 
107
  my $fasta = shift;
 
108
  if ((-f $fasta && -w dirname($fasta))
 
109
      or
 
110
      (-d $fasta && -w $fasta)) {
 
111
    require Bio::DB::Fasta;
 
112
    my $dna_db = eval {Bio::DB::Fasta->new($fasta)} 
 
113
      or warn "No sequence available. Use -gff instead of -dir if you wish to load features without sequence.\n";
 
114
    $dna_db && $self->dna_db($dna_db);
 
115
  } else {
 
116
    $self->load_fasta($fasta);
 
117
  }
 
118
}
 
119
 
 
120
sub dna_db {
 
121
  my $self = shift;
 
122
  my $d    = $self->{dna_db};
 
123
  $self->{dna_db} = shift if @_;
 
124
  $d;
 
125
}
 
126
 
25
127
sub insert_sequence {
26
128
  my $self = shift;
27
129
  my($id,$offset,$seq) = @_;
28
130
  $self->{dna}{$id} .= $seq;
29
131
}
30
132
 
 
133
# low-level fetch of a DNA substring given its
 
134
# name, class and the desired range.
31
135
sub get_dna {
32
136
  my $self = shift;
33
137
  my ($id,$start,$stop,$class) = @_;
 
138
  if (my $dna_db = $self->dna_db) {
 
139
    return $dna_db->seq($id,$start=>$stop);
 
140
  }
 
141
 
 
142
  return $self->{dna}{$id} if !defined $start || !defined $stop;
 
143
  $start = 1 if !defined $start;
 
144
 
34
145
  my $reversed = 0;
35
146
  if ($start > $stop) {
36
147
    $reversed++;
45
156
  $dna;
46
157
}
47
158
 
 
159
# this method loads the feature as a hash into memory -
 
160
# keeps an array of features-hashes as an in-memory db
48
161
sub load_gff_line {
49
162
  my $self = shift;
50
163
  my $feature_hash  = shift;
51
 
  $feature_hash->{strand} = '+' if $feature_hash->{strand} eq '.'; 
52
 
  $feature_hash->{phase} = '+' if $feature_hash->{phase} eq '.';
 
164
  $feature_hash->{strand} = '' if $feature_hash->{strand} && $feature_hash->{strand} eq '.';
 
165
  $feature_hash->{phase} = ''  if $feature_hash->{phase}  && $feature_hash->{phase} eq '.';
53
166
  push @{$self->{data}},$feature_hash;
54
167
}
55
168
 
 
169
# given sequence name, return (reference,start,stop,strand)
56
170
sub get_abscoords {
57
171
  my $self = shift;
58
172
  my ($name,$class,$refseq) = @_;
59
173
  my %refs;
 
174
  my $regexp;
 
175
 
 
176
  if ($name =~ /[*?]/) {  # uh oh regexp time
 
177
    $name =~ quotemeta($name);
 
178
    $name =~ s/\\\*/.*/g;
 
179
    $name =~ s/\\\?/.?/g;
 
180
    $regexp++;
 
181
  }
60
182
 
61
183
  # Find all features that have the requested name and class.
62
 
  #for my $type (@$typelist) {
63
 
  #     my ($method,$source) = @$type;
64
 
  #     if defined $method && length $method {
65
 
  #       next unless $feature_method ;
66
 
  #     }
67
 
  #} 
68
184
  # Sort them by reference point.
69
185
  for my $feature (@{$self->{data}}) {
70
 
    next unless $feature->{gname} eq $name;
71
 
    next unless $feature->{gclass} eq $class;
 
186
 
 
187
    my $no_match_class_name;
 
188
    my $empty_class_name;
 
189
    if (defined $feature->{gname} and defined $feature->{gclass}){
 
190
      my $matches = $feature->{gclass} eq $class
 
191
        && ($regexp ? $feature->{gname} =~ /$name/i : $feature->{gname} eq $name);
 
192
      $no_match_class_name = !$matches;  # to accomodate Shuly's interesting logic
 
193
    }
 
194
 
 
195
    else{
 
196
      $empty_class_name = 1;
 
197
    }
 
198
 
 
199
    if ($no_match_class_name || $empty_class_name){
 
200
 
 
201
      my $feature_attributes = $feature->{attributes};
 
202
      my $attributes = {Alias => $name};
 
203
      if (!_matching_attributes($feature_attributes,$attributes)){
 
204
        next;
 
205
      }
 
206
    }
72
207
    push @{$refs{$feature->{ref}}},$feature;
73
208
  }
74
209
 
75
210
  # find out how many reference points we recovered
 
211
 
 
212
  if (! %refs) {
 
213
    $self->error("$name not found in database");
 
214
    return;
 
215
  } elsif (keys %refs > 1) {
 
216
    $self->error("$name has more than one reference sequence in database");
 
217
    return;
 
218
  }
 
219
 
 
220
  # compute min and max
 
221
  my ($ref) = keys %refs;
 
222
  my @found = @{$refs{$ref}};
 
223
  my ($strand,$start,$stop);
 
224
 
76
225
  my @found_segments;
77
226
  foreach my $ref (keys %refs) {
78
227
    next if defined($refseq) and $ref ne $refseq;
80
229
    my ($strand,$start,$stop);
81
230
    foreach (@found) {
82
231
      $strand ||= $_->{strand};
83
 
      $strand = '+' if $strand eq '.'; 
 
232
      $strand = '+' if $strand && $strand eq '.'; 
84
233
      $start  = $_->{start} if !defined($start) || $start > $_->{start};
85
234
      $stop   = $_->{stop}  if !defined($stop)  || $stop  < $_->{stop};
86
235
    }
87
236
    push @found_segments,[$ref,$class,$start,$stop,$strand];
 
237
 
88
238
  }
89
239
  return \@found_segments;
90
240
}
91
241
 
92
 
 
93
 
#sub get_features{
94
 
#  my $self = shift;
95
 
#  my ($search,$options,$callback) = @_;
96
 
#  my @found_features;
97
 
#  my (%result,%obj);
98
 
 
99
 
#  for my $feature (@{$self->{data}}) {
100
 
#    my $feature_start = $feature->{start};
101
 
#    my $feature_stop  = $feature->{stop};
102
 
#    my $feature_ref   = $feature->{ref};
103
 
#    next unless $feature_ref eq $search->{refseq};
104
 
 
105
 
#    my $rangetype = $search->{rangetype};
106
 
#    if ($rangetype eq 'overlap') {
107
 
#      next unless $feature_stop >= $search->{start} && $feature_start <= $search->{stop};
108
 
#    } elsif ($rangetype eq 'contains') {
109
 
#      next unless $feature_start >= $search->{start} && $feature_stop <= $search->{stop};
110
 
#    } elsif ($rangetype eq 'contained_in') {
111
 
#      next unless $feature_start <= $search->{start} && $feature_stop >= $search->{stop};
112
 
#    } else {
113
 
#      next unless $feature_start == $search->{start} && $feature_stop == $search->{stop};
114
 
#    }
115
 
 
116
 
#    my $feature_source = $feature->{source};
117
 
#    my $feature_method = $feature->{method};
118
 
    
119
 
#    foreach (@{$search->{types}}) {
120
 
#      my ($search_method,$search_source) = @$_;
121
 
#      next if $search_method ne $feature_method;
122
 
#      next if defined($search_source) && $search_source ne $feature_source;
123
 
#    }
124
 
 
125
 
    # if we get here, then we have a feature that meets the criteria.
126
 
    # If we were asked to sort by group, then we just push onto an array
127
 
    # of found features and continue.  Otherwise we call the callback
128
 
    # immediately.
129
 
#    if ($options->{sort_by_group}) {
130
 
#      push @found_features,$feature;
131
 
#      next;
132
 
#    } else {
133
 
#      $callback->($feature_ref,
134
 
#                 $feature_start,
135
 
#                 $feature_stop,
136
 
#                 $feature_source,
137
 
#                 $feature_method,
138
 
#                 $feature->{score},
139
 
#                 $feature->{strand},
140
 
#                 $feature->{phase},
141
 
#                 $feature->{gclass},
142
 
#                 $feature->{gname},
143
 
#                 $feature->{tstart},
144
 
#                 $feature->{tstop}
145
 
#                );
146
 
#    }
147
 
#  }
148
 
 
149
 
#  for my $feature (sort
150
 
#                  {"$a->{gclass}:$a->{gname}" cmp "$b->{gclass}:$b->{gname}"
151
 
#                 } @found_features) {  # only true if the sort by group option was specified
152
 
 #   $callback->(
153
 
#               @{$feature}{qw(ref start stop source method score strand phase gclass gname tstart tstop)}
154
 
#              );
155
 
#  }
156
 
#}
157
 
 
 
242
sub search_notes {
 
243
  my $self = shift;
 
244
  my ($search_string,$limit) = @_;
 
245
  my @results;
 
246
  my @words = map {quotemeta($_)} $search_string =~ /(\w+)/g;
 
247
 
 
248
  for my $feature (@{$self->{data}}) {
 
249
    next unless defined $feature->{gclass} && defined $feature->{gname}; # ignore NULL objects
 
250
    next unless $feature->{attributes};
 
251
    my @attributes = @{$feature->{attributes}};
 
252
    my @values     = map {$_->[1]} @attributes;
 
253
    my $value      = "@values";
 
254
    my $matches    = 0;
 
255
    my $note;
 
256
    for my $w (@words) {
 
257
      my @hits = $value =~ /($w)/g;
 
258
      $note ||= $value if @hits;
 
259
      $matches += @hits;
 
260
    }
 
261
    next unless $matches;
 
262
 
 
263
    my $relevance = 10 * $matches;
 
264
    my $featname = Bio::DB::GFF::Featname->new($feature->{gclass}=>$feature->{gname});
 
265
    push @results,[$featname,$note,$relevance];
 
266
    last if @results >= $limit;
 
267
  }
 
268
  @results;
 
269
}
 
270
 
 
271
sub _delete_features {
 
272
  my $self        = shift;
 
273
  my @feature_ids = sort {$b<=>$a} @_;
 
274
  my $removed = 0;
 
275
  foreach (@feature_ids) {
 
276
    next unless $_ >= 0 && $_ < @{$self->{data}};
 
277
    $removed += defined splice(@{$self->{data}},$_,1);
 
278
  }
 
279
  $removed;
 
280
}
 
281
 
 
282
sub _delete {
 
283
  my $self = shift;
 
284
    my $delete_spec = shift;
 
285
  my $ranges      = $delete_spec->{segments} || [];
 
286
  my $types       = $delete_spec->{types}    || [];
 
287
  my $force       = $delete_spec->{force};
 
288
  my $range_type  = $delete_spec->{range_type};
 
289
 
 
290
  my $deleted = 0;
 
291
  if (@$ranges) {
 
292
    my @args = @$types ? (-type=>$types) : ();
 
293
    push @args,(-range_type => $range_type);
 
294
    my %ids_to_remove = map {$_->id => 1} map {$_->features(@args)} @$ranges;
 
295
    $deleted = $self->delete_features(keys %ids_to_remove);
 
296
  } elsif (@$types) {
 
297
    my %ids_to_remove = map {$_->id => 1} $self->features(-type=>$types);
 
298
    $deleted = $self->delete_features(keys %ids_to_remove);
 
299
  } else {
 
300
    $self->throw("This operation would delete all feature data and -force not specified")
 
301
      unless $force;
 
302
    $deleted = @{$self->{data}};
 
303
    @{$self->{data}} = ();
 
304
  }
 
305
  $deleted;
 
306
}
 
307
 
 
308
# attributes -
 
309
 
 
310
# Some GFF version 2 files use the groups column to store a series of
 
311
# attribute/value pairs.  In this interpretation of GFF, the first such
 
312
# pair is treated as the primary group for the feature; subsequent pairs
 
313
# are treated as attributes.  Two attributes have special meaning:
 
314
# "Note" is for backward compatibility and is used for unstructured text
 
315
# remarks.  "Alias" is considered as a synonym for the feature name.
 
316
# If no name is provided, then attributes() returns a flattened hash, of
 
317
# attribute=>value pairs.
 
318
 
 
319
sub do_attributes{
 
320
  my $self = shift;
 
321
  my ($feature_id,$tag) = @_;
 
322
  my $attr ;
 
323
 
 
324
  my $feature = ${$self->{data}}[$feature_id];
 
325
  my @result;
 
326
  for my $attr (@{$feature->{attributes}}) {
 
327
    my ($attr_name,$attr_value) = @$attr ;
 
328
    if (defined($tag) && $attr_name eq $tag){push @result,$attr_value;}
 
329
    elsif (!defined($tag)) {push @result,($attr_name,$attr_value);}
 
330
  }
 
331
  return @result;
 
332
}
 
333
 
 
334
 
 
335
#sub get_feature_by_attribute{
 
336
sub _feature_by_attribute{
 
337
  my $self = shift;
 
338
  my ($attributes,$callback) = @_;
 
339
  $callback || $self->throw('must provide a callback argument');
 
340
  my $count = 0;
 
341
  my $feature_id = -1;
 
342
  my $feature_group_id = undef;
 
343
 
 
344
  for my $feature (@{$self->{data}}) {
 
345
 
 
346
    $feature_id++;
 
347
    for my $attr (@{$feature->{attributes}}) {
 
348
      my ($attr_name,$attr_value) = @$attr ;
 
349
      #there could be more than one set of attributes......
 
350
      foreach (keys %$attributes) {
 
351
        if ($_ eq $attr_name && $attributes->{$_} eq $attr_value){
 
352
           $callback->($feature->{ref},
 
353
                $feature->{start},
 
354
                $feature->{stop},
 
355
                $feature->{source},
 
356
                $feature->{method},
 
357
                $feature->{score},
 
358
                $feature->{strand},
 
359
                $feature->{phase},
 
360
                $feature->{gclass},
 
361
                $feature->{gname},
 
362
                $feature->{tstart},
 
363
                $feature->{tstop},
 
364
                $feature_id,
 
365
                $feature_group_id);
 
366
           $count++;
 
367
        }
 
368
      }
 
369
    }
 
370
  }
 
371
 
 
372
}
 
373
 
 
374
 
 
375
 
 
376
# This is the low-level method that is called to retrieve GFF lines from
 
377
# the database.  It is responsible for retrieving features that satisfy
 
378
# range and feature type criteria, and passing the GFF fields to a
 
379
# callback subroutine.
158
380
 
159
381
sub get_features{
160
382
  my $self = shift;
161
 
  my ($rangetype,$refseq,$class,$start,$stop,$types,$sparse,$callback,$order_by_group) = @_;
162
 
  my @found_features;
163
 
  my (%result,%obj);
164
 
 
165
 
  for my $feature (@{$self->{data}}) {
166
 
    my $feature_start = $feature->{start};
167
 
    my $feature_stop  = $feature->{stop};
168
 
    my $feature_ref   = $feature->{ref};
169
 
    next unless $feature_ref eq $refseq;
170
 
 
171
 
 
172
 
     if (defined $start or defined $stop) {
173
 
      $start = 0               unless defined($start);
174
 
      $stop  = MAX_SEGMENT     unless defined($stop);
175
 
    
176
 
      if ($rangetype eq 'overlaps') {
177
 
        next unless $feature_stop >= $start && $feature_start <= $stop;
178
 
      } elsif ($rangetype eq 'contains') {
179
 
        next unless $feature_start >= $start && $feature_stop <= $stop;
180
 
      } elsif ($rangetype eq 'contained_in') {
181
 
        next unless $feature_start <= $start && $feature_stop >= $stop;
182
 
      } else {
183
 
        next unless $feature_start == $start && $feature_stop == $stop;
184
 
      }
185
 
 
186
 
    }
187
 
    
188
 
    my $feature_source = $feature->{source};
189
 
    my $feature_method = $feature->{method};
190
 
 
191
 
    if (defined $types && @$types){
192
 
      next unless _matching_typelist($feature_method,$feature_source,$types);
193
 
    } 
194
 
 
195
 
    # if we get here, then we have a feature that meets the criteria.
196
 
    # If we were asked to sort by group, then we just push onto an array
197
 
    # of found features and continue.  Otherwise we call the callback
198
 
    # immediately.
199
 
    if ($order_by_group) {
200
 
      push @found_features,$feature;
201
 
      next;
202
 
    } else {
203
 
      $callback->($feature_ref,
204
 
                  $feature_start,
205
 
                  $feature_stop,
206
 
                  $feature_source,
207
 
                  $feature_method,
208
 
                  $feature->{score},
209
 
                  $feature->{strand},
210
 
                  $feature->{phase},
211
 
                  $feature->{gclass},
212
 
                  $feature->{gname},
213
 
                  $feature->{tstart},
214
 
                  $feature->{tstop}
215
 
                 );
216
 
    }
217
 
  }
218
 
 
219
 
  for my $feature (sort
220
 
                   {"$a->{gclass}:$a->{gname}" cmp "$b->{gclass}:$b->{gname}"
221
 
                  } @found_features) {  # only true if the sort by group option was specified
 
383
  my $count = 0;
 
384
  my ($search,$options,$callback) = @_;
 
385
  my $data = \@{$self->{data}};
 
386
 
 
387
  my $found_features;
 
388
 
 
389
  $found_features = _get_features_by_search_options($data,$search,$options);
 
390
 
 
391
  # only true if the sort by group option was specified
 
392
  @{$found_features} = sort {"$a->{gclass}:$a->{gname}" cmp "$b->{gclass}:$b->{gname}"} 
 
393
    @{$found_features} if $options->{sort_by_group} ;
 
394
 
 
395
  for my $feature (@{$found_features}) {  # only true if the sort by group option was specified
 
396
    $count++;
222
397
    $callback->(
223
 
                @{$feature}{qw(ref start stop source method score strand phase gclass gname tstart tstop)}
224
 
               );
225
 
  }
226
 
}
227
 
 
 
398
                @{$feature}{qw(ref start stop source method score strand phase gclass gname tstart tstop feature_id feature_group_id)}
 
399
               );
 
400
  }
 
401
 
 
402
  return $count;
 
403
}
 
404
 
 
405
 
 
406
# Low level implementation of fetching a named feature.
 
407
# GFF annotations are named using the group class and name fields.
 
408
# May return zero, one, or several Bio::DB::GFF::Feature objects.
 
409
 
 
410
=head2 _feature_by_name
 
411
 
 
412
 Title   : _feature_by_name
 
413
 Usage   : $db->get_features_by_name($name,$class,$callback)
 
414
 Function: get a list of features by name and class
 
415
 Returns : count of number of features retrieved
 
416
 Args    : name of feature, class of feature, and a callback
 
417
 Status  : protected
 
418
 
 
419
This method is used internally.  The callback arguments are those used
 
420
by make_feature().
 
421
 
 
422
=cut
 
423
 
 
424
sub _feature_by_name {
 
425
  my $self = shift;
 
426
  my ($class,$name,$location,$callback) = @_;
 
427
  $callback || $self->throw('must provide a callback argument');
 
428
  my $count = 0;
 
429
  my $id    = -1;
 
430
  my $regexp;
 
431
 
 
432
  if ($name =~ /[*?]/) {  # uh oh regexp time
 
433
    $name = quotemeta($name);
 
434
    $name =~ s/\\\*/.*/g;
 
435
    $name =~ s/\\\?/.?/g;
 
436
    $regexp++;
 
437
  }
 
438
 
 
439
 
 
440
  for my $feature (@{$self->{data}}) {
 
441
    $id++;
 
442
    next unless ($regexp && $feature->{gname} =~ /$name/i) || $feature->{gname}  eq $name;
 
443
    next unless $feature->{gclass} eq $class;
 
444
    if ($location) {
 
445
      next if $location->[0] ne $feature->{ref};
 
446
      next if $location->[1] && $location->[1] > $feature->{stop};
 
447
      next if $location->[2] && $location->[2] < $feature->{start};
 
448
    }
 
449
    $count++;
 
450
    $callback->(@{$feature}{qw(
 
451
                               ref
 
452
                               start
 
453
                               stop
 
454
                               source
 
455
                               method
 
456
                               score
 
457
                               strand
 
458
                               phase
 
459
                               gclass
 
460
                               gname
 
461
                               tstart
 
462
                               tstop
 
463
                              )},$id,0
 
464
               );
 
465
  }
 
466
  return $count;
 
467
}
 
468
 
 
469
# Low level implementation of fetching a feature by it's id. 
 
470
# The id of the feature as implemented in the in-memory db, is the location of the 
 
471
# feature in the features hash array.
 
472
sub _feature_by_id{
 
473
  my $self = shift;
 
474
  my ($ids,$type,$callback) = @_;
 
475
  $callback || $self->throw('must provide a callback argument');
 
476
 
 
477
  my $feature_group_id = undef;
 
478
 
 
479
  my $count = 0;
 
480
  if ($type eq 'feature'){
 
481
    for my $feature_id (@$ids){
 
482
       my $feature = ${$self->{data}}[$feature_id];
 
483
       
 
484
       $callback->($feature->{ref},
 
485
                $feature->{start},
 
486
                $feature->{stop},
 
487
                $feature->{source},
 
488
                $feature->{method},
 
489
                $feature->{score},
 
490
                $feature->{strand},
 
491
                $feature->{phase},
 
492
                $feature->{gclass},
 
493
                $feature->{gname},
 
494
                $feature->{tstart},
 
495
                $feature->{tstop},
 
496
                $feature_id,
 
497
                $feature_group_id);
 
498
           $count++;                    
 
499
    
 
500
    }
 
501
  }
 
502
}
 
503
 
 
504
 
 
505
# This method is similar to get_features(), except that it returns an
 
506
# iterator across the query.  
 
507
# See Bio::DB::GFF::Adaptor::memory_iterator.
 
508
 
 
509
sub get_features_iterator {
 
510
  my $self = shift;
 
511
  my ($search,$options,$callback) = @_;
 
512
  $callback || $self->throw('must provide a callback argument');
 
513
 
 
514
  my $data = \@{$self->{data}};
 
515
  my $results = _get_features_by_search_options($data,$search,$options);
 
516
  my $results_array = _convert_feature_hash_to_array($results);
 
517
 
 
518
  return Bio::DB::GFF::Adaptor::memory_iterator->new($results_array,$callback);
 
519
}
 
520
 
 
521
 
 
522
 
 
523
 
 
524
# This method is responsible for fetching the list of feature type names.
 
525
# The query may be limited to a particular range, in
 
526
# which case the range is indicated by a landmark sequence name and
 
527
# class and its subrange, if any.  These arguments may be undef if it is
 
528
# desired to retrieve all feature types.
 
529
 
 
530
# If the count flag is false, the method returns a simple list of
 
531
# Bio::DB::GFF::Typename objects.  If $count is true, the method returns
 
532
# a list of $name=>$count pairs, where $count indicates the number of
 
533
# times this feature occurs in the range.
228
534
 
229
535
sub get_types {
230
536
  my $self = shift;
268
574
 
269
575
}
270
576
 
271
 
sub _matching_typelist{
272
 
  
 
577
 
 
578
 
 
579
 
 
580
# Internal method that performs a search on the features array, 
 
581
# sequentialy retrieves the features, and performs a check on each feature
 
582
# according to the search options.
 
583
sub _get_features_by_search_options{
 
584
  my $count = 0;
 
585
  my ($data,$search,$options) = @_;
 
586
  my ($rangetype,$refseq,$class,$start,$stop,$types,$sparse,$order_by_group,$attributes) = 
 
587
    (@{$search}{qw(rangetype refseq refclass start stop types)},
 
588
    @{$options}{qw(sparse sort_by_group ATTRIBUTES)}) ;
 
589
                                               
 
590
  my @found_features;
 
591
 
 
592
  my $feature_id = -1 ;
 
593
  my $feature_group_id = undef;
 
594
 
 
595
  for my $feature (@{$data}) {
 
596
 
 
597
    $feature_id++;
 
598
    
 
599
    my $feature_start = $feature->{start};
 
600
    my $feature_stop  = $feature->{stop};
 
601
    my $feature_ref   = $feature->{ref};
 
602
    
 
603
    if (defined $refseq){
 
604
      next unless $feature_ref eq $refseq;
 
605
    }
 
606
 
 
607
     if (defined $start or defined $stop) {
 
608
      $start = 0               unless defined($start);
 
609
      $stop  = MAX_SEGMENT     unless defined($stop);
 
610
    
 
611
      if ($rangetype eq 'overlaps') {
 
612
        next unless $feature_stop >= $start && $feature_start <= $stop;
 
613
      } elsif ($rangetype eq 'contains') {
 
614
        next unless $feature_start >= $start && $feature_stop <= $stop;
 
615
      } elsif ($rangetype eq 'contained_in') {
 
616
        next unless $feature_start <= $start && $feature_stop >= $stop;
 
617
      } else {
 
618
        next unless $feature_start == $start && $feature_stop == $stop;
 
619
      }
 
620
 
 
621
    }
 
622
    
 
623
    my $feature_source = $feature->{source};
 
624
    my $feature_method = $feature->{method};
 
625
 
 
626
    if (defined $types && @$types){
 
627
      next unless _matching_typelist($feature_method,$feature_source,$types);
 
628
    } 
 
629
 
 
630
    my $feature_attributes = $feature->{attributes};
 
631
    if (defined $attributes){
 
632
      next unless _matching_attributes($feature_attributes,$attributes);
 
633
    } 
 
634
    
 
635
    # if we get here, then we have a feature that meets the criteria.
 
636
    # Then we just push onto an array
 
637
    # of found features and continue. 
 
638
   
 
639
    my $found_feature = $feature ;
 
640
    $found_feature->{feature_id} = $feature_id;
 
641
    $found_feature->{group_id} = $feature_group_id;
 
642
    push @found_features,$found_feature;
 
643
   
 
644
  }
 
645
 
 
646
  return \@found_features; 
 
647
}
 
648
 
 
649
 
 
650
 
 
651
 
 
652
 
 
653
# this subroutine is needed for convertion of the feature from hash to array in order to 
 
654
# pass it to the callback subroutine
 
655
sub _convert_feature_hash_to_array{
 
656
  my @features_hash_array = @_;
 
657
 
 
658
  use constant FREF    => 0;
 
659
  use constant FSTART  => 1;
 
660
  use constant FSTOP   => 2;
 
661
  use constant FSOURCE => 3;
 
662
  use constant FMETHOD => 4;
 
663
  use constant FSCORE  => 5;
 
664
  use constant FSTRAND => 6;
 
665
  use constant FPHASE  => 7;
 
666
  use constant GCLASS  => 8;
 
667
  use constant GNAME   => 9;
 
668
  use constant TSTART  => 10;
 
669
  use constant TSTOP   => 11;
 
670
  use constant FID     => 12;
 
671
  use constant GID     => 13;
 
672
 
 
673
  my @features_array_array;
 
674
  my $feature_count = 0;
 
675
   
 
676
  for my $feature_hash (@{$features_hash_array[0]}){
 
677
    my @feature_array;
 
678
 
 
679
    $feature_array[FREF]    = $feature_hash->{ref};
 
680
    $feature_array[FSTART]  = $feature_hash->{start};
 
681
    $feature_array[FSTOP]   = $feature_hash->{stop};  
 
682
    $feature_array[FSOURCE] = $feature_hash->{source};
 
683
    $feature_array[FMETHOD] = $feature_hash->{method};
 
684
    $feature_array[FSCORE]  = $feature_hash->{score};
 
685
    $feature_array[FSTRAND] = $feature_hash->{strand};  
 
686
    $feature_array[FPHASE ] = $feature_hash->{phase};
 
687
    $feature_array[GCLASS]  = $feature_hash->{gclass};  
 
688
    $feature_array[GNAME]   = $feature_hash->{gname};
 
689
    $feature_array[TSTART]  = $feature_hash->{tstart};
 
690
    $feature_array[TSTOP]   = $feature_hash->{tstop};
 
691
    $feature_array[FID]     = $feature_hash->{feature_id};  
 
692
    $feature_array[GID]     = $feature_hash->{group_id};
 
693
 
 
694
    $features_array_array[$feature_count] = \@feature_array;
 
695
    $feature_count++;
 
696
  }
 
697
  return \@features_array_array;
 
698
}
 
699
 
 
700
sub _matching_typelist{ 
273
701
  my ($feature_method,$feature_source,$typelist) = @_; 
274
 
 
275
702
  foreach (@$typelist) {
276
703
         my ($search_method,$search_source) = @$_;
277
704
         next if $search_method ne $feature_method;
278
705
         next if defined($search_source) && $search_source ne $feature_source;
279
706
         return 1;
280
707
  }
281
 
 
282
 
  return 0;
283
 
}
 
708
  return 0;
 
709
}
 
710
 
 
711
sub _matching_attributes{
 
712
  my ($feature_attributes,$attributes) = @_ ;
 
713
  foreach (keys %$attributes) {
 
714
    return 0 if !_match_all_attr_in_feature($_,$attributes->{$_},$feature_attributes)
 
715
   
 
716
  }
 
717
  return 1;
 
718
}
 
719
 
 
720
sub _match_all_attr_in_feature{
 
721
  my ($attr_name,$attr_value,$feature_attributes) = @_;
 
722
  for my $attr (@$feature_attributes) {
 
723
      my ($feature_attr_name,$feature_attr_value) = @$attr ;
 
724
      next if ($attr_name ne $feature_attr_name || $attr_value ne $feature_attr_value);
 
725
      return 1;
 
726
  }
 
727
  return 0;
 
728
}
 
729
 
284
730
 
285
731
sub do_initialize { 1; }
286
732
sub setup_load { }
287
733
sub finish_load { 1; }
 
734
sub get_feature_by_group_id{ 1; }
288
735
 
289
736
1;
290
737