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=": |
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
|
|
62701
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
85
|
|
253
|
2
|
|
|
2
|
|
1288
|
use Bio::Graphics::Feature; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
81
|
|
254
|
2
|
|
|
2
|
|
1258
|
use Bio::DB::GFF::Util::Rearrange; |
|
2
|
|
|
|
|
1007
|
|
|
2
|
|
|
|
|
119
|
|
255
|
2
|
|
|
2
|
|
11
|
use Carp 'cluck','carp','croak'; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
101
|
|
256
|
2
|
|
|
2
|
|
539
|
use IO::File; |
|
2
|
|
|
|
|
840
|
|
|
2
|
|
|
|
|
277
|
|
257
|
2
|
|
|
2
|
|
10
|
use File::Glob ':glob'; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
438
|
|
258
|
2
|
|
|
2
|
|
1035
|
use Text::ParseWords 'shellwords'; |
|
2
|
|
|
|
|
2261
|
|
|
2
|
|
|
|
|
116
|
|
259
|
2
|
|
|
2
|
|
1776
|
use Bio::DB::SeqFeature::Store; |
|
2
|
|
|
|
|
35404
|
|
|
2
|
|
|
|
|
95
|
|
260
|
2
|
|
|
2
|
|
23
|
use File::Basename 'dirname'; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
184
|
|
261
|
2
|
|
|
2
|
|
14
|
use File::Spec; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
52
|
|
262
|
2
|
|
|
2
|
|
12
|
use Cwd 'getcwd'; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
159
|
|
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
|
|
|
|
|
200
|
|
273
|
2
|
|
|
2
|
|
10
|
use constant MAX_REMAP => 100; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
544
|
|
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=head2 METHODS |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=over 4 |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=item $version = Bio::Graphics::FeatureFile-Eversion |
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-Enew(@args) |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
Create a new Bio::Graphics::FeatureFile using @args to initialize the |
290
|
|
|
|
|
|
|
object. Arguments are -name=Evalue 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 |
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 |
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
|
134
|
shift->_new(@_); |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub _new { |
361
|
1
|
|
|
1
|
|
2
|
my $class = shift; |
362
|
1
|
|
|
|
|
4
|
my %args = @_; |
363
|
1
|
|
|
|
|
11
|
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
|
|
|
|
|
|
|
$self->{coordinate_mapper} = $args{-map_coords} |
376
|
1
|
50
|
33
|
|
|
5
|
if exists $args{-map_coords} && ref($args{-map_coords}) eq 'CODE'; |
377
|
|
|
|
|
|
|
|
378
|
1
|
50
|
|
|
|
5
|
$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
|
|
|
|
5
|
$self->safe_world(1) if $args{-safe_world}; |
381
|
1
|
50
|
|
|
|
3
|
$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
|
|
|
|
4
|
if (my $file = $args{-file}) { |
|
|
0
|
|
|
|
|
|
389
|
2
|
|
|
2
|
|
13
|
no strict 'refs'; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
22035
|
|
390
|
1
|
50
|
|
|
|
9
|
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-Enew_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 <
|
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-Erender([$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
|
my %types = map {$_=>1 |
602
|
|
|
|
|
|
|
} map { |
603
|
0
|
|
0
|
|
|
0
|
shellwords ($self->setting($_=>'feature')||$_) } @labels; |
|
0
|
|
|
|
|
0
|
|
604
|
0
|
|
|
|
|
0
|
my %lc_types = map {lc($_)}%types; |
|
0
|
|
|
|
|
0
|
|
605
|
|
|
|
|
|
|
|
606
|
0
|
|
|
|
|
0
|
my @unconfigured_types = sort grep {!exists $lc_types{lc $_} && |
607
|
0
|
|
0
|
|
|
0
|
!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
|
|
2
|
my $self = shift; |
683
|
1
|
|
|
|
|
1
|
my $file = shift; |
684
|
1
|
50
|
|
|
|
7
|
defined fileno($file) or return; |
685
|
1
|
50
|
|
|
|
11
|
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
|
|
|
|
|
7
|
|
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
|
|
|
|
|
2
|
$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-Eerror([$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-Esmart_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
|
1
|
my $self = shift; |
752
|
1
|
|
|
|
|
2
|
my $file = shift; |
753
|
|
|
|
|
|
|
|
754
|
1
|
|
|
|
|
3
|
$file =~ s/(\s)/\\$1/g; # escape whitespace from glob expansion |
755
|
|
|
|
|
|
|
|
756
|
1
|
|
|
|
|
50
|
for my $f (glob($file)) { |
757
|
1
|
50
|
|
|
|
10
|
my $fh = IO::File->new($f) or return; |
758
|
1
|
|
|
|
|
134
|
my $cwd = getcwd(); |
759
|
1
|
|
|
|
|
79
|
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
|
|
|
|
|
2
|
my $fh = shift; |
768
|
1
|
|
|
|
|
4
|
$self->_stat($fh); |
769
|
1
|
|
|
|
|
4
|
local $/ = "\n"; |
770
|
1
|
|
|
|
|
2
|
local $_; |
771
|
1
|
|
|
|
|
13
|
while (<$fh>) { |
772
|
5
|
|
|
|
|
7
|
chomp; |
773
|
5
|
50
|
|
|
|
10
|
$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
|
5
|
my $self = shift; |
788
|
5
|
|
|
|
|
6
|
my $line = shift; |
789
|
|
|
|
|
|
|
|
790
|
5
|
|
|
|
|
6
|
$line =~ s/\015//g; # get rid of carriage returns left over by MS-DOS/Windows systems |
791
|
5
|
|
|
|
|
10
|
$line =~ s/\s+$//; # get rid of trailing whitespace |
792
|
|
|
|
|
|
|
|
793
|
5
|
50
|
|
|
|
12
|
if (/^#include\s+(.+)/i) { # #include directive |
794
|
0
|
|
|
|
|
0
|
my ($include_file) = shellwords($1); |
795
|
|
|
|
|
|
|
# detect some loops |
796
|
|
|
|
|
|
|
croak "#include loop detected at $include_file" |
797
|
0
|
0
|
|
|
|
0
|
if $self->{includes}{$include_file}++; |
798
|
0
|
|
|
|
|
0
|
$self->parse_file($include_file); |
799
|
0
|
|
|
|
|
0
|
return 1; |
800
|
|
|
|
|
|
|
} |
801
|
|
|
|
|
|
|
|
802
|
5
|
50
|
|
|
|
9
|
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
|
|
|
|
10
|
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
|
|
|
|
|
7
|
my $old_state = $self->{state}; |
815
|
5
|
|
|
|
|
9
|
my $new_state = $self->_state_transition($line); |
816
|
|
|
|
|
|
|
|
817
|
5
|
100
|
|
|
|
10
|
if ($new_state ne $old_state) { |
818
|
1
|
|
|
|
|
2
|
delete $self->{current_config}; |
819
|
1
|
|
|
|
|
1
|
delete $self->{current_tag}; |
820
|
|
|
|
|
|
|
} |
821
|
|
|
|
|
|
|
|
822
|
5
|
100
|
|
|
|
9
|
if ($new_state eq 'config') { |
|
|
50
|
|
|
|
|
|
823
|
4
|
|
|
|
|
8
|
$self->parse_config_line($line); |
824
|
|
|
|
|
|
|
} elsif ($new_state eq 'data') { |
825
|
1
|
|
|
|
|
4
|
$self->parse_data_line($line); |
826
|
|
|
|
|
|
|
} |
827
|
4
|
|
|
|
|
7
|
$self->{state} = $new_state; |
828
|
4
|
|
|
|
|
14
|
1; |
829
|
|
|
|
|
|
|
} |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
sub _state_transition { |
832
|
5
|
|
|
5
|
|
4
|
my $self = shift; |
833
|
5
|
|
|
|
|
5
|
my $line = shift; |
834
|
5
|
|
|
|
|
4
|
my $current_state = $self->{state}; |
835
|
|
|
|
|
|
|
|
836
|
5
|
50
|
|
|
|
15
|
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
|
|
|
|
10
|
return 'data' if $line =~ /^\#\#(\w+)/; # GFF3 meta instruction |
842
|
5
|
100
|
|
|
|
10
|
return 'data' if $line =~ /^reference\s*=/; # feature-file reference sequence directive |
843
|
|
|
|
|
|
|
|
844
|
4
|
50
|
|
|
|
10
|
return 'config' if $line =~ /^\s*$/; #empty line |
845
|
4
|
100
|
|
|
|
11
|
return 'config' if $line =~ m/^\[(.+)\]/; # section beginning |
846
|
|
|
|
|
|
|
return 'config' if $line =~ m/^[\w:\s]+=/ |
847
|
3
|
50
|
33
|
|
|
18
|
&& $self->{current_config}; # configuration line |
848
|
|
|
|
|
|
|
return 'config' if $line =~ m/^\s+(.+)/ |
849
|
0
|
0
|
0
|
|
|
0
|
&& $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
|
2
|
my $self = shift; |
858
|
4
|
|
|
|
|
5
|
local $_ = shift; |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
# strip right-column comments unless they look like colors or html fragments |
861
|
4
|
50
|
33
|
|
|
28
|
s/\s*\#.*$// unless /\#[0-9a-f]{6,8}\s*$/i || /\w+\#\w+/ || /\w+\"*\s*\#\d+$/; |
|
|
|
33
|
|
|
|
|
862
|
|
|
|
|
|
|
|
863
|
4
|
50
|
33
|
|
|
27
|
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
|
|
|
|
|
|
|
$self->{config}{$cc}{$self->{current_tag}} .= "\n" |
869
|
0
|
0
|
|
|
|
0
|
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
|
|
|
|
|
3
|
my $label = $1; |
875
|
1
|
50
|
|
|
|
6
|
my $cc = $label =~ /^(general|default)$/i ? 'general' : $label; # normalize |
876
|
1
|
50
|
|
|
|
3
|
push @{$self->{types}},$cc unless $cc eq 'general'; |
|
0
|
|
|
|
|
0
|
|
877
|
1
|
|
|
|
|
3
|
$self->{current_config} = $cc; |
878
|
1
|
|
|
|
|
2
|
return 1; |
879
|
|
|
|
|
|
|
} |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
elsif (/^([\w: -]+?)\s*=\s*(.*)/) { # key value pair within a configuration section |
882
|
3
|
|
|
|
|
8
|
my $tag = lc $1; |
883
|
3
|
|
50
|
|
|
7
|
my $cc = $self->{current_config} ||= 'general'; # in case no configuration named |
884
|
3
|
50
|
|
|
|
6
|
my $value = defined $2 ? $2 : ''; |
885
|
3
|
|
|
|
|
8
|
$self->{config}{$cc}{$tag} = $value; |
886
|
3
|
|
|
|
|
3
|
$self->{current_tag} = $tag; |
887
|
3
|
|
|
|
|
5
|
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
|
2
|
my $self = shift; |
902
|
1
|
|
|
|
|
2
|
my $line = shift; |
903
|
1
|
0
|
33
|
|
|
8
|
$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
|
|
1
|
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
|
|
|
|
|
|
|
my $loader = $type->new(-store => $db, |
935
|
|
|
|
|
|
|
-map_coords => $self->{coordinate_mapper}, |
936
|
0
|
|
|
|
|
0
|
-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
|
3
|
my $self = shift; |
947
|
1
|
|
33
|
|
|
18
|
return $self->{db} ||= Bio::DB::SeqFeature::Store->new(-adaptor=>'memory', |
948
|
|
|
|
|
|
|
-write => 1); |
949
|
|
|
|
|
|
|
} |
950
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
=over 4 |
952
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
=item $flat = $features-Eallow_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-Eadd_feature($feature [=E$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-Eadd_type($type=E$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=Evalue 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-Eset($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
|
2
|
my $self = shift; |
1057
|
1
|
|
|
|
|
56
|
delete $self->{features}; |
1058
|
|
|
|
|
|
|
} |
1059
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
sub DESTROY { |
1061
|
1
|
|
|
1
|
|
2785
|
my $self = shift; |
1062
|
1
|
|
|
|
|
5
|
$self->finished(@_); |
1063
|
|
|
|
|
|
|
# $self->{safe_context}->unlink_all_worlds |
1064
|
|
|
|
|
|
|
# if $self->{safe_context}; |
1065
|
|
|
|
|
|
|
} |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
=over 4 |
1068
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
=item $value = $features-Esetting($stanza =E $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-Ecode_setting($stanza=E$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-Esafe_setting($stanza=E$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 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-Esafe([$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-Esafe_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(<
|
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-Eset_callback_source($type,$tag,$value) |
1341
|
|
|
|
|
|
|
|
1342
|
|
|
|
|
|
|
=item $features-Eget_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-Estyle($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-Eadd_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-Eglyph($type); |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
Return the name of the glyph corresponding to the given type (same as |
1396
|
|
|
|
|
|
|
$features-Esetting($type=E'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-Econfigured_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-Etypes() |
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-Efeatures($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-Efeatures($type) |
1490
|
|
|
|
|
|
|
|
1491
|
|
|
|
|
|
|
# Reference to an array of all features of all types |
1492
|
|
|
|
|
|
|
$features = $features-Efeatures() |
1493
|
|
|
|
|
|
|
|
1494
|
|
|
|
|
|
|
# A list when called in a list context |
1495
|
|
|
|
|
|
|
@features = $features-Efeatures() |
1496
|
|
|
|
|
|
|
|
1497
|
|
|
|
|
|
|
2) Bio::Das::SegmentI API: |
1498
|
|
|
|
|
|
|
|
1499
|
|
|
|
|
|
|
@features = $features-Efeatures(-type=>['list','of','types']); |
1500
|
|
|
|
|
|
|
|
1501
|
|
|
|
|
|
|
# variants |
1502
|
|
|
|
|
|
|
$features = $features-Efeatures(-type=>['list','of','types']); |
1503
|
|
|
|
|
|
|
$features = $features-Efeatures(-type=>'a type'); |
1504
|
|
|
|
|
|
|
$iterator = $features-Efeatures(-type=>'a type',-iterator=>1); |
1505
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
$iterator = $features-Efeatures(-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-Efeatures($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-Erefs |
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-Emin |
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-Emax |
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
|
2
|
my $s = shift; |
1725
|
|
|
|
|
|
|
|
1726
|
1
|
|
|
|
|
2
|
$s->{max} = $s->{min} = undef; |
1727
|
1
|
|
|
|
|
2
|
$s->{types} = []; |
1728
|
1
|
|
|
|
|
2
|
$s->{features} = {}; |
1729
|
1
|
|
|
|
|
2
|
$s->{config} = {}; |
1730
|
1
|
|
|
|
|
2
|
$s->{loader} = undef; |
1731
|
1
|
|
|
|
|
3
|
$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-Emtime |
1836
|
|
|
|
|
|
|
|
1837
|
|
|
|
|
|
|
=item $atime = $features-Eatime |
1838
|
|
|
|
|
|
|
|
1839
|
|
|
|
|
|
|
=item $ctime = $features-Ectime |
1840
|
|
|
|
|
|
|
|
1841
|
|
|
|
|
|
|
=item $size = $features-Esize |
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-Efeature2label($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-Elink_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-Ecitation($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-Ename([$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__ |