File Coverage

lib/Bio/Graphics/FeatureFile.pm
Criterion Covered Total %
statement 139 656 21.1
branch 38 410 9.2
condition 9 199 4.5
subroutine 28 94 29.7
pod 38 66 57.5
total 252 1425 17.6


line stmt bran cond sub pod time code
1             package Bio::Graphics::FeatureFile;
2              
3             # This package parses and renders a simple tab-delimited format for features.
4             # It is simpler than GFF, but still has a lot of expressive power.
5             # See __END__ for the file format
6              
7             =head1 NAME
8              
9             Bio::Graphics::FeatureFile -- A set of Bio::Graphics features, stored in a file
10              
11             =head1 SYNOPSIS
12              
13             use Bio::Graphics::FeatureFile;
14             my $data = Bio::Graphics::FeatureFile->new(-file => 'features.txt');
15              
16              
17             # create a new panel and render contents of the file onto it
18             my $panel = $data->new_panel;
19             my $tracks_rendered = $data->render($panel);
20              
21             # or do it all in one step
22             my ($tracks_rendered,$panel) = $data->render;
23              
24             # for more control, render tracks individually
25             my @feature_types = $data->types;
26             for my $type (@feature_types) {
27             my $features = $data->features($type);
28             my %options = $data->style($type);
29             $panel->add_track($features,%options); # assuming we have a Bio::Graphics::Panel
30             }
31              
32             # get individual settings
33             my $est_fg_color = $data->setting(EST => 'fgcolor');
34              
35             # or create the FeatureFile by hand
36              
37             # add a type
38             $data->add_type(EST => {fgcolor=>'blue',height=>12});
39              
40             # add a feature
41             my $feature = Bio::Graphics::Feature->new(
42             # params
43             ); # or some other SeqI
44             $data->add_feature($feature=>'EST');
45              
46             =head1 DESCRIPTION
47              
48             The Bio::Graphics::FeatureFile module reads and parses files that
49             describe sequence features and their renderings. It accepts both GFF
50             format and a more human-friendly file format described below. Once a
51             FeatureFile object has been initialized, you can interrogate it for
52             its consistuent features and their settings, or render the entire file
53             onto a Bio::Graphics::Panel.
54              
55             This module is a precursor of Jason Stajich's
56             Bio::Annotation::Collection class, and fulfills a similar function of
57             storing a collection of sequence features. However, it also stores
58             rendering information about the features, and does not currently
59             follow the CollectionI interface.
60              
61             =head1 The File Format
62              
63             There are two types of entry in the file format: feature entries, and
64             formatting entries. They can occur in any order. See the Appendix
65             for a full example.
66              
67             =head2 Formatting Entries
68              
69             Formatting entries are in the form:
70              
71             [Stanza Name]
72             option1 = value1
73             option2 = value2
74             option3 = value3
75              
76             [Stanza Name 2]
77             option1 = value1
78             option2 = value2
79             ...
80              
81             There can be zero or more stanzas, each with a unique name. The names
82             can contain any character except the [] characters. Each stanza
83             consists of one or more option = value pairs, where the option and the
84             value are separated by an "=" sign and optional whitespace. Values can
85             be continued across multiple lines by indenting the continuation lines
86             by one or more spaces, as in:
87              
88             [Named Genes]
89             feature = gene
90             glyph = transcript2
91             description = These are genes that have been named
92             by the international commission on gene naming
93             (The Hague).
94              
95             Typically configuration stanzas will consist of several Bio::Graphics
96             formatting options. A -option=>$value pair passed to
97             Bio::Graphics::Panel->add_track() becomes a "option=value" pair in the
98             feature file.
99              
100             =head2 Feature Entries
101              
102             Feature entries can take several forms. At their simplest, they look
103             like this:
104              
105             Gene B0511.1 Chr1:516..11208
106              
107             This means that a feature of type "Gene" and name "B0511.1" occupies
108             the range between bases 516 and 11208 on a sequence entry named
109             Chr1. Columns are separated using whitespace (tabs or spaces).
110             Embedded whitespace can be escaped using quote marks or backslashes:
111              
112             Gene "My Favorite Gene" Chr1:516..11208
113              
114             =head2 Specifying Positions and Ranges
115              
116             A feature position is specified using a sequence ID (a genbank
117             accession number, a chromosome name, a contig, or any other meaningful
118             reference system, followed by a colon and a position range. Ranges are
119             two integers separated by double dots or the hyphen. Examples:
120             "Chr1:516..11208", "ctgA:1-5000". Negative coordinates are allowed, as
121             in "Chr1:-187..1000".
122              
123             A discontinuous range ("split location") uses commas to separate the
124             ranges. For example:
125              
126             Gene B0511.1 Chr1:516..619,3185..3294,10946..11208
127              
128             In the case of a split location, the sequence id only has to appear in
129             front of the first range.
130              
131             Alternatively, a split location can be indicated by repeating the
132             features type and name on multiple adjacent lines:
133              
134             Gene B0511.1 Chr1:516..619
135             Gene B0511.1 Chr1:3185..3294
136             Gene B0511.1 Chr1:10946..11208
137              
138             If all the locations are on the same reference sequence, you can
139             specify a default chromosome using a "reference=<seqid>":
140              
141             reference=Chr1
142             Gene B0511.1 516..619
143             Gene B0511.1 3185..3294
144             Gene B0511.1 10946..11208
145              
146             The default seqid is in effect until the next "reference" line
147             appears.
148              
149             =head2 Feature Tags
150              
151             Tags can be added to features by adding a fourth column consisting of
152             "tag=value" pairs:
153              
154             Gene B0511.1 Chr1:516..619,3185..3294 Note="Putative primase"
155              
156             Tags and their values take any form you want, and multiple tags can be
157             separated by semicolons. You can also repeat tags multiple times:
158              
159             Gene B0511.1 Chr1:516..619,3185..3294 GO_Term=GO:100;GO_Term=GO:2087
160              
161             Several tags have special meanings:
162              
163             Tag Meaning
164             --- -------
165              
166             Type The primary tag for a subfeature.
167             Score The score of a feature or subfeature.
168             Phase The phase of a feature or subfeature.
169             URL A URL to link to (via the Bio::Graphics library).
170             Note A note to attach to the feature for display by the Bio::Graphics library.
171              
172             For example, in the common case of an mRNA, you can use the "Type" tag
173             to distinguish the parts of the mRNA into UTR and CDS:
174              
175             mRNA B0511.1 Chr1:1..100 Type=UTR
176             mRNA B0511.1 Chr1:101..200,300..400,500..800 Type=CDS
177             mRNA B0511.1 Chr1:801..1000 Type=UTR
178              
179             The top level feature's primary tag will be "mRNA", and its subparts
180             will have types UTR and CDS as indicated. Additional tags that are
181             placed in the first line of the feature will be applied to the top
182             level. In this example, the note "Putative primase" will be applied to
183             the mRNA at the top level of the feature:
184              
185             mRNA B0511.1 Chr1:1..100 Type=UTR;Note="Putative primase"
186             mRNA B0511.1 Chr1:101..200,300..400,500..800 Type=CDS
187             mRNA B0511.1 Chr1:801..1000 Type=UTR
188              
189             =head2 Feature Groups
190              
191             Features can be grouped so that they are rendered by the "group"
192             glyph. To start a group, create a two-column feature entry showing
193             the group type and a name for the group. Follow this with a list of
194             feature entries with a blank type. For example:
195              
196             EST yk53c10
197             yk53c10.3 15000-15500,15700-15800
198             yk53c10.5 18892-19154
199              
200             This example is declaring that the ESTs named yk53c10.3 and yk53c10.5
201             belong to the same group named yk53c10.
202              
203             =head2 Comments
204              
205             Lines that begin with the # sign are treated as comments and
206             ignored. When a # sign appears within a line, everything to the right
207             of the symbol is also ignored, unless it looks like an HTML fragment or
208             an HTML color, e.g.:
209              
210             # this is ignored
211             [Example]
212             glyph = generic # this comment is ignored
213             bgcolor = #FF0000
214             link = http://www.google.com/search?q=$name#results
215              
216             Be careful, because the processing of # signs uses a regexp heuristic. To be safe,
217             always put a space after the # sign to make sure it is treated as a comment.
218              
219             =head2 The #include and #exec Directives
220              
221             The special comment "#include 'filename'" acts like the C preprocessor
222             directive and will insert the comments of a named file into the
223             position at which it occurs. Relative paths will be treated relative
224             to the file in which the #include occurs. Nested #include directives
225             (a #include located in a file that is itself an include file) are
226             #allowed. You may also use one of the shell wildcard characters * and
227             #? to include all matching files in a directory.
228              
229             The following are examples of valid #include directives:
230              
231             #include "/usr/local/share/my_directives.txt"
232             #include 'my_directives.txt'
233             #include chromosome3_features.gff3
234             #include gff.d/*.conf
235            
236             You can enclose the file path in single or double quotes as shown
237             above. If there are no spaces in the filename the quotes are optional.
238             The #include directive is case insensitive, allowing you to use
239             #INCLUDE or #Include if you prefer.
240              
241             Include file processing is not very smart and will not catch all
242             circular #include references. You have been warned!
243              
244             The special comment "#exec 'command'" will spawn a shell and
245             incorporate the output of the command into the configuration
246             file. This command will be executed quite frequently, so it is
247             suggested that any time-consuming processing that does not need to be
248             performed on the fly each time should be cached in a local file.
249              
250             =cut
251              
252 2     2   31595 use strict;
  2         4  
  2         79  
253 2     2   1699 use Bio::Graphics::Feature;
  2         8  
  2         99  
254 2     2   2576 use Bio::DB::GFF::Util::Rearrange;
  2         13524  
  2         178  
255 2     2   19 use Carp 'cluck','carp','croak';
  2         4  
  2         142  
256 2     2   1182 use IO::File;
  2         1138  
  2         319  
257 2     2   12 use File::Glob ':glob';
  2         4  
  2         489  
258 2     2   2107 use Text::ParseWords 'shellwords';
  2         3653  
  2         138  
259 2     2   3470 use Bio::DB::SeqFeature::Store;
  2         67876  
  2         132  
260 2     2   29 use File::Basename 'dirname';
  2         5  
  2         208  
261 2     2   13 use File::Spec;
  2         4  
  2         49  
262 2     2   13 use Cwd 'getcwd';
  2         5  
  2         198  
263              
264             # default colors for unconfigured features
265             my @COLORS = qw(cyan blue red yellow green wheat turquoise orange);
266              
267             # package variable which holds the limited set of libraries accessible
268             # from within the Safe::World container (please see the description of
269             # the -safe_world option).
270             # my $SAFE_LIB;
271              
272 2     2   38 use constant WIDTH => 600;
  2         5  
  2         324  
273 2     2   12 use constant MAX_REMAP => 100;
  2         5  
  2         758  
274              
275             =head2 METHODS
276              
277             =over 4
278              
279             =item $version = Bio::Graphics::FeatureFile-E<gt>version
280              
281             Return the version number -- needed for API checking by GBrowse
282              
283             =cut
284              
285 0     0 1 0 sub version { return 2 }
286              
287             =item $features = Bio::Graphics::FeatureFile-E<gt>new(@args)
288              
289             Create a new Bio::Graphics::FeatureFile using @args to initialize the
290             object. Arguments are -name=E<gt>value pairs:
291              
292             Argument Value
293             -------- -----
294              
295             -file Read data from a file path or filehandle. Use
296             "-" to read from standard input.
297              
298             -text Read data from a text scalar.
299              
300             -allow_whitespace If true, relax GFF2 and GFF3 parsing rules to allow
301             columns to be delimited by whitespace rather than
302             tabs.
303              
304             -map_coords Coderef containing a subroutine to use for remapping
305             all coordinates.
306              
307             -smart_features Flag indicating that the features created by this
308             module should be made aware of the FeatureFile
309             object by calling their configurator() method.
310              
311             -safe Indicates that the contents of this file is trusted.
312             Any option value that begins with the string "sub {"
313             or \&subname will be evaluated as a code reference.
314              
315             -safe_world If the -safe option is not set, and -safe_world
316             is set to a true value, then Bio::Graphics::FeatureFile
317             will evalute "sub {}" options in a L<Safe::World>
318             environment with minimum permissions. Subroutines
319             will be able to access and interrogate
320             Bio::DB::SeqFeature objects and perform basic Perl
321             operations, but will have no ability to load or
322             access other modules, to access the file system,
323             or to make system calls. This feature depends on
324             availability of the CPAN-installable L<Safe::World>
325             module.
326              
327             The -file and -text arguments are mutually exclusive, and -file will
328             supersede the other if both are present.
329              
330             -map_coords points to a coderef with the following signature:
331              
332             ($newref,[$start1,$end1],[$start2,$end2]....)
333             = coderef($ref,[$start1,$end1],[$start2,$end2]...)
334              
335             See the Bio::Graphics::Browser (part of the generic genome browser
336             package) for an illustration of how to use this to do wonderful stuff.
337              
338             The -smart_features flag is used by the generic genome browser to
339             provide features with a way to access the link-generation code. See
340             gbrowse for how this works.
341              
342             If the file is trusted, and there is an option named "init_code" in
343             the [GENERAL] section of the file, it will be evaluated as perl code
344             immediately after parsing. You can use this to declare global
345             variables and subroutines for use in option values.
346              
347             =cut
348              
349             # args array:
350             # -file => parse from a file (- allowed for ARGV)
351             # -text => parse from a text scalar
352             # -map_coords => code ref to do coordinate mapping
353             # called with ($ref,[$start1,$stop1],[$start2,$stop2]...)
354             # returns ($newref,$new_coord1,$new_coord2...)
355              
356             sub new {
357 1     1 1 52 shift->_new(@_);
358             }
359              
360             sub _new {
361 1     1   2 my $class = shift;
362 1         5 my %args = @_;
363 1         12 my $self = bless {
364             config => {},
365             features => {},
366             seenit => {},
367             types => [],
368             max => undef,
369             min => undef,
370             stat => [],
371             refs => {},
372             safe => undef,
373             safe_world => undef,
374             },$class;
375 1 50 33     7 $self->{coordinate_mapper} = $args{-map_coords}
376             if exists $args{-map_coords} && ref($args{-map_coords}) eq 'CODE';
377              
378 1 50       4 $self->smart_features($args{-smart_features}) if exists $args{-smart_features};
379 1 50       9 $self->{safe} = $args{-safe} if exists $args{-safe};
380 1 50       4 $self->safe_world(1) if $args{-safe_world};
381 1 50       4 $self->allow_whitespace(1) if $args{-allow_whitespace};
382              
383 1         5 $self->init_parse();
384              
385             # call with
386             # -file
387             # -text
388 1 50       3 if (my $file = $args{-file}) {
    0          
389 2     2   62 no strict 'refs';
  2         4  
  2         40480  
390 1 50       10 if (defined fileno($file)) { # a filehandle
    50          
391 0         0 $self->parse_fh($file);
392             } elsif ($file eq '-') {
393 0         0 $self->parse_argv();
394             } else {
395 1         5 $self->parse_file($file);
396             }
397             } elsif (my $text = $args{-text}) {
398 0         0 $self->parse_text($text);
399             }
400              
401 0         0 $self->finish_parse();
402 0         0 return $self;
403             }
404              
405             =item $features = Bio::Graphics::FeatureFile-E<gt>new_from_cache(@args)
406              
407             Like new() but caches the parsed file in /tmp/bio_graphics_ff_cache_*
408             (where * is the UID of the current user). This can speed up parsing
409             tremendously for files that have many includes.
410              
411             Note that the presence of an #exec statement always invalidates the
412             cache and causes a full parse.
413              
414             =cut
415              
416             sub new_from_cache {
417 0     0 1 0 my $self = shift;
418 0         0 my %args = @_;
419 0         0 my $has_libs;
420              
421 0 0       0 unless ($has_libs = defined &nfreeze) {
422 0         0 $has_libs = eval <<END;
423             use Storable 'lock_store','lock_retrieve';
424             use File::Path 'mkpath';
425             1;
426             END
427 0 0       0 warn "You need Storable to use new_from_cache(); returning uncached data" unless $has_libs;
428             }
429              
430 0         0 $Storable::Deparse = 1;
431 0         0 $Storable::Eval = 1;
432              
433 0 0 0     0 my $file = $has_libs && $args{-file} or return $self->_new(@_);
434 0         0 (my $name = $args{-file}) =~ s!/!_!g;
435 0         0 my $cachefile = $self->cachefile($name);
436 0 0 0     0 if (-e $cachefile && (stat(_))[9] >= $self->file_mtime($args{-file})) { # cache is valid
437             # if (-e $cachefile && -M $cachefile < 0) { # cache is valid
438 0         0 my $parsed_file = lock_retrieve($cachefile);
439 0 0       0 $parsed_file->initialize_code if $parsed_file->safe;
440 0         0 return $parsed_file;
441             } else {
442 0         0 mkpath(dirname($cachefile));
443 0         0 my $parsed = $self->_new(@_);
444 0         0 $parsed->initialize_code();
445 0         0 eval {lock_store($parsed,$cachefile)};
  0         0  
446 0 0       0 warn $@ if $@;
447 0         0 return $parsed;
448             }
449            
450             }
451              
452             sub cachedir {
453 0     0 0 0 my $self = shift;
454 0         0 my $uid = $<;
455 0         0 return File::Spec->catfile(File::Spec->tmpdir,"bio_graphics_ff_cache_${uid}");
456             }
457              
458             sub cachefile {
459 0     0 0 0 my $self = shift;
460 0         0 my $name = shift;
461 0         0 return File::Spec->catfile($self->cachedir,$name);
462             }
463              
464             =item $mtime = Bio::Graphics::FeatureFile->file_mtime($path)
465              
466             Return the modification time of the indicated feature file without performing a full parse. This
467             takes into account the various #include and #exec directives and returns the maximum mtime of
468             any of the included files. Any #exec directive will return the current time. This is
469             useful for caching the parsed data structure.
470              
471             =back
472              
473             =cut
474              
475             sub file_mtime {
476 0     0 1 0 my $self = shift;
477              
478 0         0 my $file = shift;
479 0         0 my $mtime = 0;
480              
481 0         0 for my $f (glob($file)) {
482 0 0       0 my $m = (stat($f))[9] or next;
483 0 0       0 $mtime = $m if $mtime < $m;
484 0 0       0 open my $fh,'<',$file or next;
485 0         0 my $cwd = getcwd();
486 0         0 chdir(dirname($file));
487              
488 0         0 local $_;
489 0         0 while (<$fh>) {
490 0 0       0 if (/^\#exec/) {
491 0         0 return time(); # now!
492             }
493 0 0       0 if (/^\#include\s+(.+)/i) { # #include directive
494 0         0 my ($include_file) = shellwords($1);
495 0         0 my $m = $self->file_mtime($include_file);
496 0 0       0 $mtime = $m if $mtime < $m;
497             }
498             }
499 0         0 chdir($cwd);
500             }
501              
502 0         0 return $mtime;
503             }
504              
505             sub file_list {
506 0     0 0 0 my $self = shift;
507 0         0 my @list = ();
508 0         0 my $file = shift;
509              
510 0         0 for my $f (glob($file)) {
511 0 0       0 open my $fh,'<',$file or next;
512 0         0 my $cwd = getcwd();
513 0         0 chdir(dirname($file));
514              
515              
516 0         0 while (<$fh>) {
517 0 0       0 if (/^\#include\s+(.+)/i) { # #include directive
518 0         0 my ($include_file) = shellwords($1);
519 0         0 my @files = glob($include_file);
520 0 0       0 @files ? @list = (@list,@files) : push(@list,$include_file);
521             }
522             }
523 0         0 chdir($cwd);
524             }
525              
526 0         0 return \@list;
527             }
528              
529             # render our features onto a panel using configuration data
530             # return the number of tracks inserted
531              
532             =over 4
533              
534             =item ($rendered,$panel,$tracks) = $features-E<gt>render([$panel, $position_to_insert, $options, $max_bump, $max_label, $selector])
535              
536             Render features in the data set onto the indicated
537             Bio::Graphics::Panel. If no panel is specified, creates one.
538              
539             All arguments are optional.
540              
541             $panel is a Bio::Graphics::Panel that has previously been created and
542             configured.
543              
544             $position_to_insert indicates the position at which to start inserting
545             new tracks. The last current track on the panel is assumed.
546              
547             $options is a scalar used to control automatic expansion of the
548             tracks. 0=auto, 1=compact, 2=expanded, 3=expand and label,
549             4=hyperexpand, 5=hyperexpand and label.
550              
551             $max_bump and $max_label indicate the maximum number of features
552             before bumping and labeling are turned off.
553              
554             $selector is a code ref that can be used to filter which features to
555             render. It receives a feature and should return true to include the
556             feature and false to exclude it.
557              
558             In a scalar context returns the number of tracks rendered. In a list
559             context, returns a three-element list containing the number of
560             features rendered, the created panel, and an array ref of all the
561             track objects created.
562              
563             Instead of a Bio::Graphics::Panel object, you can provide a hash
564             reference containing the arguments that you would pass to
565             Bio::Graphics::Panel->new(). For example, to render an SVG image, you
566             could do this:
567              
568             my ($tracks_rendered,$panel) = $data->render({-image_class=>'GD::SVG'});
569             print $panel->svg;
570              
571             =back
572              
573             =cut
574              
575             #"
576              
577             sub render {
578 0     0 1 0 my $self = shift;
579 0         0 my $panel = shift; # 8 arguments
580 0         0 my ($position_to_insert,
581             $options,
582             $max_bump,
583             $max_label,
584             $selector,
585             $range,
586             $override_options
587             ) = @_;
588 0         0 my %seenit;
589              
590 0 0 0     0 unless ($panel && UNIVERSAL::isa($panel,'Bio::Graphics::Panel')) {
591 0         0 $panel = $self->new_panel($panel);
592             }
593              
594             # count up number of tracks inserted
595 0         0 my @tracks;
596             my $color;
597 0         0 my @labels = $self->labels;
598              
599             # we need to add a dummy section for each type that isn't
600             # specifically configured
601 0   0     0 my %types = map {$_=>1
  0         0  
602             } map {
603 0         0 shellwords ($self->setting($_=>'feature')||$_) } @labels;
604 0         0 my %lc_types = map {lc($_)}%types;
  0         0  
605              
606 0   0     0 my @unconfigured_types = sort grep {!exists $lc_types{lc $_} &&
  0         0  
607             !exists $lc_types{lc $_->method}
608             } $self->types;
609              
610 0         0 my @configured_types = keys %types;
611              
612 0         0 my @labels_to_render = (@labels,@unconfigured_types);
613              
614 0         0 my @base_config = $self->style('general');
615              
616 0         0 my @pack_options = ();
617 0 0 0     0 if ($options && ref $options eq 'HASH') {
618 0         0 @pack_options = %$options;
619             } else {
620 0   0     0 $options ||= 0;
621 0 0       0 if ($options == 1) { # compact
    0          
    0          
    0          
    0          
622 0         0 push @pack_options,(-bump => 0,-label=>0);
623             } elsif ($options == 2) { #expanded
624 0         0 push @pack_options,(-bump=>1);
625             } elsif ($options == 3) { #expand and label
626 0         0 push @pack_options,(-bump=>1,-label=>1);
627             } elsif ($options == 4) { #hyperexpand
628 0         0 push @pack_options,(-bump => 2);
629             } elsif ($options == 5) { #hyperexpand and label
630 0         0 push @pack_options,(-bump => 2,-label=>1);
631             }
632             }
633              
634 0         0 for my $label (@labels_to_render) {
635              
636              
637 0   0     0 my @types = shellwords($self->setting($label=>'feature')||'');
638 0 0       0 @types = $label unless @types;
639              
640 0 0 0     0 next if defined $selector and !$selector->($self,$label);
641              
642 0 0       0 my @features = !$range ? grep {$self->_visible($_)} $self->features(\@types)
  0         0  
643             : $self->features(-types => \@types,
644             -seq_id => $range->seq_id,
645             -start => $range->start,
646             -end => $range->end
647             );
648 0 0       0 next unless @features; # suppress tracks for features that don't appear
649              
650             # fix up funky group hack
651 0 0       0 foreach (@features) {$_->primary_tag('group') if $_->has_tag('_ff_group')};
  0         0  
652 0         0 my $features = \@features;
653              
654 0         0 my @auto_bump;
655 0 0       0 push @auto_bump,(-bump => @$features < $max_bump) if defined $max_bump;
656 0 0       0 push @auto_bump,(-label => @$features < $max_label) if defined $max_label;
657              
658 0 0       0 my @more_arguments = $override_options ? @$override_options : ();
659              
660 0   0     0 my @config = ( -glyph => 'segments', # really generic
661             -bgcolor => $COLORS[$color++ % @COLORS],
662             -label => 1,
663             -description => 1,
664             -key => $features[0]->type || $label,
665             @auto_bump,
666             @base_config, # global
667             $self->style($label), # feature-specific
668             @pack_options,
669             @more_arguments,
670             );
671              
672 0 0       0 if (defined($position_to_insert)) {
673 0         0 push @tracks,$panel->insert_track($position_to_insert++,$features,@config);
674             } else {
675 0         0 push @tracks,$panel->add_track($features,@config);
676             }
677             }
678 0 0       0 return wantarray ? (scalar(@tracks),$panel,\@tracks) : scalar @tracks;
679             }
680              
681             sub _stat {
682 1     1   1 my $self = shift;
683 1         2 my $file = shift;
684 1 50       6 defined fileno($file) or return;
685 1 50       17 my @stat = stat($file) or return;
686 1 50 33     5 if ($self->{stat} && @{$self->{stat}}) { # merge #includes so that mtime etc are max age
  1         6  
687 0         0 for (8,9,10) {
688 0 0       0 $self->{stat}[$_] = $stat[$_] if $stat[$_] > $self->{stat}[$_];
689             }
690 0         0 $self->{stat}[7] += $stat[7];
691             } else {
692 1         3 $self->{stat} = \@stat;
693             }
694             }
695              
696             sub _visible {
697 0     0   0 my $self = shift;
698 0         0 my $feat = shift;
699 0         0 my $min = $self->min;
700 0         0 my $max = $self->max;
701 0   0     0 return $feat->start<=$max && $feat->end>=$min;
702             }
703              
704             =over 4
705              
706             =item $error = $features-E<gt>error([$error])
707              
708             Get/set the current error message.
709              
710             =back
711              
712             =cut
713              
714             sub error {
715 0     0 1 0 my $self = shift;
716 0         0 my $d = $self->{error};
717 0 0       0 $self->{error} = shift if @_;
718 0         0 $d;
719             }
720              
721             =over 4
722              
723             =item $smart_features = $features-E<gt>smart_features([$flag]
724              
725             Get/set the "smart_features" flag. If this is set, then any features
726             added to the featurefile object will have their configurator() method
727             called using the featurefile object as the argument.
728              
729             =back
730              
731             =cut
732              
733             sub smart_features {
734 0     0 1 0 my $self = shift;
735 0         0 my $d = $self->{smart_features};
736 0 0       0 $self->{smart_features} = shift if @_;
737 0         0 $d;
738             }
739              
740             sub parse_argv {
741 0     0 0 0 my $self = shift;
742 0         0 local $/ = "\n";
743 0         0 local $_;
744 0         0 while (<>) {
745 0         0 chomp;
746 0         0 $self->parse_line($_);
747             }
748             }
749              
750             sub parse_file {
751 1     1 0 2 my $self = shift;
752 1         1 my $file = shift;
753              
754 1         4 $file =~ s/(\s)/\\$1/g; # escape whitespace from glob expansion
755              
756 1         52 for my $f (glob($file)) {
757 1 50       9 my $fh = IO::File->new($f) or return;
758 1         104 my $cwd = getcwd();
759 1         94 chdir(dirname($f));
760 1         5 $self->parse_fh($fh);
761 0         0 chdir($cwd);
762             }
763             }
764              
765             sub parse_fh {
766 1     1 0 2 my $self = shift;
767 1         1 my $fh = shift;
768 1         5 $self->_stat($fh);
769 1         4 local $/ = "\n";
770 1         1 local $_;
771 1         21 while (<$fh>) {
772 5         8 chomp;
773 5 50       12 $self->parse_line($_) || last;
774             }
775             }
776              
777             sub parse_text {
778 0     0 0 0 my $self = shift;
779 0         0 my $text = shift;
780              
781 0         0 foreach (split m/\015?\012|\015\012?/,$text) {
782 0         0 $self->parse_line($_);
783             }
784             }
785              
786             sub parse_line {
787 5     5 0 7 my $self = shift;
788 5         7 my $line = shift;
789              
790 5         7 $line =~ s/\015//g; # get rid of carriage returns left over by MS-DOS/Windows systems
791 5         13 $line =~ s/\s+$//; # get rid of trailing whitespace
792              
793 5 50       13 if (/^#include\s+(.+)/i) { # #include directive
794 0         0 my ($include_file) = shellwords($1);
795             # detect some loops
796 0 0       0 croak "#include loop detected at $include_file"
797             if $self->{includes}{$include_file}++;
798 0         0 $self->parse_file($include_file);
799 0         0 return 1;
800             }
801              
802 5 50       14 if (/^#exec\s+(.+)/i) { # #exec directive
803 0         0 my ($command,@args) = shellwords($1);
804 0 0       0 open (my $fh,'-|') || exec $command,@args;
805 0         0 $self->parse_fh($fh);
806 0         0 return 1;
807             }
808              
809 5 50       12 return 1 if $line =~ /^\s*\#[^\#]?$/; # comment line
810              
811             # Are we in a configuration section or a data section?
812             # We start out in 'config' state, and are triggered to
813             # reenter config state whenever we see a /^\[ pattern (config section)
814 5         8 my $old_state = $self->{state};
815 5         11 my $new_state = $self->_state_transition($line);
816              
817 5 100       13 if ($new_state ne $old_state) {
818 1         2 delete $self->{current_config};
819 1         2 delete $self->{current_tag};
820             }
821              
822 5 100       11 if ($new_state eq 'config') {
    50          
823 4         10 $self->parse_config_line($line);
824             } elsif ($new_state eq 'data') {
825 1         4 $self->parse_data_line($line);
826             }
827 4         22 $self->{state} = $new_state;
828 4         17 1;
829             }
830              
831             sub _state_transition {
832 5     5   6 my $self = shift;
833 5         6 my $line = shift;
834 5         6 my $current_state = $self->{state};
835              
836 5 50       18 if ($current_state eq 'data') {
    50          
837 0 0       0 return 'config' if $line =~ m/^\s*\[([^\]]+)\]/; # start of a configuration section
838             }
839              
840             elsif ($current_state eq 'config') {
841 5 50       11 return 'data' if $line =~ /^\#\#(\w+)/; # GFF3 meta instruction
842 5 100       13 return 'data' if $line =~ /^reference\s*=/; # feature-file reference sequence directive
843            
844 4 50       12 return 'config' if $line =~ /^\s*$/; #empty line
845 4 100       13 return 'config' if $line =~ m/^\[(.+)\]/; # section beginning
846 3 50 33     25 return 'config' if $line =~ m/^[\w:\s]+=/
847             && $self->{current_config}; # configuration line
848 0 0 0     0 return 'config' if $line =~ m/^\s+(.+)/
849             && $self->{current_tag}; # continuation section
850 0 0       0 return 'config' if $line =~ /^\#/; # comment -not a meta
851 0         0 return 'data';
852             }
853 0         0 return $current_state;
854             }
855              
856             sub parse_config_line {
857 4     4 0 5 my $self = shift;
858 4         6 local $_ = shift;
859              
860             # strip right-column comments unless they look like colors or html fragments
861 4 50 33     31 s/\s*\#.*$// unless /\#[0-9a-f]{6,8}\s*$/i || /\w+\#\w+/ || /\w+\"*\s*\#\d+$/;
      33        
862              
863 4 50 33     33 if (/^\s+(.+)/ && $self->{current_tag}) { # configuration continuation line
    100          
    50          
    0          
864 0         0 my $value = $1;
865 0   0     0 my $cc = $self->{current_config} ||= 'general'; # in case no configuration named
866 0         0 $self->{config}{$cc}{$self->{current_tag}} .= ' ' . $value;
867             # respect newlines in code subs
868 0 0       0 $self->{config}{$cc}{$self->{current_tag}} .= "\n"
869             if $self->{config}{$cc}{$self->{current_tag}}=~ /^sub\s*\{/;
870 0         0 return 1;
871             }
872              
873             elsif (/^\[(.+)\]/) { # beginning of a configuration section
874 1         18 my $label = $1;
875 1 50       7 my $cc = $label =~ /^(general|default)$/i ? 'general' : $label; # normalize
876 1 50       6 push @{$self->{types}},$cc unless $cc eq 'general';
  0         0  
877 1         3 $self->{current_config} = $cc;
878 1         3 return 1;
879             }
880              
881             elsif (/^([\w: -]+?)\s*=\s*(.*)/) { # key value pair within a configuration section
882 3         7 my $tag = lc $1;
883 3   50     8 my $cc = $self->{current_config} ||= 'general'; # in case no configuration named
884 3 50       8 my $value = defined $2 ? $2 : '';
885 3         9 $self->{config}{$cc}{$tag} = $value;
886 3         6 $self->{current_tag} = $tag;
887 3         6 return 1;
888             }
889              
890              
891             elsif (/^$/) { # empty line
892             # no longer required -- new sections are indicated by the start of a [stanza]
893             # line and not by termination with a blank line
894             # undef $self->{current_tag};
895 0         0 return 1;
896             }
897              
898             }
899              
900             sub parse_data_line {
901 1     1 0 1 my $self = shift;
902 1         2 my $line = shift;
903 1 0 33     7 $self->{loader} ||= $self->_make_loader($line) or return;
904 0         0 $self->{loader}->load_line($line);
905             }
906              
907             sub _make_loader {
908 1     1   2 my $self = shift;
909 1         1 local $_ = shift;
910 1         4 my $db = $self->db;
911              
912 0         0 my $type;
913              
914             # we support gff2, gff3 and featurefile formats
915 0 0       0 if (/^\#\#gff-version\s+([23])/) {
    0          
916 0         0 $type = "Bio::DB::SeqFeature::Store::GFF$1Loader";
917             }
918             elsif (/^reference\s*=.+/) {
919 0         0 $type = "Bio::DB::SeqFeature::Store::FeatureFileLoader";
920             }
921             else {
922 0         0 my @tokens = shellwords($_);
923 0 0 0     0 unshift @tokens,'' if /^\s+/ and length $tokens[0];
924            
925 0 0 0     0 if (@tokens >=8 && $tokens[3]=~ /^-?\d+$/ && $tokens[4]=~ /^-?\d+$/) {
      0        
926 0         0 $type = 'Bio::DB::SeqFeature::Store::GFF3Loader';
927             }
928             else {
929 0         0 $type = 'Bio::DB::SeqFeature::Store::FeatureFileLoader';
930             }
931             }
932 0 0       0 eval "require $type"
933             unless $type->can('new');
934 0         0 my $loader = $type->new(-store => $db,
935             -map_coords => $self->{coordinate_mapper},
936             -index_subfeatures => 0,
937             );
938 0 0       0 eval {$loader->allow_whitespace(1)}
  0         0  
939             if $self->allow_whitespace; # gff2 and gff3 loaders allow this
940              
941 0 0       0 $loader->start_load() if $loader;
942 0         0 return $loader;
943             }
944              
945             sub db {
946 1     1 0 2 my $self = shift;
947 1   33     17 return $self->{db} ||= Bio::DB::SeqFeature::Store->new(-adaptor=>'memory',
948             -write => 1);
949             }
950              
951             =over 4
952              
953             =item $flat = $features-E<gt>allow_whitespace([$new_flag])
954              
955             If true, then GFF3 and GFF2 parsing is relaxed to allow whitespace to
956             delimit the columns. Default is false.
957              
958             =back
959              
960             =cut
961              
962             sub allow_whitespace {
963 0     0 1 0 my $self = shift;
964 0         0 my $d = $self->{allow_whitespace};
965 0 0       0 $self->{allow_whitespace} = shift if @_;
966 0         0 $d;
967             }
968              
969             =over 4
970              
971             =item $features-E<gt>add_feature($feature [=E<gt>$type])
972              
973             Add a new Bio::FeatureI object to the set. If $type is specified, the
974             object's primary_tag() will be set to that type. Otherwise, the method
975             will use the feature's existing primary_tag() to index and store the
976             feature.
977              
978             =back
979              
980             =cut
981              
982             # add a feature of given type to our list
983             # we use the primary_tag() method
984             sub add_feature {
985 0     0 1 0 my $self = shift;
986 0         0 my ($feature,$type) = @_;
987 0 0       0 $feature->configurator($self) if $self->smart_features;
988 0 0       0 $feature->primary_tag($type) if defined $type;
989 0         0 $self->db->store($feature);
990             }
991              
992              
993             =over 4
994              
995             =item $features-E<gt>add_type($type=E<gt>$hashref)
996              
997             Add a new feature type to the set. The type is a string, such as
998             "EST". The hashref is a set of key=E<gt>value pairs indicating options to
999             set on the type. Example:
1000              
1001             $features->add_type(EST => { glyph => 'generic', fgcolor => 'blue'})
1002              
1003             When a feature of type "EST" is rendered, it will use the generic
1004             glyph and have a foreground color of blue.
1005              
1006             =back
1007              
1008             =cut
1009              
1010             # Add a type to the list. Hash values are used for key/value pairs
1011             # in the configuration. Call as add_type($type,$configuration) where
1012             # $configuration is a hashref.
1013             sub add_type {
1014 0     0 1 0 my $self = shift;
1015 0         0 my ($type,$type_configuration) = @_;
1016 0 0       0 my $cc = $type =~ /^(general|default)$/i ? 'general' : $type; # normalize
1017 0 0 0     0 push @{$self->{types}},$cc unless $cc eq 'general' or $self->{config}{$cc};
  0         0  
1018 0 0       0 if (defined $type_configuration) {
1019 0         0 for my $tag (keys %$type_configuration) {
1020 0         0 $self->{config}{$cc}{lc $tag} = $type_configuration->{$tag};
1021             }
1022             }
1023             }
1024              
1025              
1026              
1027             =over 4
1028              
1029             =item $features-E<gt>set($type,$tag,$value)
1030              
1031             Change an individual option for a particular type. For example, this
1032             will change the foreground color of EST features to my favorite color:
1033              
1034             $features->set('EST',fgcolor=>'chartreuse')
1035              
1036             =back
1037              
1038             =cut
1039              
1040             # change configuration of a type. Call as set($type,$tag,$value)
1041             # $type will be added if not already there.
1042             sub set {
1043 0     0 1 0 my $self = shift;
1044 0 0       0 croak("Usage: \$featurefile->set(\$type,\$tag,\$value\n")
1045             unless @_ == 3;
1046 0         0 my ($type,$tag,$value) = @_;
1047 0 0       0 unless ($self->{config}{$type}) {
1048 0         0 return $self->add_type($type,{$tag=>$value});
1049             } else {
1050 0         0 $self->{config}{$type}{lc $tag} = $value;
1051             }
1052             }
1053              
1054             # break circular references
1055             sub finished {
1056 1     1 0 5 my $self = shift;
1057 1         209 delete $self->{features};
1058             }
1059              
1060             sub DESTROY {
1061 1     1   20858 my $self = shift;
1062 1         9 $self->finished(@_);
1063             # $self->{safe_context}->unlink_all_worlds
1064             # if $self->{safe_context};
1065             }
1066              
1067             =over 4
1068              
1069             =item $value = $features-E<gt>setting($stanza =E<gt> $option)
1070              
1071             In the two-element form, the setting() method returns the value of an
1072             option in the configuration stanza indicated by $stanza. For example:
1073              
1074             $value = $features->setting(general => 'height')
1075              
1076             will return the value of the "height" option in the [general] stanza.
1077              
1078             Call with one element to retrieve all the option names in a stanza:
1079              
1080             @options = $features->setting('general');
1081              
1082             Call with no elements to retrieve all stanza names:
1083              
1084             @stanzas = $features->setting;
1085              
1086             =back
1087              
1088             =cut
1089              
1090             sub setting {
1091 0     0 1 0 my $self = shift;
1092 0 0       0 if (@_ > 2) {
    0          
    0          
    0          
1093 0         0 $self->{config}->{$_[0]}{$_[1]} = $_[2];
1094             }
1095              
1096             elsif (@_ <= 1) {
1097 0         0 return $self->_setting(@_);
1098             }
1099              
1100             elsif ($self->safe) {
1101 0         0 return $self->code_setting(@_);
1102             }
1103              
1104             elsif ($self->safe_world) {
1105 0         0 return $self->safe_setting(@_);
1106             }
1107              
1108             else {
1109 0 0       0 $self->{code_check}++ && $self->clean_code(); # not safe; clean coderefs
1110 0         0 return $self->_setting(@_);
1111             }
1112             }
1113              
1114             =head2 fallback_setting()
1115              
1116             $value = $browser->setting(gene => 'fgcolor');
1117              
1118             Tries to find the setting for designated label (e.g. "gene") first. If
1119             this fails, looks in [TRACK DEFAULTS]. If this fails, looks in [GENERAL].
1120              
1121             =cut
1122              
1123             sub fallback_setting {
1124 0     0 1 0 my $self = shift;
1125 0         0 my ($label,$option) = @_;
1126 0         0 for my $key ($label,'TRACK DEFAULTS','GENERAL') {
1127 0         0 my $value = $self->setting($key,$option);
1128 0 0       0 return $value if defined $value;
1129             }
1130 0         0 return;
1131             }
1132              
1133              
1134             # return configuration information
1135             # arguments are ($type) => returns tags for type
1136             # ($type=>$tag) => returns values of tag on type
1137             # ($type=>$tag,$value) => sets value of tag
1138             sub _setting {
1139 0     0   0 my $self = shift;
1140 0 0       0 my $config = $self->{config} or return;
1141 0 0       0 return keys %{$config} unless @_;
  0         0  
1142 0 0       0 return keys %{$config->{$_[0]}} if @_ == 1;
  0         0  
1143 0 0 0     0 return $config->{$_[0]}{$_[1]} if @_ == 2 && defined $_[0] && exists $config->{$_[0]};
      0        
1144 0 0       0 return $config->{$_[0]}{$_[1]} = $_[2] if @_ > 2;
1145 0         0 return;
1146             }
1147              
1148              
1149             =over 4
1150              
1151             =item $value = $features-E<gt>code_setting($stanza=E<gt>$option);
1152              
1153             This works like setting() except that it is also able to evaluate code
1154             references. These are options whose values begin with the characters
1155             "sub {". In this case the value will be passed to an eval() and the
1156             resulting codereference returned. Use this with care!
1157              
1158             =back
1159              
1160             =cut
1161              
1162             sub code_setting {
1163 0     0 1 0 my $self = shift;
1164 0         0 my $section = shift;
1165 0         0 my $option = shift;
1166 0 0       0 croak 'Cannot call code_setting unless feature file is marked as safe'
1167             unless $self->safe;
1168              
1169 0         0 my $setting = $self->_setting($section=>$option);
1170 0 0       0 return unless defined $setting;
1171 0 0       0 return $setting if ref($setting) eq 'CODE';
1172 0 0       0 if ($setting =~ /^\\&([:\w]+)/) { # coderef in string form
    0          
1173 0         0 my $subroutine_name = $1;
1174 0         0 my $package = $self->base2package;
1175 0 0       0 my $codestring = $subroutine_name =~ /::/
1176             ? "\\&$subroutine_name"
1177             : "\\&${package}\:\:${subroutine_name}" ;
1178 0         0 my $coderef = eval $codestring;
1179 0 0       0 $self->_callback_complain($section,$option) if $@;
1180 0         0 $self->set($section,$option,$coderef);
1181 0         0 $self->set_callback_source($section,$option,$setting);
1182 0         0 return $coderef;
1183             }
1184             elsif ($setting =~ /^sub\s*(\(\$\$\))*\s*\{/) {
1185 0         0 my $package = $self->base2package;
1186 0         0 my $coderef = eval "package $package; $setting";
1187 0 0       0 $self->_callback_complain($section,$option) if $@;
1188 0         0 $self->set($section,$option,$coderef);
1189 0         0 $self->set_callback_source($section,$option,$setting);
1190 0         0 return $coderef;
1191             } else {
1192 0         0 return $setting;
1193             }
1194             }
1195              
1196             sub _callback_complain {
1197 0     0   0 my $self = shift;
1198 0         0 my ($section,$option) = @_;
1199 0         0 carp "An error occurred while evaluating the callback at section='$section', option='$option':\n => $@";
1200             }
1201              
1202             =over 4
1203              
1204             =item $value = $features-E<gt>safe_setting($stanza=E<gt>$option);
1205              
1206             This works like code_setting() except that it evaluates anonymous code
1207             references in a "Safe::World" compartment. This depends on the
1208             L<Safe::World> module being installed and the -safe_world option being
1209             set to true during object construction.
1210              
1211             =back
1212              
1213             =cut
1214              
1215             sub safe_setting {
1216 0     0 1 0 my $self = shift;
1217              
1218 0         0 my $section = shift;
1219 0         0 my $option = shift;
1220              
1221 0         0 my $setting = $self->_setting($section=>$option);
1222 0 0       0 return unless defined $setting;
1223 0 0       0 return $setting if ref($setting) eq 'CODE';
1224              
1225 0 0 0     0 if ($setting =~ /^sub\s*(\(\$\$\))*\s*\{/
1226             && (my $context = $self->{safe_context})) {
1227              
1228              
1229             # turn setting from an anonymous sub into a named
1230             # sub in the context namespace
1231              
1232             # create proper symbol name
1233 0         0 my $subname = "${section}_${option}";
1234 0         0 $subname =~ tr/a-zA-Z0-9_//cd;
1235 0         0 $subname =~ s/^\d+//;
1236              
1237 0         0 my ($prototype)
1238             = $setting =~ /^sub\s*\(\$\$\)/;
1239              
1240 0         0 $setting =~ s/^sub?.*?\{/sub $subname {/;
1241              
1242 0         0 my $success = $context->eval("$setting; 1");
1243 0 0       0 $self->_callback_complain($section,$option) if $@;
1244 0 0       0 unless ($success) {
1245 0         0 $self->set($section,$option,1); # if call fails, it becomes a generic "true" value
1246 0         0 return 1;
1247             }
1248              
1249             my $coderef = $prototype
1250 0     0   0 ? sub ($$) { return $context->call($subname,$_[0],$_[1]) }
1251             : sub {
1252 0 0   0   0 if ($_[-1]->isa('Bio::Graphics::Glyph')) {
1253 0         0 my %newglyph = %{$_[-1]};
  0         0  
1254 0         0 $_[-1] = bless \%newglyph,'Bio::Graphics::Glyph'; # make generic
1255             }
1256 0         0 $context->call($subname,@_);
1257 0 0       0 };
1258 0         0 $self->set($section,$option,$coderef);
1259 0         0 $self->set_callback_source($section,$option,$setting);
1260 0         0 return $coderef;
1261             }
1262             else {
1263 0         0 return $setting;
1264             }
1265             }
1266              
1267             =over 4
1268              
1269             =item $flag = $features-E<gt>safe([$flag]);
1270              
1271             This gets or sets and "safe" flag. If the safe flag is set, then
1272             calls to setting() will invoke code_setting(), allowing values that
1273             begin with the string "sub {" to be interpreted as anonymous
1274             subroutines. This is a potential security risk when used with
1275             untrusted files of features, so use it with care.
1276              
1277             =back
1278              
1279             =cut
1280              
1281             sub safe {
1282 0     0 1 0 my $self = shift;
1283 0         0 my $d = $self->{safe};
1284 0 0       0 $self->{safe} = shift if @_;
1285 0 0 0     0 $self->evaluate_coderefs if $self->{safe} && !$d;
1286 0         0 $d;
1287             }
1288              
1289             =over 4
1290              
1291             =item $flag = $features-E<gt>safe_world([$flag]);
1292              
1293             This gets or sets and "safe_world" flag. If the safe_world flag is
1294             set, then values that begin with the string "sub {" will be evaluated
1295             in a "safe" compartment that gives minimal access to the system. This
1296             is not a panacea for security risks, so use with care.
1297              
1298             =back
1299              
1300             =cut
1301              
1302             sub safe_world {
1303 0     0 1 0 my $self = shift;
1304 0         0 my $safe = shift;
1305              
1306 0 0 0     0 if ($safe && !$self->{safe_content}) { # initialise the thing
1307              
1308 0         0 eval "require Safe::World; 1";
1309 0 0       0 unless (Safe::World->can('new')) {
1310 0         0 warn "The Safe::World module is not installed on this system. Can't use it to evaluate codesubs in a safe context";
1311 0         0 return;
1312             }
1313            
1314 0 0       0 unless ($self->{safe_lib}) {
1315 0 0       0 $self->{safe_lib} = Safe::World->new(sharepack => ['Bio::DB::SeqFeature',
1316             'Bio::Graphics::Feature',
1317             'Bio::SeqFeature::Lite',
1318             'Bio::Graphics::Glyph',
1319             ]) or return;
1320              
1321 0 0       0 $self->{safe_lib}->eval(<<END) or return;
1322             use Bio::DB::SeqFeature;
1323             use Bio::Graphics::Feature;
1324             use Bio::SeqFeature::Lite;
1325             use Bio::Graphics::Glyph;
1326             1;
1327             END
1328             }
1329              
1330 0 0       0 $self->{safe_context} = Safe::World->new(root => $self->base2package) or return;
1331 0         0 $self->{safe_context}->op_permit_only(':default');
1332 0         0 $self->{safe_context}->link_world($self->{safe_lib});
1333 0         0 $self->{safe_world} = $safe;
1334             }
1335 0         0 return $self->{safe_world};
1336             }
1337              
1338             =over 4
1339              
1340             =item $features-E<gt>set_callback_source($type,$tag,$value)
1341              
1342             =item $features-E<gt>get_callback_source($type,$tag)
1343              
1344             These routines are used internally to get and set the source of a sub
1345             {} callback.
1346              
1347             =back
1348              
1349             =cut
1350              
1351             sub set_callback_source {
1352 0     0 1 0 my $self = shift;
1353 0         0 my ($type,$tag,$value) = @_;
1354 0         0 $self->{source}{$type}{lc $tag} = $value;
1355             }
1356              
1357             sub get_callback_source {
1358 0     0 1 0 my $self = shift;
1359 0         0 my ($type,$tag) = @_;
1360 0         0 $self->{source}{$type}{lc $tag};
1361             }
1362              
1363             =over 4
1364              
1365             =item @args = $features-E<gt>style($type)
1366              
1367             Given a feature type, returns a list of track configuration arguments
1368             suitable for suitable for passing to the
1369             Bio::Graphics::Panel-E<gt>add_track() method.
1370              
1371             =back
1372              
1373             =cut
1374              
1375             # turn configuration into a set of -name=>value pairs suitable for add_track()
1376             sub style {
1377 0     0 1 0 my $self = shift;
1378 0         0 my $type = shift;
1379              
1380 0 0       0 my $config = $self->{config} or return;
1381 0         0 my $hashref = $config->{$type};
1382 0 0       0 unless ($hashref) {
1383 0         0 $type =~ s/:.+$//;
1384 0 0       0 $hashref = $config->{$type} or return;
1385             }
1386              
1387 0         0 return map {("-$_" => $hashref->{$_})} keys %$hashref;
  0         0  
1388             }
1389              
1390              
1391             =over 4
1392              
1393             =item $glyph = $features-E<gt>glyph($type);
1394              
1395             Return the name of the glyph corresponding to the given type (same as
1396             $features-E<gt>setting($type=E<gt>'glyph')).
1397              
1398             =back
1399              
1400             =cut
1401              
1402             # retrieve just the glyph part of the configuration
1403             sub glyph {
1404 0     0 1 0 my $self = shift;
1405 0         0 my $type = shift;
1406 0 0       0 my $config = $self->{config} or return;
1407 0 0       0 my $hashref = $config->{$type} or return;
1408 0         0 return $hashref->{glyph};
1409             }
1410              
1411              
1412             =over 4
1413              
1414             =item @types = $features-E<gt>configured_types()
1415              
1416             Return a list of all the feature types currently known to the feature
1417             file set. Roughly equivalent to:
1418              
1419             @types = grep {$_ ne 'general'} $features->setting;
1420              
1421             =back
1422              
1423             =cut
1424              
1425             # return list of configured types, in proper order
1426             sub configured_types {
1427 0     0 1 0 my $self = shift;
1428 0 0       0 my $types = $self->{types} or return;
1429 0         0 return @$types;
1430             }
1431              
1432             sub labels {
1433 0     0 0 0 return shift->configured_types;
1434             }
1435              
1436             =over 4
1437              
1438             =item @types = $features-E<gt>types()
1439              
1440             This is similar to the previous method, but will return *all* feature
1441             types, including those that are not configured with a stanza.
1442              
1443             =back
1444              
1445             =cut
1446              
1447             sub types {
1448 0     0 1 0 my $self = shift;
1449 0         0 my $db = $self->db;
1450 0         0 $self->_patch_old_bioperl;
1451 0         0 return $self->db->types;
1452             }
1453              
1454             sub _patch_old_bioperl {
1455 0     0   0 my $self = shift;
1456 0 0 0     0 if ($Bio::Root::Version::VERSION >= 1.0069 &&
1457             $Bio::Root::Version::VERSION <= 1.006901
1458             ) { # bad version!
1459 0         0 local $^W=0;
1460             *Bio::DB::SeqFeature::Store::memory::types = sub {
1461 0     0   0 my $self = shift;
1462 0 0       0 eval "require Bio::DB::GFF::Typename" unless Bio::DB::GFF::Typename->can('new');
1463 0         0 my @types;
1464 0         0 for my $primary_tag ( keys %{$$self{_index}{type}} ) {
  0         0  
1465 0         0 for my $source_tag ( keys %{$$self{_index}{type}{$primary_tag}} ) {
  0         0  
1466 0         0 push @types, Bio::DB::GFF::Typename->new($primary_tag,$source_tag);
1467             }
1468             }
1469 0         0 return @types;
1470             }
1471 0         0 }
1472             }
1473              
1474             =over 4
1475              
1476             =item $features = $features-E<gt>features($type)
1477              
1478             Return a list of all the feature types of type "$type". If the
1479             featurefile object was created by parsing a file or text scalar, then
1480             the features will be of type Bio::Graphics::Feature (which follow the
1481             Bio::FeatureI interface). Otherwise the list will contain objects of
1482             whatever type you added with calls to add_feature().
1483              
1484             Two APIs:
1485              
1486             1) original API:
1487              
1488             # Reference to an array of all features of type "$type"
1489             $features = $features-E<gt>features($type)
1490              
1491             # Reference to an array of all features of all types
1492             $features = $features-E<gt>features()
1493              
1494             # A list when called in a list context
1495             @features = $features-E<gt>features()
1496              
1497             2) Bio::Das::SegmentI API:
1498              
1499             @features = $features-E<gt>features(-type=>['list','of','types']);
1500              
1501             # variants
1502             $features = $features-E<gt>features(-type=>['list','of','types']);
1503             $features = $features-E<gt>features(-type=>'a type');
1504             $iterator = $features-E<gt>features(-type=>'a type',-iterator=>1);
1505              
1506             $iterator = $features-E<gt>features(-type=>'a type',-seq_id=>$id,-start=>$start,-end=>$end);
1507              
1508             =back
1509              
1510             =cut
1511              
1512             # return features
1513             sub features {
1514 0     0 1 0 my $self = shift;
1515 0 0 0     0 my ($types,$iterator,$seq_id,$start,$end,@rest) = defined($_[0] && $_[0]=~/^-/)
1516             ? rearrange([['TYPE','TYPES'],'ITERATOR','SEQ_ID','START','END'],@_) : (\@_);
1517              
1518 0 0 0     0 $types = [$types] if $types && !ref($types);
1519 0 0 0     0 my @args = $types && @$types ? (-type=>$types) : ();
1520              
1521 0 0       0 push @args,(-seq_id => $seq_id) if $seq_id;
1522 0 0       0 push @args,(-start => $start) if defined $start;
1523 0 0       0 push @args,(-end => $end) if defined $end;
1524              
1525 0         0 my $db = $self->db;
1526              
1527 0 0       0 if ($iterator) {
1528 0         0 return $db->get_seq_stream(@args);
1529             } else {
1530 0         0 my @f = $db->features(@args);
1531 0 0       0 return wantarray ? @f : \@f;
1532             }
1533             }
1534              
1535              
1536              
1537             =over 4
1538              
1539             =item @features = $features-E<gt>features($type)
1540              
1541             Return a list of all the feature types of type "$type". If the
1542             featurefile object was created by parsing a file or text scalar, then
1543             the features will be of type Bio::Graphics::Feature (which follow the
1544             Bio::FeatureI interface). Otherwise the list will contain objects of
1545             whatever type you added with calls to add_feature().
1546              
1547             =back
1548              
1549             =cut
1550              
1551             sub make_strand {
1552 0     0 0 0 local $^W = 0;
1553 0 0 0     0 return +1 if $_[0] =~ /^\+/ || $_[0] > 0;
1554 0 0 0     0 return -1 if $_[0] =~ /^\-/ || $_[0] < 0;
1555 0         0 return 0;
1556             }
1557              
1558             =head2 get_seq_stream
1559              
1560             Title : get_seq_stream
1561             Usage : $stream = $s->get_seq_stream(@args)
1562             Function: get a stream of features that overlap this segment
1563             Returns : a Bio::SeqIO::Stream-compliant stream
1564             Args : see below
1565             Status : Public
1566              
1567             This is the same as feature_stream(), and is provided for Bioperl
1568             compatibility. Use like this:
1569              
1570             $stream = $s->get_seq_stream('exon');
1571             while (my $exon = $stream->next_seq) {
1572             print $exon->start,"\n";
1573             }
1574              
1575             =cut
1576              
1577             sub get_seq_stream {
1578 0     0 1 0 my $self = shift;
1579 0         0 local $^W = 0;
1580 0 0       0 my @args = $_[0] =~ /^-/ ? (@_,-iterator=>1) : (-types=>\@_,-iterator=>1);
1581 0         0 $self->features(@args);
1582             }
1583              
1584             =head2 get_feature_by_name
1585              
1586             Usage : $db->get_feature_by_name(-name => $name)
1587             Function: fetch features by their name
1588             Returns : a list of Bio::DB::GFF::Feature objects
1589             Args : the name of the desired feature
1590             Status : public
1591              
1592             This method can be used to fetch a named feature from the file.
1593              
1594             The full syntax is as follows. Features can be filtered by
1595             their reference, start and end positions
1596              
1597             @f = $db->get_feature_by_name(-name => $name,
1598             -ref => $sequence_name,
1599             -start => $start,
1600             -end => $end);
1601              
1602             This method may return zero, one, or several Bio::Graphics::Feature
1603             objects.
1604              
1605             =cut
1606              
1607             sub get_feature_by_name {
1608 0     0 1 0 my $self = shift;
1609 0         0 my ($name,$ref,$start,$end) = rearrange(['NAME','REF','START','END'],@_);
1610 0         0 my @args;
1611 0 0       0 push @args,(-name => $name) if defined $name;
1612 0 0       0 push @args,(-seq_id => $ref) if defined $ref;
1613 0 0       0 push @args,(-start => $start)if defined $start;
1614 0 0       0 push @args,(-end => $end) if defined $end;
1615 0         0 return $self->db->features(@args);
1616             }
1617              
1618 0     0 0 0 sub get_features_by_name { shift->get_feature_by_name(@_) }
1619              
1620             =head2 search_notes
1621              
1622             Title : search_notes
1623             Usage : @search_results = $db->search_notes("full text search string",$limit)
1624             Function: Search the notes for a text string
1625             Returns : array of results
1626             Args : full text search string, and an optional row limit
1627             Status : public
1628              
1629             Each row of the returned array is a arrayref containing the following fields:
1630              
1631             column 1 Display name of the feature
1632             column 2 The text of the note
1633             column 3 A relevance score.
1634              
1635             =cut
1636              
1637             sub search_notes {
1638 0     0 1 0 my $self = shift;
1639 0         0 return $self->db->search_notes(@_);
1640             }
1641              
1642              
1643             =head2 get_feature_stream(), top_SeqFeatures(), all_SeqFeatures()
1644              
1645             Provided for compatibility with older BioPerl and/or Bio::DB::GFF
1646             APIs.
1647              
1648             =cut
1649              
1650             *get_feature_stream = \&get_seq_stream;
1651             *top_SeqFeatures = *all_SeqFeatures = \&features;
1652              
1653              
1654             =over 4
1655              
1656             =item @refs = $features-E<gt>refs
1657              
1658             Return the list of reference sequences referred to by this data file.
1659              
1660             =back
1661              
1662             =cut
1663              
1664             sub refs {
1665 0     0 1 0 my $self = shift;
1666 0 0       0 my $refs = $self->{refs} or return;
1667 0         0 keys %$refs;
1668             }
1669              
1670             =over 4
1671              
1672             =item $min = $features-E<gt>min
1673              
1674             Return the minimum coordinate of the leftmost feature in the data set.
1675              
1676             =back
1677              
1678             =cut
1679              
1680             sub min {
1681 0     0 1 0 my $self = shift;
1682 0         0 $self->_min_max();
1683 0         0 $self->{min};
1684             }
1685              
1686             =over 4
1687              
1688             =item $max = $features-E<gt>max
1689              
1690             Return the maximum coordinate of the rightmost feature in the data set.
1691              
1692             =back
1693              
1694             =cut
1695              
1696             sub max {
1697 0     0 1 0 my $self = shift;
1698 0         0 $self->_min_max();
1699 0         0 $self->{max};
1700             }
1701              
1702             sub _min_max {
1703 0     0   0 my $self = shift;
1704 0 0 0     0 return if defined $self->{min} and defined $self->{max};
1705              
1706 0         0 my ($min,$max);
1707 0 0       0 if (my $bases = $self->setting(general=>'bases')) {
1708 0         0 ($min,$max) = $bases =~ /^(-?\d+)(?:\.\.|-)(-?\d+)/;
1709             }
1710              
1711 0 0       0 if (!defined $min) {
1712             # otherwise sort through the features
1713 0         0 my $fs = $self->get_seq_stream;
1714 0         0 while (my $f = $fs->next_seq) {
1715 0 0 0     0 $min = $f->start if !defined $min or $min > $f->start;
1716 0 0 0     0 $max = $f->end if !defined $max or $max < $f->start;
1717             }
1718             }
1719              
1720 0         0 @{$self}{'min','max'} = ($min,$max);
  0         0  
1721             }
1722              
1723             sub init_parse {
1724 1     1 0 3 my $s = shift;
1725              
1726 1         2 $s->{max} = $s->{min} = undef;
1727 1         3 $s->{types} = [];
1728 1         2 $s->{features} = {};
1729 1         2 $s->{config} = {};
1730 1         3 $s->{loader} = undef;
1731 1         2 $s->{state} = 'config';
1732 1         3 $s->{feature_count}= 0;
1733             }
1734              
1735             sub finish_parse {
1736 0     0 0   my $s = shift;
1737 0 0         if ($s->safe) {
    0          
1738 0           $s->initialize_code;
1739 0           $s->evaluate_coderefs;
1740             }
1741             elsif ($s->safe_world) {
1742 0           $s->evaluate_safecoderefs;
1743             }
1744 0 0         $s->{loader}->finish_load() if $s->{loader};
1745 0           $s->{loader} = undef;
1746 0           $s->{state} = 'config';
1747             }
1748              
1749             sub evaluate_coderefs {
1750 0     0 0   my $self = shift;
1751 0           for my $s ($self->_setting) {
1752 0           for my $o ($self->_setting($s)) {
1753 0           $self->code_setting($s,$o);
1754             }
1755             }
1756             }
1757             sub evaluate_safecoderefs {
1758 0     0 0   my $self = shift;
1759 0           for my $s ($self->_setting) {
1760 0           for my $o ($self->_setting($s)) {
1761 0           $self->safe_setting($s,$o);
1762             }
1763             }
1764             }
1765              
1766             sub clean_code {
1767 0     0 0   my $self = shift;
1768 0           for my $s ($self->_setting) {
1769 0           for my $o ($self->_setting($s)) {
1770 0 0         $self->_setting($s,$o,1) if
1771             $self->_setting($s,$o) =~ /\Asub\s*{/;
1772             }
1773             }
1774             }
1775              
1776             sub initialize_code {
1777 0     0 0   my $self = shift;
1778 0           my $package = $self->base2package;
1779 0 0         my $init_code = $self->_setting(general => 'init_code') or return;
1780 0           my $code = "package $package; $init_code; 1;";
1781 0           eval $code;
1782 0 0         $self->_callback_complain(general=>'init_code') if $@;
1783             }
1784              
1785             sub base2package {
1786 0     0 0   my $self = shift;
1787 0 0         return $self->{base2package} if exists $self->{base2package};
1788 0           my $rand = int rand(1000000);
1789 0           return $self->{base2package} = "Bio::Graphics::FeatureFile::CallBack::P$rand";
1790             }
1791              
1792             sub split_group {
1793 0     0 0   my $self = shift;
1794 0   0       my $gff = $self->{gff} ||= Bio::DB::GFF->new(-adaptor=>'memory');
1795 0           return $gff->split_group(shift, $self->{gff_version} > 2);
1796             }
1797              
1798             # create a panel if needed
1799             sub new_panel {
1800 0     0 0   my $self = shift;
1801 0           my $options = shift;
1802              
1803 0 0         eval "require Bio::Graphics::Panel" unless Bio::Graphics::Panel->can('new');
1804              
1805             # general configuration of the image here
1806 0   0       my $width = $self->setting(general => 'pixels')
1807             || $self->setting(general => 'width')
1808             || WIDTH;
1809              
1810 0           my ($start,$stop);
1811 0           my $range_expr = '(-?\d+)(?:-|\.\.)(-?\d+)';
1812              
1813 0 0         if (my $bases = $self->setting(general => 'bases')) {
1814 0           ($start,$stop) = $bases =~ /([\d-]+)(?:-|\.\.)([\d-]+)/;
1815             }
1816              
1817 0 0 0       if (!defined $start || !defined $stop) {
1818 0 0         $start = $self->min unless defined $start;
1819 0 0         $stop = $self->max unless defined $stop;
1820             }
1821              
1822 0           my $new_segment = Bio::Graphics::Feature->new(-start=>$start,-stop=>$stop);
1823 0 0 0       my @panel_options = %$options if $options && ref $options eq 'HASH';
1824 0           my $panel = Bio::Graphics::Panel->new(-segment => $new_segment,
1825             -width => $width,
1826             -key_style => 'between',
1827             $self->style('general'),
1828             @panel_options
1829             );
1830 0           $panel;
1831             }
1832              
1833             =over 4
1834              
1835             =item $mtime = $features-E<gt>mtime
1836              
1837             =item $atime = $features-E<gt>atime
1838              
1839             =item $ctime = $features-E<gt>ctime
1840              
1841             =item $size = $features-E<gt>size
1842              
1843             Returns stat() information about the data file, for featurefile
1844             objects created using the -file option. Size is in bytes. mtime,
1845             atime, and ctime are in seconds since the epoch.
1846              
1847             =back
1848              
1849             =cut
1850              
1851             sub mtime {
1852 0     0 1   my $self = shift;
1853 0   0       my $d = $self->{m_time} || $self->{stat}->[9];
1854 0 0         $self->{m_time} = shift if @_;
1855 0           $d;
1856             }
1857 0     0 1   sub atime { shift->{stat}->[8]; }
1858 0     0 1   sub ctime { shift->{stat}->[10]; }
1859 0     0 1   sub size { shift->{stat}->[7]; }
1860              
1861             =over 4
1862              
1863             =item $label = $features-E<gt>feature2label($feature)
1864              
1865             Given a feature, determines the configuration stanza that bests
1866             describes it. Uses the feature's type() method if it has it (DasI
1867             interface) or its primary_tag() method otherwise.
1868              
1869             =back
1870              
1871             =cut
1872              
1873             sub feature2label {
1874 0     0 1   my $self = shift;
1875 0           my $feature = shift;
1876 0 0         my $type = $feature->can('type') ? $feature->type
1877             : $feature->primary_tag;
1878 0 0         $type or return;
1879 0           (my $basetype = $type) =~ s/:.+$//;
1880 0           my @labels = $self->type2label($type);
1881 0 0         @labels = $self->type2label($basetype) unless @labels;
1882 0 0         @labels = ($type) unless @labels;
1883 0 0         wantarray ? @labels : $labels[0];
1884             }
1885              
1886             =over 4
1887              
1888             =item $link = $features-E<gt>link_pattern($linkrule,$feature,$panel)
1889              
1890             Given a feature, tries to generate a URL to link out from it. This
1891             uses the 'link' option, if one is present. This method is a
1892             convenience for the generic genome browser.
1893              
1894             =back
1895              
1896             =cut
1897              
1898             sub link_pattern {
1899 0     0 1   my $self = shift;
1900 0           my ($linkrule,$feature,$panel,$dont_escape) = @_;
1901              
1902 0   0       $panel ||= 'Bio::Graphics::Panel';
1903              
1904 0 0 0       if (ref($linkrule) && ref($linkrule) eq 'CODE') {
1905 0           my $val = eval {$linkrule->($feature,$panel)};
  0            
1906 0 0         $self->_callback_complain(none=>"linkrule for $feature") if $@;
1907 0           return $val;
1908             }
1909              
1910 0 0         require CGI unless defined &CGI::escape;
1911 0 0   0     my $escape_method = $dont_escape ? sub {shift} : \&CGI::escape;
  0            
1912              
1913 0           my $n;
1914 0   0       $linkrule ||= ''; # prevent uninit warning
1915 0 0         my $seq_id = $feature->can('seq_id') ? $feature->seq_id() : $feature->location->seq_id();
1916 0   0       $seq_id ||= $feature->seq_id; #fallback
1917 0           $linkrule =~ s!\$(\w+)!
1918             $escape_method->(
1919             $1 eq 'ref' ? (($n = $seq_id) && "$n") || ''
1920             : $1 eq 'name' ? (($n = $feature->display_name) && "$n") || ''
1921             : $1 eq 'class' ? eval {$feature->class} || ''
1922             : $1 eq 'type' ? eval {$feature->method} || $feature->primary_tag || ''
1923             : $1 eq 'method' ? eval {$feature->method} || $feature->primary_tag || ''
1924             : $1 eq 'source' ? eval {$feature->source} || $feature->source_tag || ''
1925             : $1 =~ 'seq_?id' ? eval{$feature->seq_id} || eval{$feature->location->seq_id} || ''
1926             : $1 eq 'start' ? $feature->start || ''
1927             : $1 eq 'end' ? $feature->end || ''
1928             : $1 eq 'stop' ? $feature->end || ''
1929             : $1 eq 'segstart' ? $panel->start || ''
1930             : $1 eq 'segend' ? $panel->end || ''
1931             : $1 eq 'length' ? $feature->length || 0
1932             : $1 eq 'description' ? eval {join '',$feature->notes} || ''
1933 0 0 0       : $1 eq 'id' ? eval {$feature->feature_id} || eval {$feature->primary_id} || ''
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
1934             : '$'.$1
1935             )
1936             !exg;
1937 0           return $linkrule;
1938             }
1939              
1940             sub make_link {
1941 0     0 0   my $self = shift;
1942 0           my ($feature,$panel) = @_;
1943              
1944 0           my ($linkrule) = $feature->each_tag_value('link');
1945              
1946 0 0         unless ($linkrule) {
1947 0           for my $label ($self->feature2label($feature)) {
1948 0   0       $linkrule ||= $self->setting($label,'link');
1949 0   0       $linkrule ||= $self->setting(general=>'link');
1950             }
1951             }
1952 0           return $self->link_pattern($linkrule,$feature,$panel);
1953             }
1954              
1955             sub make_title {
1956 0     0 0   my $self = shift;
1957 0           my $feature = shift;
1958              
1959 0           for my $label ($self->feature2label($feature)) {
1960 0           my $linkrule = $self->setting($label,'title');
1961 0   0       $linkrule ||= $self->setting(general=>'title');
1962 0 0         next unless $linkrule;
1963 0           return $self->link_pattern($linkrule,$feature,undef,1);
1964             }
1965              
1966 0   0       my $method = eval {$feature->method} || $feature->primary_tag;
1967 0 0         my $seqid = $feature->can('seq_id') ? $feature->seq_id : $feature->location->seq_id;
1968 0           my $title = eval {
1969 0 0 0       if ($feature->can('target') && (my $target = $feature->target)) {
1970 0 0         join (' ',
1971             $method,
1972             (defined $seqid ? "$seqid:" : '').
1973             $feature->start."..".$feature->end,
1974             $feature->target.':'.
1975             $feature->target->start."..".$feature->target->end);
1976             } else {
1977 0 0 0       join(' ',
    0 0        
1978             $method,
1979             $feature->can('display_name') ? $feature->display_name : $feature->info,
1980             (defined $seqid ? "$seqid:" : '').
1981             ($feature->start||'?')."..".($feature->end||'?')
1982             );
1983             }
1984             };
1985 0 0         warn $@ if $@;
1986 0           $title;
1987             }
1988              
1989             # given a feature type, return its label(s)
1990             sub type2label {
1991 0     0 0   my $self = shift;
1992 0           my $type = shift;
1993 0   0       $self->{_type2label} ||= $self->invert_types;
1994 0           my @labels = keys %{$self->{_type2label}{lc $type}};
  0            
1995 0 0         wantarray ? @labels : $labels[0]
1996             }
1997              
1998             sub invert_types {
1999 0     0 0   my $self = shift;
2000 0 0         my $config = $self->{config} or return;
2001 0           my %inverted;
2002 0           for my $label (keys %{$config}) {
  0            
2003 0   0       my $feature = $config->{$label}{feature} || $label;
2004 0   0       foreach (shellwords($feature||'')) {
2005 0           $inverted{lc $_}{$label}++;
2006             }
2007             }
2008 0           \%inverted;
2009             }
2010              
2011             =over 4
2012              
2013             =item $citation = $features-E<gt>citation($feature)
2014              
2015             Given a feature, tries to generate a citation for it, using the
2016             "citation" option if one is present. This method is a convenience for
2017             the generic genome browser.
2018              
2019             =back
2020              
2021             =cut
2022              
2023             # This routine returns the "citation" field. It is here in order to simplify the logic
2024             # a bit in the generic browser
2025             sub citation {
2026 0     0 1   my $self = shift;
2027 0   0       my $feature = shift || 'general';
2028 0           return $self->setting($feature=>'citation');
2029             }
2030              
2031             =over 4
2032              
2033             =item $name = $features-E<gt>name([$feature])
2034              
2035             Get/set the name of this feature set. This is a convenience method
2036             useful for keeping track of multiple feature sets.
2037              
2038             =back
2039              
2040             =cut
2041              
2042             # give this feature file a nickname
2043             sub name {
2044 0     0 1   my $self = shift;
2045 0           my $d = $self->{name};
2046 0 0         $self->{name} = shift if @_;
2047 0           $d;
2048             }
2049              
2050             1;
2051              
2052             __END__
2053              
2054             =head1 Appendix -- Sample Feature File
2055              
2056             # file begins
2057             [general]
2058             pixels = 1024
2059             bases = 1-20000
2060             reference = Contig41
2061             height = 12
2062              
2063             [mRNA]
2064             glyph = gene
2065             key = Spliced genes
2066              
2067             [Cosmid]
2068             glyph = segments
2069             fgcolor = blue
2070             key = C. elegans conserved regions
2071              
2072             [EST]
2073             glyph = segments
2074             bgcolor= yellow
2075             connector = dashed
2076             height = 5;
2077              
2078             [FGENESH]
2079             glyph = transcript2
2080             bgcolor = green
2081             description = 1
2082              
2083             mRNA B0511.1 Chr1:1..100 Type=UTR;Note="putative primase"
2084             mRNA B0511.1 Chr1:101..200,300..400,500..800 Type=CDS
2085             mRNA B0511.1 Chr1:801..1000 Type=UTR
2086              
2087             reference = Chr3
2088             Cosmid B0511 516..619
2089             Cosmid B0511 3185..3294
2090             Cosmid B0511 10946..11208
2091             Cosmid B0511 13126..13511
2092             Cosmid B0511 11394..11539
2093             EST yk260e10.5 15569..15724
2094             EST yk672a12.5 537..618,3187..3294
2095             EST yk595e6.5 552..618
2096             EST yk595e6.5 3187..3294
2097             EST yk846e07.3 11015..11208
2098             EST yk53c10
2099             yk53c10.3 15000..15500,15700..15800
2100             yk53c10.5 18892..19154
2101             EST yk53c10.5 16032..16105
2102             SwissProt PECANEX 13153-13656 Note="Swedish fish"
2103             FGENESH "Predicted gene 1" 1-205,518-616,661-735,3187-3365,3436-3846 "Pfam domain"
2104             # file ends
2105              
2106             =head1 SEE ALSO
2107              
2108             L<Bio::Graphics::Panel>,
2109             L<Bio::Graphics::Glyph>,
2110             L<Bio::DB::SeqFeature::Store::FeatureFileLoader>,
2111             L<Bio::Graphics::Feature>,
2112             L<Bio::Graphics::FeatureFile>
2113              
2114             =head1 AUTHOR
2115              
2116             Lincoln Stein E<lt>lstein@cshl.orgE<gt>.
2117              
2118             Copyright (c) 2001 Cold Spring Harbor Laboratory
2119              
2120             This library is free software; you can redistribute it and/or modify
2121             it under the same terms as Perl itself. See DISCLAIMER.txt for
2122             disclaimers of warranty.
2123              
2124             =cut
2125              
2126              
2127