0
|
1 #
|
|
2 # Ensembl module for Bio::EnsEMBL::Funcgen::Storable
|
|
3 #
|
|
4
|
|
5 =head1 LICENSE
|
|
6
|
|
7 Copyright (c) 1999-2011 The European Bioinformatics Institute and
|
|
8 Genome Research Limited. All rights reserved.
|
|
9
|
|
10 This software is distributed under a modified Apache license.
|
|
11 For license details, please see
|
|
12
|
|
13 http://www.ensembl.org/info/about/code_licence.html
|
|
14
|
|
15 =head1 CONTACT
|
|
16
|
|
17 Please email comments or questions to the public Ensembl
|
|
18 developers list at <ensembl-dev@ebi.ac.uk>.
|
|
19
|
|
20 Questions may also be sent to the Ensembl help desk at
|
|
21 <helpdesk@ensembl.org>.
|
|
22
|
|
23 =head1 NAME
|
|
24
|
|
25 Bio::EnsEMBL::Funcgen::Storable
|
|
26
|
|
27 =head1 SYNOPSIS
|
|
28
|
|
29 my $dbID = $storable_object->dbID();
|
|
30 my $adaptor = $storable_object->adaptor();
|
|
31 if($storable_object->is_stored($db_adaptor))) {
|
|
32
|
|
33 }
|
|
34 =head1 DESCRIPTION
|
|
35
|
|
36 This is a simple wrapper class to provide convenience methods for the StorableAdaptor.
|
|
37 Only get type methods have been implemented here to avoid obfuscating DB writes which
|
|
38 should only be done by the specific 'Storable'Adaptors.
|
|
39
|
|
40 =head1 SEE ALSO
|
|
41
|
|
42 Bio::EnsEMBL::Funcgen::DBSQL::BaseAdaptor
|
|
43
|
|
44 =cut
|
|
45
|
|
46 use strict;
|
|
47 use warnings;
|
|
48
|
|
49 package Bio::EnsEMBL::Funcgen::Storable;
|
|
50
|
|
51
|
|
52 use Bio::EnsEMBL::Registry;
|
|
53 use Bio::EnsEMBL::Utils::Exception qw(throw warning);
|
|
54 use Bio::EnsEMBL::Utils::Argument qw(rearrange);
|
|
55 use Bio::EnsEMBL::Storable;
|
|
56
|
|
57 use vars qw(@ISA);
|
|
58
|
|
59 @ISA = qw(Bio::EnsEMBL::Storable);
|
|
60
|
|
61 =head2 new
|
|
62
|
|
63 Arg [-STATES] : Arrayref of states
|
|
64 Arg [-dbID] : database internal id
|
|
65 Example : none
|
|
66 Caller : internal calls
|
|
67 Description : create a new Storable object
|
|
68 Returntype : Bio::EnsEMBL::Storable
|
|
69 Exceptions : Adaptor not a Bio::EnsEMBL::Funcgen::DBSQL::BaseAdaptor
|
|
70 Status : Stable
|
|
71
|
|
72 =cut
|
|
73
|
|
74 sub new {
|
|
75 my $caller = shift;
|
|
76
|
|
77 my $class = ref($caller) || $caller;
|
|
78
|
|
79 my $self = $class->SUPER::new(@_);
|
|
80
|
|
81
|
|
82 my ($states, $assoc_ftypes) = rearrange(['STATES', 'ASSOCIATED_FEATURE_TYPES'] ,@_);
|
|
83
|
|
84 if ($self->adaptor() && (! $self->adaptor->isa("Bio::EnsEMBL::Funcgen::DBSQL::BaseAdaptor"))){
|
|
85 throw("Adaptor muct be a valid Bio::EnsEMBL::Funcgen::DBSQL::BaseAdaptor");
|
|
86 }
|
|
87
|
|
88 #will these break using _new_fast
|
|
89 #THerefore ResultFeature, Probe and ProbeFeature should not be Funcgen::Storables
|
|
90
|
|
91 @{$self->{'states'}} = @$states if $states;
|
|
92 $self->associated_feature_types($assoc_ftypes) if(defined $assoc_ftypes);
|
|
93
|
|
94
|
|
95 return $self;
|
|
96 }
|
|
97
|
|
98
|
|
99
|
|
100
|
|
101
|
|
102
|
|
103 =head2 has_status
|
|
104
|
|
105 Arg [1] : string - status e.g. IMPORTED, DISPLAYABLE
|
|
106 Example : if($experimental_chip->has_status('IMPORTED'){ ... skip import ... };
|
|
107 Description: Tests whether storable has a given status
|
|
108 Returntype : BOOLEAN
|
|
109 Exceptions : Throws if not status is provided
|
|
110 Caller : general
|
|
111 Status : At risk
|
|
112
|
|
113 =cut
|
|
114
|
|
115
|
|
116
|
|
117 sub has_status{
|
|
118 my ($self, $status) = @_;
|
|
119
|
|
120 throw("Must provide a status to check") if ! $status;
|
|
121
|
|
122 my @state = grep(/$status/, @{$self->get_all_states()});
|
|
123 my $boolean = scalar(@state);#will be 0 or 1 due to table contraints
|
|
124
|
|
125 return $boolean;
|
|
126 }
|
|
127
|
|
128
|
|
129
|
|
130 #There is a potential to create an obj from scratch which may already exist in the db
|
|
131 #If we add a state to this (obj has not dbID so will not retrieve stored states)
|
|
132 # and then try and store it, this will result in adding the state to the previously stored obj.
|
|
133 #The behaviour is silent and could cause problems.
|
|
134
|
|
135 #To resolve this the adaptor implementations must throw if we find a matching object
|
|
136 #We must force the user to generate the obj from the db(use recover) rather than from scratch
|
|
137 #to make them aware of the situation. This is useful to protect objects where we do not want to overwrite previous data
|
|
138 #e.g. experiment, experimental_chip, channel
|
|
139 #For objects which are routinely resued, we must make sure we always try the db first(not just when recover is set)
|
|
140 #Then warn/throw if there are differing attributes
|
|
141
|
|
142 #This is not possible for set objects, but is not a problem as it will just create another set entry rather than overwriting
|
|
143 #All update/store_states methods should be okay so long as we have a dbID first.
|
|
144
|
|
145
|
|
146
|
|
147
|
|
148 =head2 get_all_states
|
|
149
|
|
150 Example : my @ec_states = @{$experimental_chip->get_all_states()};
|
|
151 Description: Retrieves all states from DB and merges with current states array
|
|
152 Returntype : LISTREF
|
|
153 Exceptions : None
|
|
154 Caller : general
|
|
155 Status : At risk
|
|
156
|
|
157 =cut
|
|
158
|
|
159
|
|
160
|
|
161 sub get_all_states{
|
|
162 my ($self) = @_;
|
|
163
|
|
164 my %states;
|
|
165
|
|
166 #This could miss states in the DB for storables which have been created and had states added
|
|
167 #but already exist with states in the DB
|
|
168 #The way to get around this is to throw if we try and store an object without a dbID which matches
|
|
169 #something in the DB.
|
|
170 #Remove func in adaptors(ec and channel only?) to automatically use prestored objects, throw instead if no dbID and matches.
|
|
171 #force use of recover to retrieve object from DB and then skip to relevant step based on states.
|
|
172 #Have states => next method hash in Importer/ArrayDefs?
|
|
173
|
|
174
|
|
175
|
|
176 if($self->is_stored($self->adaptor->db()) && ! $self->{'states'}){
|
|
177 @{$self->{'states'}} = @{$self->adaptor->fetch_all_states($self)};
|
|
178 }
|
|
179
|
|
180 return $self->{'states'};
|
|
181 }
|
|
182
|
|
183
|
|
184 =head2 add_status
|
|
185
|
|
186 Example : $ec->add_state('DISPLAYABLE');
|
|
187 Description: Adds a state to a new or previously stored Storable
|
|
188 Returntype : None
|
|
189 Exceptions : Throws if no status supplied
|
|
190 Caller : general
|
|
191 Status : At risk
|
|
192
|
|
193 =cut
|
|
194
|
|
195
|
|
196
|
|
197 sub add_status{
|
|
198 my ($self, $status) = @_;
|
|
199
|
|
200 throw("Must pass a status to add e.g. 'DISPLAYABLE'") if ! $status;
|
|
201
|
|
202
|
|
203
|
|
204 #this does not resolve the problem!!???
|
|
205 #can add a status to an unstored object which
|
|
206
|
|
207 if($self->adaptor && $self->is_stored($self->adaptor->db()) && ! $self->{'states'}){
|
|
208 @{$self->{'states'}} = @{$self->adaptor->fetch_all_states($self)};
|
|
209 }
|
|
210
|
|
211 push @{$self->{'states'}}, $status;
|
|
212
|
|
213 return;
|
|
214 }
|
|
215
|
|
216 sub is_displayable{
|
|
217 my $self = shift;
|
|
218
|
|
219 return $self->has_status('DISPLAYABLE');
|
|
220 }
|
|
221
|
|
222 #These DBEntry methods are here to enable xrefs to FeatureType, Probe, ProbeSet & ProbeFeature
|
|
223 #They only work as SetFeature has Storable as the second element of @ISA and Bio::EnsEMBL::Feature
|
|
224 #get_all_DBEntries be incorporated into Bio::EnsEMBL::Storable as generic method
|
|
225 #With the other wrapper methods in the Storables of the non-core APIs?
|
|
226 #Or can these be moved to core as a supra core class?
|
|
227 #i.e. Is common to all non-core APIs but not relevant for core?
|
|
228 #Can we bring these together to stop code propogation?
|
|
229
|
|
230
|
|
231 =head2 get_all_Gene_DBEntries
|
|
232
|
|
233 Example : my @gene_dbentries = @{ $storable->get_all_Gene_DBEntries };
|
|
234 Description: Retrieves Ensembl Gene DBEntries (xrefs) for this Storable.
|
|
235 This does _not_ include the corresponding translations
|
|
236 DBEntries (see get_all_DBLinks).
|
|
237
|
|
238 This method will attempt to lazy-load DBEntries from a
|
|
239 database if an adaptor is available and no DBEntries are present
|
|
240 on the transcript (i.e. they have not already been added or
|
|
241 loaded).
|
|
242 Returntype : Listref of Bio::EnsEMBL::DBEntry objects
|
|
243 Exceptions : none
|
|
244 Caller : general
|
|
245 Status : at risk
|
|
246
|
|
247 =cut
|
|
248
|
|
249 #Need to add optional Transcript/Gene param so we can filter
|
|
250 #Filter here or would be better to restrict in sql query ni DBEntryAdaptor?
|
|
251
|
|
252 sub get_all_Gene_DBEntries {
|
|
253 my $self = shift;
|
|
254
|
|
255
|
|
256 #We wouldn't need this if we made the xref schema multi species
|
|
257 #my $species = $self->adaptor->db->species;
|
|
258 my $species = Bio::EnsEMBL::Registry->get_alias($self->adaptor->db->species);
|
|
259
|
|
260 if(!$species){
|
|
261 throw('You must specify a DBAdaptor -species to retrieve DBEntries based on the external_db.db_name');
|
|
262 }
|
|
263
|
|
264 #safety in case we get Homo sapiens
|
|
265 ($species = lc($species)) =~ s/ /_/;
|
|
266
|
|
267
|
|
268
|
|
269 return $self->get_all_DBEntries($species.'_core_Gene');
|
|
270 }
|
|
271
|
|
272 =head2 get_all_Transcript_DBEntries
|
|
273
|
|
274 Arg[0] : optional - Bio::EnsEMBL::Transcript to filter DBEntries on.
|
|
275 Example : my @transc_dbentries = @{ $set_feature->get_all_Transcript_DBEntries };
|
|
276 Description: Retrieves ensembl Transcript DBEntries (xrefs) for this Storable.
|
|
277 This does _not_ include the corresponding translations
|
|
278 DBEntries (see get_all_DBLinks).
|
|
279
|
|
280 This method will attempt to lazy-load DBEntries from a
|
|
281 database if an adaptor is available and no DBEntries are present
|
|
282 on the Storable (i.e. they have not already been added or
|
|
283 loaded).
|
|
284 Returntype : Listref of Bio::EnsEMBL::DBEntry objects
|
|
285 Exceptions : none
|
|
286 Caller : general
|
|
287 Status : at risk
|
|
288
|
|
289 =cut
|
|
290
|
|
291 #change the to ensembl_core when we implement Gene/Transcript/Protein|Translation links on the same external_db
|
|
292
|
|
293 sub get_all_Transcript_DBEntries {
|
|
294 my ($self, $transcript) = @_;
|
|
295
|
|
296 #We wouldn't need this if we made the xref schema multi species
|
|
297 my $species = Bio::EnsEMBL::Registry->get_alias($self->adaptor->db->species);
|
|
298 #Need to make sure this is latin name
|
|
299
|
|
300
|
|
301 if(!$species){
|
|
302 throw('You must specify a DBAdaptor -species to retrieve DBEntries based on the external_db.db_name');
|
|
303 }
|
|
304
|
|
305 #safety in case we get Homo sapiens
|
|
306 #($species = lc($species)) =~ s/ /_/;
|
|
307
|
|
308 my $dbes = $self->get_all_DBEntries($species.'_core_Transcript');
|
|
309
|
|
310 #This needs to be moved to the DBEntryAdaptor and restrict the query using the
|
|
311 #dbprimary_acc
|
|
312
|
|
313 if($transcript){
|
|
314 my $sid = $transcript->stable_id;
|
|
315
|
|
316 #Test for sid here?
|
|
317
|
|
318 if(ref($transcript) && $transcript->isa('Bio::EnsEMBL::Transcript')){
|
|
319 my @dbes;
|
|
320
|
|
321 foreach my $dbe(@$dbes){
|
|
322 if($dbe->primary_id eq $sid){
|
|
323 push @dbes, $dbe;
|
|
324 }
|
|
325 }
|
|
326 $dbes = \@dbes;
|
|
327 }
|
|
328 else{
|
|
329 throw('Transcript argument must be a valid Bio::EnsEMBL::Transcript');
|
|
330 }
|
|
331 }
|
|
332
|
|
333
|
|
334 return $dbes;
|
|
335 }
|
|
336
|
|
337
|
|
338 =head2 get_all_UnmappedObjects
|
|
339
|
|
340 Example : my @uos = @{$storable->get_all_UnmappedObjects };
|
|
341 Description: Retrieves UnamappedObjects for this Storable.
|
|
342 Returntype : arrayref of Bio::EnsEMBL::UnmappedObject objects
|
|
343 Exceptions : none
|
|
344 Caller : general
|
|
345 Status : At risk - move to Bio::Ensembl::Storable?
|
|
346
|
|
347 =cut
|
|
348
|
|
349 sub get_all_UnmappedObjects{
|
|
350 my $self = shift;
|
|
351 #Do we want to add external_db or analysis param here?
|
|
352
|
|
353 my $class = ref($self);
|
|
354 $class =~ s/.*:://;
|
|
355
|
|
356 return $self->adaptor->db->get_UnmappedObjectAdaptor->fetch_all_by_object_type_id($class, $self->dbID);
|
|
357 }
|
|
358
|
|
359
|
|
360 =head2 get_all_DBEntries
|
|
361
|
|
362 Arg[1] : string - External DB name e.g. ensembl_core_Gene
|
|
363 Arg[2] : string - External DB type
|
|
364 Example : my @dbentries = @{ $set_feature->get_all_DBEntries };
|
|
365 Description: Retrieves DBEntries (xrefs) for this SetFeature.
|
|
366 This does _not_ include the corresponding translations
|
|
367 DBEntries (see get_all_DBLinks).
|
|
368
|
|
369 This method will attempt to lazy-load DBEntries from a
|
|
370 database if an adaptor is available and no DBEntries are present
|
|
371 on the SetFeature (i.e. they have not already been added or
|
|
372 loaded).
|
|
373 Returntype : Listref of Bio::EnsEMBL::DBEntry objects
|
|
374 Exceptions : none
|
|
375 Caller : general, get_all_DBLinks
|
|
376 Status : Stable - at risk move to storable
|
|
377
|
|
378 =cut
|
|
379
|
|
380
|
|
381 #We could add 3rd arg here which would be xref(info_)type e.g. Gene/Transcript etc.
|
|
382 #Move info_type to ox.linkage_type to sit along side linkage_annotated
|
|
383
|
|
384
|
|
385 sub get_all_DBEntries {
|
|
386 my $self = shift;
|
|
387 my $ex_db_exp = shift;
|
|
388 my $ex_db_type = shift;
|
|
389
|
|
390 my $cache_name = "dbentries";
|
|
391
|
|
392 if(defined($ex_db_exp)){
|
|
393 $cache_name .= $ex_db_exp;
|
|
394 }
|
|
395 if(defined($ex_db_type)){
|
|
396 $cache_name .= $ex_db_type;
|
|
397 }
|
|
398
|
|
399 #Need to add tests for valid objects for xrefs
|
|
400
|
|
401 # if not cached, retrieve all of the xrefs for this gene
|
|
402
|
|
403 #This is not using the caching optimally
|
|
404 #It seems for naive(ex_db_exp,ex_db_type) queries we create a naive cache
|
|
405 #This means that further more specific queries will make another query and not use the cache
|
|
406
|
|
407
|
|
408 if( (! defined $self->{$cache_name}) && $self->adaptor() ){
|
|
409
|
|
410 my @tables = $self->adaptor->_tables;
|
|
411 @tables = split/_/, $tables[0]->[0];#split annotated_feature
|
|
412 my $object_type = join('', (map ucfirst($_), @tables));#change to AnnotatedFeature
|
|
413
|
|
414 $self->{$cache_name} =
|
|
415 $self->adaptor->db->get_DBEntryAdaptor->_fetch_by_object_type($self->dbID(), $object_type, $ex_db_exp, $ex_db_type);
|
|
416 }
|
|
417 elsif( ! defined $self->{$cache_name} ){
|
|
418 throw('You must have set and adaptor to be able to get_all_DBEntries');
|
|
419 }
|
|
420
|
|
421
|
|
422 $self->{$cache_name} ||= [];
|
|
423
|
|
424 return $self->{$cache_name};
|
|
425 }
|
|
426
|
|
427
|
|
428 =head2 add_DBEntry
|
|
429
|
|
430 Arg [1] : Bio::EnsEMBL::DBEntry $dbe
|
|
431 The dbEntry to be added
|
|
432 Example : my $dbe = Bio::EnsEMBL::DBEntry->new(...);
|
|
433 $transcript->add_DBEntry($dbe);
|
|
434 Description: Associates a DBEntry with this object. Note that adding
|
|
435 DBEntries will prevent future lazy-loading of DBEntries for this
|
|
436 storable (see get_all_DBEntries).
|
|
437 Returntype : none
|
|
438 Exceptions : thrown on incorrect argument type
|
|
439 Caller : general
|
|
440 Status : Stable
|
|
441
|
|
442 =cut
|
|
443
|
|
444 sub add_DBEntry {
|
|
445 my $self = shift;
|
|
446 my $dbe = shift;
|
|
447
|
|
448 unless($dbe && ref($dbe) && $dbe->isa('Bio::EnsEMBL::DBEntry')) {
|
|
449 throw('Expected DBEntry argument');
|
|
450 }
|
|
451
|
|
452 $self->{'dbentries'} ||= [];
|
|
453 push @{$self->{'dbentries'}}, $dbe;
|
|
454 }
|
|
455
|
|
456
|
|
457 =head2 associated_feature_types
|
|
458
|
|
459 Example : my @associated_ftypes = @{$feature->associated_feature_types()};
|
|
460 Description: Getter/Setter for other associated FeatureTypes.
|
|
461 Returntype : ARRAYREF of Bio::EnsEMBL::Funcgen:FeatureType objects
|
|
462 Exceptions : None
|
|
463 Caller : General
|
|
464 Status : At risk
|
|
465
|
|
466 =cut
|
|
467
|
|
468 sub associated_feature_types{
|
|
469 my ($self, $ftypes) = @_;
|
|
470
|
|
471 #Lazy load as we don't want to have to do a join on all features when most will not have any
|
|
472
|
|
473
|
|
474 if(defined $ftypes){
|
|
475
|
|
476 if(ref($ftypes) eq 'ARRAY'){
|
|
477
|
|
478 foreach my $ftype(@$ftypes){
|
|
479
|
|
480 if( ! $ftype->isa('Bio::EnsEMBL::Funcgen::FeatureType') ){
|
|
481 throw('You must pass and ARRAYREF of stored Bio::EnsEMBL::Funcgen::FeatureType objects');
|
|
482 }
|
|
483 #test is stored in adaptor
|
|
484 }
|
|
485
|
|
486 if(defined $self->{'associated_feature_types'}){
|
|
487 warn('You are overwriting associated feature types');
|
|
488 #we could simply add the new ones and make them NR.
|
|
489 }
|
|
490
|
|
491 $self->{'associated_feature_types'} = $ftypes;
|
|
492 }
|
|
493 else{
|
|
494 throw('You must pass and ARRAYREF of stored Bio::EnsEMBL::Funcgen::FeatureType objects');
|
|
495 }
|
|
496 }
|
|
497
|
|
498
|
|
499 if(! defined $self->{'associated_feature_types'}){
|
|
500 #This will fail if we have not stored yet
|
|
501
|
|
502 if(defined $self->adaptor){
|
|
503 $self->{'associated_feature_types'} = $self->adaptor->db->get_FeatureTypeAdaptor->fetch_all_by_association($self);
|
|
504 }
|
|
505
|
|
506 }
|
|
507
|
|
508
|
|
509 #This has the potential to return undef, or an arrayref which may be empty.
|
|
510 return $self->{'associated_feature_types'};
|
|
511 }
|
|
512
|
|
513
|
|
514
|
|
515
|
|
516
|
|
517
|
|
518
|
|
519 1;
|