Archive Ensembl HomeArchive Ensembl Home
TranscriptAdaptor.pm
Go to the documentation of this file.
00001 =head1 LICENSE
00002 
00003   Copyright (c) 1999-2012 The European Bioinformatics Institute and
00004   Genome Research Limited.  All rights reserved.
00005 
00006   This software is distributed under a modified Apache license.
00007   For license details, please see
00008 
00009     http://www.ensembl.org/info/about/code_licence.html
00010 
00011 =head1 CONTACT
00012 
00013   Please email comments or questions to the public Ensembl
00014   developers list at <dev@ensembl.org>.
00015 
00016   Questions may also be sent to the Ensembl help desk at
00017   <helpdesk@ensembl.org>.
00018 
00019 =cut
00020 
00021 =head1 NAME
00022 
00023 Bio::EnsEMBL::DBSQL::TranscriptAdaptor - An adaptor which performs database
00024 interaction relating to the storage and retrieval of Transcripts
00025 
00026 =head1 SYNOPSIS
00027 
00028   use Bio::EnsEMBL::Registry;
00029 
00030   Bio::EnsEMBL::Registry->load_registry_from_db(
00031     -host => 'ensembldb.ensembl.org',
00032     -user => 'anonymous'
00033   );
00034 
00035   $transcript_adaptor =
00036     Bio::EnsEMBL::Registry->get_adaptor( 'Human', 'Core',
00037     'Transcript' );
00038 
00039   $transcript = $transcript_adaptor->fetch_by_dbID(1234);
00040 
00041   $transcript =
00042     $transcript_adaptor->fetch_by_stable_id('ENST00000201961');
00043 
00044   $slice =
00045     $slice_adaptor->fetch_by_region( 'Chromosome', '3', 1, 1000000 );
00046   @transcripts = @{ $transcript_adaptor->fetch_all_by_Slice($slice) };
00047 
00048   ($transcript) =
00049     @{ $transcript_adaptor->fetch_all_by_external_name('NP_065811.1') };
00050 
00051 =head1 DESCRIPTION
00052 
00053 This adaptor provides a means to retrieve and store information related
00054 to Transcripts.  Primarily this involves the retrieval or storage of
00055 Bio::EnsEMBL::Transcript objects from a database.
00056 
00057 See Bio::EnsEMBL::Transcript for details of the Transcript class.
00058 
00059 =cut
00060 
00061 package Bio::EnsEMBL::DBSQL::TranscriptAdaptor;
00062 
00063 use strict;
00064 
00065 use Bio::EnsEMBL::DBSQL::BaseFeatureAdaptor;
00066 use Bio::EnsEMBL::Gene;
00067 use Bio::EnsEMBL::Exon;
00068 use Bio::EnsEMBL::Transcript;
00069 use Bio::EnsEMBL::Translation;
00070 use Bio::EnsEMBL::Utils::Exception qw( deprecate throw warning );
00071 
00072 use vars qw(@ISA);
00073 @ISA = qw( Bio::EnsEMBL::DBSQL::BaseFeatureAdaptor );
00074 
00075 
00076 # _tables
00077 #
00078 #  Description: PROTECTED implementation of superclass abstract method.
00079 #               Returns the names, aliases of the tables to use for queries.
00080 #  Returntype : list of listrefs of strings
00081 #  Exceptions : none
00082 #  Caller     : internal
00083 #  Status     : Stable
00084 
00085 sub _tables {
00086   return (
00087     [ 'transcript',           't' ],
00088     [ 'xref',                 'x' ],
00089     [ 'external_db',          'exdb' ] );
00090 }
00091 
00092 
00093 #_columns
00094 #
00095 #  Description: PROTECTED implementation of superclass abstract method.
00096 #               Returns a list of columns to use for queries.
00097 #  Returntype : list of strings
00098 #  Exceptions : none
00099 #  Caller     : internal
00100 #  Status     : Stable
00101 
00102 sub _columns {
00103   my ($self) = @_;
00104 
00105   my $created_date =
00106     $self->db()->dbc()->from_date_to_seconds("created_date");
00107   my $modified_date =
00108     $self->db()->dbc()->from_date_to_seconds("modified_date");
00109 
00110   return (
00111     't.transcript_id',     't.seq_region_id',
00112     't.seq_region_start',  't.seq_region_end',
00113     't.seq_region_strand', 't.analysis_id',
00114     't.gene_id',           't.is_current',
00115     't.stable_id',         't.version',
00116     $created_date,         $modified_date,
00117     't.description',       't.biotype',
00118     't.status',            'exdb.db_name',
00119     'exdb.status',         'exdb.db_display_name',
00120     'x.xref_id',           'x.display_label',
00121     'x.dbprimary_acc',     'x.version',
00122     'x.description',       'x.info_type',
00123     'x.info_text'
00124   );
00125 }
00126 
00127 sub _left_join {
00128   return (
00129     [ 'xref',                 "x.xref_id = t.display_xref_id" ],
00130     [ 'external_db',          "exdb.external_db_id = x.external_db_id" ]
00131   );
00132 }
00133 
00134 
00135 =head2 fetch_by_stable_id
00136 
00137   Arg [1]    : String $stable_id 
00138                The stable id of the transcript to retrieve
00139   Example    : my $tr = $tr_adaptor->fetch_by_stable_id('ENST00000309301');
00140   Description: Retrieves a transcript via its stable id.
00141   Returntype : Bio::EnsEMBL::Transcript
00142   Exceptions : none
00143   Caller     : general
00144   Status     : Stable
00145 
00146 =cut
00147 
00148 sub fetch_by_stable_id {
00149   my ($self, $stable_id) = @_;
00150 
00151   my $constraint = "t.stable_id = ? AND t.is_current = 1";
00152 
00153   $self->bind_param_generic_fetch($stable_id,SQL_VARCHAR);
00154 
00155   my ($transcript) = @{ $self->generic_fetch($constraint) };
00156 
00157   return $transcript;
00158 }
00159 
00160 
00161 sub fetch_all {
00162   my ($self) = @_;
00163 
00164   my $constraint = 't.biotype != "LRG_gene" and t.is_current = 1';
00165   my @trans  = @{ $self->generic_fetch($constraint) };
00166   return \@trans ;
00167 }
00168 
00169 =head2 fetch_all_versions_by_stable_id 
00170 
00171   Arg [1]     : String $stable_id 
00172                 The stable ID of the transcript to retrieve
00173   Example     : my $tr = $tr_adaptor->fetch_all_version_by_stable_id
00174                   ('ENST00000309301');
00175   Description : Similar to fetch_by_stable_id, but retrieves all versions of a
00176                 transcript stored in the database.
00177   Returntype  : listref of Bio::EnsEMBL::Transcript objects
00178   Exceptions  : if we cant get the gene in given coord system
00179   Caller      : general
00180   Status      : At Risk
00181 
00182 =cut
00183 
00184 sub fetch_all_versions_by_stable_id {
00185   my ($self, $stable_id) = @_;
00186 
00187   my $constraint = "t.stable_id = ?";
00188 
00189   $self->bind_param_generic_fetch($stable_id,SQL_VARCHAR);
00190 
00191   return $self->generic_fetch($constraint);
00192 }
00193 
00194 
00195 =head2 fetch_by_translation_stable_id
00196 
00197   Arg [1]    : String $transl_stable_id
00198                The stable identifier of the translation of the transcript to 
00199                retrieve
00200   Example    : my $tr = $tr_adaptor->fetch_by_translation_stable_id
00201                   ('ENSP00000311007');
00202   Description: Retrieves a Transcript object using the stable identifier of
00203                its translation.
00204   Returntype : Bio::EnsEMBL::Transcript
00205   Exceptions : none
00206   Caller     : general
00207   Status     : Stable
00208 
00209 =cut
00210 
00211 sub fetch_by_translation_stable_id {
00212   my ($self, $transl_stable_id ) = @_;
00213 
00214   my $sth = $self->prepare(qq(
00215       SELECT t.transcript_id
00216       FROM   translation tl,
00217              transcript t
00218       WHERE  tl.stable_id = ?
00219       AND    tl.transcript_id = t.transcript_id
00220       AND    t.is_current = 1
00221   ));
00222 
00223   $sth->bind_param(1, $transl_stable_id, SQL_VARCHAR);
00224   $sth->execute();
00225 
00226   my ($id) = $sth->fetchrow_array;
00227   $sth->finish;
00228   if ($id){
00229     return $self->fetch_by_dbID($id);
00230   } else {
00231     return undef;
00232   }
00233 }
00234 
00235 
00236 =head2 fetch_by_translation_id
00237 
00238   Arg [1]    : Int $id
00239                The internal identifier of the translation whose transcript
00240                is to be retrieved
00241   Example    : my $tr = $tr_adaptor->fetch_by_translation_id($transl->dbID);
00242   Description: Given the internal identifier of a translation this method 
00243                retrieves the transcript associated with that translation.
00244                If the transcript cannot be found undef is returned instead.
00245   Returntype : Bio::EnsEMBL::Transcript or undef
00246   Exceptions : none
00247   Caller     : general
00248   Status     : Stable
00249 
00250 =cut
00251 
00252 sub fetch_by_translation_id {
00253   my ( $self, $p_dbID ) = @_;
00254 
00255   if ( !defined($p_dbID) ) {
00256     throw("dbID argument is required");
00257   }
00258 
00259   my $sth =
00260     $self->prepare(   "SELECT transcript_id "
00261                     . "FROM   translation "
00262                     . "WHERE  translation_id = ?" );
00263 
00264   $sth->bind_param( 1, $p_dbID, SQL_INTEGER );
00265   $sth->execute();
00266 
00267   my ($dbID) = $sth->fetchrow_array();
00268   $sth->finish();
00269 
00270   if ($dbID) {
00271     return $self->fetch_by_dbID($dbID);
00272   }
00273 
00274   return undef;
00275 }
00276 
00277 =head2 fetch_all_by_Gene
00278 
00279   Arg [1]    : Bio::EnsEMBL::Gene $gene
00280                The gene to fetch transcripts of
00281   Example    : my $gene = $gene_adaptor->fetch_by_stable_id('ENSG0000123');
00282                my @transcripts = { $tr_adaptor->fetch_all_by_Gene($gene) };
00283   Description: Retrieves Transcript objects for given gene. Puts Genes slice
00284                in each Transcript. 
00285   Returntype : Listref of Bio::EnsEMBL::Transcript objects
00286   Exceptions : none
00287   Caller     : Gene->get_all_Transcripts()
00288   Status     : Stable
00289 
00290 =cut
00291 
00292 sub fetch_all_by_Gene {
00293   my ( $self, $gene ) = @_;
00294 
00295   my $constraint = "t.gene_id = " . $gene->dbID();
00296 
00297   # Use the fetch_all_by_Slice_constraint method because it handles the
00298   # difficult Haps/PARs and coordinate remapping.
00299 
00300   # Get a slice that entirely overlaps the gene.  This is because we
00301   # want all transcripts to be retrieved, not just ones overlapping
00302   # the slice the gene is on (the gene may only partially overlap the
00303   # slice).  For speed reasons, only use a different slice if necessary
00304   # though.
00305 
00306   my $gslice = $gene->slice();
00307 
00308   if ( !defined($gslice) ) {
00309     throw("Gene must have attached slice to retrieve transcripts.");
00310   }
00311 
00312   my $slice;
00313 
00314   if ( $gene->start() < 1 || $gene->end() > $gslice->length() ) {
00315     if ( $gslice->is_circular() ) {
00316       $slice = $gslice;
00317     } else {
00318       $slice = $self->db->get_SliceAdaptor->fetch_by_Feature($gene);
00319     }
00320   } else {
00321     $slice = $gslice;
00322   }
00323 
00324   my $transcripts =
00325     $self->fetch_all_by_Slice_constraint( $slice, $constraint );
00326 
00327   if ( $slice != $gslice ) {
00328     my @out;
00329     foreach my $tr ( @{$transcripts} ) {
00330       push( @out, $tr->transfer($gslice) );
00331     }
00332     $transcripts = \@out;
00333   }
00334 
00335   my $canonical_t = $gene->canonical_transcript();
00336 
00337   foreach my $t ( @{$transcripts} ) {
00338     if ( $t->equals($canonical_t) ) {
00339       $t->is_canonical(1);
00340       last;
00341     }
00342   }
00343 
00344   return $transcripts;
00345 } ## end sub fetch_all_by_Gene
00346 
00347 
00348 =head2 fetch_all_by_Slice
00349 
00350   Arg [1]    : Bio::EnsEMBL::Slice $slice
00351                The slice to fetch transcripts on
00352   Arg [2]    : (optional) Boolean $load_exons
00353                If true, exons will be loaded immediately rather than
00354                lazy loaded later
00355   Arg [3]    : (optional) String $logic_name
00356                The logic name of the type of features to obtain
00357   ARG [4]    : (optional) String $constraint
00358                An extra contraint.
00359   Example    : my @transcripts = @{ $tr_adaptor->fetch_all_by_Slice($slice) };
00360   Description: Overrides superclass method to optionally load exons
00361                immediately rather than lazy-loading them later. This
00362                is more efficient when there are a lot of transcripts whose
00363                exons are going to be used.
00364   Returntype : Listref of Bio::EnsEMBL::Transcript objects
00365   Exceptions : thrown if exon cannot be placed on transcript slice
00366   Caller     : Slice::get_all_Transcripts
00367   Status     : Stable
00368 
00369 =cut
00370 
00371 sub fetch_all_by_Slice {
00372   my ( $self, $slice, $load_exons, $logic_name, $constraint ) = @_;
00373 
00374   my $transcripts;
00375   if ( defined($constraint) && $constraint ne '' ) {
00376     $transcripts = $self->SUPER::fetch_all_by_Slice_constraint( $slice,
00377       't.is_current = 1 AND ' . $constraint, $logic_name );
00378   } else {
00379     $transcripts = $self->SUPER::fetch_all_by_Slice_constraint( $slice,
00380       't.is_current = 1', $logic_name );
00381   }
00382 
00383   # if there are 0 or 1 transcripts still do lazy-loading
00384   if ( !$load_exons || @$transcripts < 2 ) {
00385     return $transcripts;
00386   }
00387 
00388   # preload all of the exons now, instead of lazy loading later
00389   # faster than 1 query per transcript
00390 
00391   # first check if the exons are already preloaded
00392   # @todo FIXME: Should test all exons.
00393   if ( exists( $transcripts->[0]->{'_trans_exon_array'} ) ) {
00394     return $transcripts;
00395   }
00396 
00397   # get extent of region spanned by transcripts
00398   my ( $min_start, $max_end );
00399   foreach my $tr (@$transcripts) {
00400     if ( !defined($min_start) || $tr->seq_region_start() < $min_start )
00401     {
00402       $min_start = $tr->seq_region_start();
00403     }
00404     if ( !defined($max_end) || $tr->seq_region_end() > $max_end ) {
00405       $max_end = $tr->seq_region_end();
00406     }
00407   }
00408 
00409   my $ext_slice;
00410 
00411   if ( $min_start >= $slice->start() && $max_end <= $slice->end() ) {
00412     $ext_slice = $slice;
00413   } else {
00414     my $sa = $self->db()->get_SliceAdaptor();
00415     $ext_slice = $sa->fetch_by_region(
00416       $slice->coord_system->name(), $slice->seq_region_name(),
00417       $min_start,                   $max_end,
00418       $slice->strand(),             $slice->coord_system->version() );
00419   }
00420 
00421   # associate exon identifiers with transcripts
00422 
00423   my %tr_hash = map { $_->dbID => $_ } @{$transcripts};
00424 
00425   my $tr_id_str = join( ',', keys(%tr_hash) );
00426 
00427   my $sth =
00428     $self->prepare( "SELECT transcript_id, exon_id, rank "
00429       . "FROM exon_transcript "
00430       . "WHERE transcript_id IN ($tr_id_str)" );
00431 
00432   $sth->execute();
00433 
00434   my ( $tr_id, $ex_id, $rank );
00435   $sth->bind_columns( \( $tr_id, $ex_id, $rank ) );
00436 
00437   my %ex_tr_hash;
00438 
00439   while ( $sth->fetch() ) {
00440     $ex_tr_hash{$ex_id} ||= [];
00441     push( @{ $ex_tr_hash{$ex_id} }, [ $tr_hash{$tr_id}, $rank ] );
00442   }
00443 
00444   my $ea    = $self->db()->get_ExonAdaptor();
00445   my $exons = $ea->fetch_all_by_Slice_constraint(
00446     $ext_slice,
00447     sprintf( "e.exon_id IN (%s)",
00448       join( ',', sort { $a <=> $b } keys(%ex_tr_hash) ) ) );
00449 
00450   # move exons onto transcript slice, and add them to transcripts
00451   foreach my $ex ( @{$exons} ) {
00452     my $new_ex;
00453     if ( $slice != $ext_slice ) {
00454       $new_ex = $ex->transfer($slice);
00455       if ( !defined($new_ex) ) {
00456         throw("Unexpected. "
00457             . "Exon could not be transfered onto Transcript slice." );
00458       }
00459     } else {
00460       $new_ex = $ex;
00461     }
00462 
00463     foreach my $row ( @{ $ex_tr_hash{ $new_ex->dbID() } } ) {
00464       my ( $tr, $rank ) = @{$row};
00465       $tr->add_Exon( $new_ex, $rank );
00466     }
00467   }
00468 
00469   my $tla = $self->db()->get_TranslationAdaptor();
00470 
00471   # load all of the translations at once
00472   $tla->fetch_all_by_Transcript_list($transcripts);
00473 
00474   return $transcripts;
00475 } ## end sub fetch_all_by_Slice
00476 
00477 
00478 =head2 fetch_all_by_external_name
00479 
00480   Arg [1]    : String $external_name
00481                An external identifier of the transcript to be obtained
00482   Arg [2]    : (optional) String $external_db_name
00483                The name of the external database from which the
00484                identifier originates.
00485   Example    : my @transcripts =
00486                   @{ $tr_adaptor->fetch_all_by_external_name( 'NP_065811.1') };
00487                my @more_transcripts = 
00488                   @{$tr_adaptor->fetch_all_by_external_name( 'NP_0658__._')};
00489   Description: Retrieves all transcripts which are associated with
00490                an external identifier such as a GO term, Swissprot
00491                identifer, etc.  Usually there will only be a single
00492                transcript returned in the list reference, but not
00493                always.  Transcripts are returned in their native
00494                coordinate system, i.e. the coordinate system in which
00495                they are stored in the database.  If they are required
00496                in another coordinate system the Transcript::transfer or
00497                Transcript::transform method can be used to convert them.
00498                If no transcripts with the external identifier are found,
00499                a reference to an empty list is returned.
00500                SQL wildcards % and _ are supported in the $external_name
00501   Returntype : listref of Bio::EnsEMBL::Transcript
00502   Exceptions : none
00503   Caller     : general
00504   Status     : Stable
00505 
00506 =cut
00507 
00508 sub fetch_all_by_external_name {
00509   my ( $self, $external_name, $external_db_name ) = @_;
00510 
00511   my $entryAdaptor = $self->db->get_DBEntryAdaptor();
00512 
00513   my @ids =
00514     $entryAdaptor->list_transcript_ids_by_extids( $external_name,
00515                                                   $external_db_name );
00516 
00517   return $self->fetch_all_by_dbID_list( \@ids );
00518 }
00519 
00520 =head2 fetch_all_by_GOTerm
00521 
00522   Arg [1]   : Bio::EnsEMBL::OntologyTerm
00523               The GO term for which transcripts should be fetched.
00524 
00525   Example:  @transcripts = @{
00526               $transcript_adaptor->fetch_all_by_GOTerm(
00527                 $go_adaptor->fetch_by_accession('GO:0030326') ) };
00528 
00529   Description   : Retrieves a list of transcripts that are
00530                   associated with the given GO term, or with any of
00531                   its descendent GO terms.  The transcripts returned
00532                   are in their native coordinate system, i.e. in
00533                   the coordinate system in which they are stored
00534                   in the database.  If another coordinate system
00535                   is required then the Transcript::transfer or
00536                   Transcript::transform method can be used.
00537 
00538   Return type   : listref of Bio::EnsEMBL::Transcript
00539   Exceptions    : Throws of argument is not a GO term
00540   Caller        : general
00541   Status        : Stable
00542 
00543 =cut
00544 
00545 sub fetch_all_by_GOTerm {
00546   my ( $self, $term ) = @_;
00547 
00548   assert_ref( $term, 'Bio::EnsEMBL::OntologyTerm' );
00549   if ( $term->ontology() ne 'GO' ) {
00550     throw('Argument is not a GO term');
00551   }
00552 
00553   my $entryAdaptor = $self->db->get_DBEntryAdaptor();
00554 
00555   my %unique_dbIDs;
00556   foreach my $accession ( map { $_->accession() }
00557                           ( $term, @{ $term->descendants() } ) )
00558   {
00559     my @ids =
00560       $entryAdaptor->list_transcript_ids_by_extids( $accession, 'GO' );
00561     foreach my $dbID (@ids) { $unique_dbIDs{$dbID} = 1 }
00562   }
00563 
00564   my @result = @{
00565     $self->fetch_all_by_dbID_list(
00566                               [ sort { $a <=> $b } keys(%unique_dbIDs) ]
00567     ) };
00568 
00569   return \@result;
00570 } ## end sub fetch_all_by_GOTerm
00571 
00572 =head2 fetch_all_by_GOTerm_accession
00573 
00574   Arg [1]   : String
00575               The GO term accession for which genes should be
00576               fetched.
00577 
00578   Example   :
00579 
00580     @genes =
00581       @{ $gene_adaptor->fetch_all_by_GOTerm_accession(
00582         'GO:0030326') };
00583 
00584   Description   : Retrieves a list of genes that are associated with
00585                   the given GO term, or with any of its descendent
00586                   GO terms.  The genes returned are in their native
00587                   coordinate system, i.e. in the coordinate system
00588                   in which they are stored in the database.  If
00589                   another coordinate system is required then the
00590                   Gene::transfer or Gene::transform method can be
00591                   used.
00592 
00593   Return type   : listref of Bio::EnsEMBL::Gene
00594   Exceptions    : Throws of argument is not a GO term accession
00595   Caller        : general
00596   Status        : Stable
00597 
00598 =cut
00599 
00600 sub fetch_all_by_GOTerm_accession {
00601   my ( $self, $accession ) = @_;
00602 
00603   if ( $accession !~ /^GO:/ ) {
00604     throw('Argument is not a GO term accession');
00605   }
00606 
00607   my $goAdaptor =
00608     Bio::EnsEMBL::Registry->get_adaptor( 'Multi', 'Ontology',
00609                                          'OntologyTerm' );
00610 
00611   my $term = $goAdaptor->fetch_by_accession($accession);
00612 
00613   return $self->fetch_all_by_GOTerm($term);
00614 }
00615 
00616 =head2 fetch_by_display_label
00617 
00618   Arg [1]    : String $label - display label of transcript to fetch
00619   Example    : my $tr = $tr_adaptor->fetch_by_display_label("BRCA2");
00620   Description: Returns the transcript which has the given display label or
00621                undef if there is none. If there are more than 1, only the first
00622                is reported.
00623   Returntype : Bio::EnsEMBL::Transcript
00624   Exceptions : none
00625   Caller     : general
00626   Status     : Stable
00627 
00628 =cut
00629 
00630 sub fetch_by_display_label {
00631   my $self = shift;
00632   my $label = shift;
00633 
00634   my $constraint = "x.display_label = ? AND t.is_current = 1";
00635 
00636   $self->bind_param_generic_fetch($label,SQL_VARCHAR);
00637 
00638   my ($transcript) = @{ $self->generic_fetch($constraint) };
00639 
00640   return $transcript;
00641 }
00642 
00643 
00644 =head2 fetch_all_by_exon_stable_id
00645 
00646   Arg [1]    : String $stable_id 
00647                The stable id of an exon in a transcript
00648   Example    : my $tr = $tr_adaptor->fetch_all_by_exon_stable_id
00649                   ('ENSE00000309301');
00650   Description: Retrieves a list of transcripts via an exon stable id.
00651   Returntype : Listref of Bio::EnsEMBL::Transcript objects
00652   Exceptions : none
00653   Caller     : general
00654   Status     : Stable
00655 
00656 =cut
00657 
00658 sub fetch_all_by_exon_stable_id {
00659   my ($self, $stable_id) = @_;
00660 
00661   my @trans ;
00662 
00663   my $sth = $self->prepare(qq(
00664       SELECT t.transcript_id 
00665       FROM exon_transcript et, exon e, transcript t
00666       WHERE e.exon_id = et.exon_id
00667       AND et.transcript_id = t.transcript_id
00668       AND e.stable_id = ?
00669       AND t.is_current = 1
00670   ));
00671   
00672   $sth->bind_param(1, $stable_id, SQL_VARCHAR);
00673   $sth->execute();
00674 
00675   while( my $id = $sth->fetchrow_array ) {
00676     my $transcript = $self->fetch_by_dbID($id);
00677     push(@trans, $transcript) if $transcript;
00678   }
00679 
00680   if (!@trans) {
00681     return undef;
00682   }
00683 
00684   return \@trans;
00685 }
00686 
00687 
00688 =head2 store
00689 
00690   Arg [1]    : Bio::EnsEMBL::Transcript $transcript
00691                The transcript to be written to the database
00692   Arg [2]    : Int $gene_dbID
00693                The identifier of the gene that this transcript is associated 
00694                with
00695   Arg [3]    : DEPRECATED (optional) Int $analysis_id
00696                The analysis_id to use when storing this gene. This is for 
00697                backward compatibility only and used to fall back to the gene
00698                analysis_id if no analysis object is attached to the transcript
00699                (which you should do for new code).
00700   Example    : $transID = $tr_adaptor->store($transcript, $gene->dbID);
00701   Description: Stores a transcript in the database and returns the new
00702                internal identifier for the stored transcript.
00703   Returntype : Int 
00704   Exceptions : none
00705   Caller     : general
00706   Status     : Stable
00707 
00708 =cut
00709 
00710 sub store {
00711   my ( $self, $transcript, $gene_dbID, $analysis_id ) = @_;
00712 
00713   if (    !ref($transcript)
00714        || !$transcript->isa('Bio::EnsEMBL::Transcript') )
00715   {
00716     throw("$transcript is not a EnsEMBL transcript - not storing");
00717   }
00718 
00719   my $db = $self->db();
00720 
00721   if ( $transcript->is_stored($db) ) {
00722     return $transcript->dbID();
00723   }
00724 
00725   # Force lazy-loading of exons and ensure coords are correct.
00726   $transcript->recalculate_coordinates();
00727 
00728   my $is_current = ( defined( $transcript->is_current() )
00729                      ? $transcript->is_current()
00730                      : 1 );
00731 
00732   # store analysis
00733   my $analysis = $transcript->analysis();
00734   my $new_analysis_id;
00735 
00736   if ($analysis) {
00737     if ( $analysis->is_stored($db) ) {
00738       $new_analysis_id = $analysis->dbID;
00739     } else {
00740       $new_analysis_id = $db->get_AnalysisAdaptor->store($analysis);
00741     }
00742   } elsif ($analysis_id) {
00743     # Fall back to analysis passed in (usually from gene) if analysis
00744     # wasn't set explicitely for the transcript. This is deprectated
00745     # though.
00746     warning(   "You should explicitely attach "
00747              . "an analysis object to the Transcript. "
00748              . "Will fall back to Gene analysis, "
00749              . "but this behaviour is deprecated." );
00750     $new_analysis_id = $analysis_id;
00751   } else {
00752     throw("Need an analysis_id to store the Transcript.");
00753   }
00754 
00755   #
00756   # Store exons - this needs to be done before the possible transfer
00757   # of the transcript to another slice (in _prestore()).  Transfering
00758   # results in copies being made of the exons and we need to preserve
00759   # the object identity of the exons so that they are not stored twice
00760   # by different transcripts.
00761   #
00762   my $exons       = $transcript->get_all_Exons();
00763   my $exonAdaptor = $db->get_ExonAdaptor();
00764   foreach my $exon ( @{$exons} ) {
00765     $exonAdaptor->store($exon);
00766   }
00767 
00768   my $original_translation = $transcript->translation();
00769   my $original             = $transcript;
00770   my $seq_region_id;
00771   ( $transcript, $seq_region_id ) = $self->_pre_store($transcript);
00772 
00773   # First store the transcript without a display xref.  The display xref
00774   # needs to be set after xrefs are stored which needs to happen after
00775   # transcript is stored.
00776 
00777   #
00778   # Store transcript
00779   #
00780   my $store_transcript_sql = qq(
00781       INSERT INTO transcript 
00782         SET gene_id = ?, 
00783             analysis_id = ?, 
00784             seq_region_id = ?, 
00785             seq_region_start = ?,
00786             seq_region_end = ?, 
00787             seq_region_strand = ?, 
00788             biotype = ?, 
00789             status = ?, 
00790             description = ?,
00791             is_current = ?, 
00792             canonical_translation_id = ? 
00793   );
00794 
00795   if ( defined( $transcript->stable_id() ) ) {
00796 
00797       my $created = $self->db->dbc->from_seconds_to_date($transcript->created_date());
00798       my $modified = $self->db->dbc->from_seconds_to_date($transcript->modified_date());
00799       $store_transcript_sql .= ", stable_id = ?, version = ?, created_date = " . $created . " , modified_date = " . $modified;
00800 
00801   }
00802 
00803   my $tst = $self->prepare($store_transcript_sql);
00804   $tst->bind_param( 1,  $gene_dbID,                 SQL_INTEGER );
00805   $tst->bind_param( 2,  $new_analysis_id,           SQL_INTEGER );
00806   $tst->bind_param( 3,  $seq_region_id,             SQL_INTEGER );
00807   $tst->bind_param( 4,  $transcript->start(),       SQL_INTEGER );
00808   $tst->bind_param( 5,  $transcript->end(),         SQL_INTEGER );
00809   $tst->bind_param( 6,  $transcript->strand(),      SQL_TINYINT );
00810   $tst->bind_param( 7,  $transcript->biotype(),     SQL_VARCHAR );
00811   $tst->bind_param( 8,  $transcript->status(),      SQL_VARCHAR );
00812   $tst->bind_param( 9,  $transcript->description(), SQL_LONGVARCHAR );
00813   $tst->bind_param( 10, $is_current,                SQL_TINYINT );
00814 
00815   # If the transcript has a translation, this is updated later:
00816   $tst->bind_param( 11, undef, SQL_INTEGER );
00817 
00818   if ( defined( $transcript->stable_id() ) ) {
00819 
00820     $tst->bind_param( 12, $transcript->stable_id(), SQL_VARCHAR );
00821     my $version = ($transcript->version()) ? $transcript->version() : 1;
00822     $tst->bind_param( 13, $version,                 SQL_INTEGER );
00823   }
00824 
00825 
00826   $tst->execute();
00827   $tst->finish();
00828 
00829   my $transc_dbID = $tst->{'mysql_insertid'};
00830 
00831   #
00832   # Store translation
00833   #
00834 
00835   my $alt_translations =
00836     $transcript->get_all_alternative_translations();
00837   my $translation = $transcript->translation();
00838 
00839   if ( defined($translation) ) {
00840     # Make sure that the start and end exon are set correctly.
00841     my $start_exon = $translation->start_Exon();
00842     my $end_exon   = $translation->end_Exon();
00843 
00844     if ( !defined($start_exon) ) {
00845       throw("Translation does not define a start exon.");
00846     }
00847 
00848     if ( !defined($end_exon) ) {
00849       throw("Translation does not defined an end exon.");
00850     }
00851 
00852     # If the dbID is not set, this means the exon must have been a
00853     # different object in memory than the the exons of the transcript.
00854     # Try to find the matching exon in all of the exons we just stored.
00855     if ( !defined( $start_exon->dbID() ) ) {
00856       my $key = $start_exon->hashkey();
00857       ($start_exon) = grep { $_->hashkey() eq $key } @$exons;
00858 
00859       if ( defined($start_exon) ) {
00860         $translation->start_Exon($start_exon);
00861       } else {
00862         throw(   "Translation's start_Exon does not appear "
00863                . "to be one of the exons in "
00864                . "its associated Transcript" );
00865       }
00866     }
00867 
00868     if ( !defined( $end_exon->dbID() ) ) {
00869       my $key = $end_exon->hashkey();
00870       ($end_exon) = grep { $_->hashkey() eq $key } @$exons;
00871 
00872       if ( defined($end_exon) ) {
00873         $translation->end_Exon($end_exon);
00874       } else {
00875         throw(   "Translation's end_Exon does not appear "
00876                . "to be one of the exons in "
00877                . "its associated Transcript." );
00878       }
00879     }
00880 
00881     my $old_dbid = $translation->dbID();
00882     $db->get_TranslationAdaptor()->store( $translation, $transc_dbID );
00883 
00884     # Need to update the canonical_translation_id for this transcript.
00885 
00886     my $sth = $self->prepare(
00887       q(
00888       UPDATE transcript
00889       SET canonical_translation_id = ?
00890       WHERE transcript_id = ?)
00891     );
00892 
00893     $sth->bind_param( 1, $translation->dbID(), SQL_INTEGER );
00894     $sth->bind_param( 2, $transc_dbID,         SQL_INTEGER );
00895 
00896     $sth->execute();
00897 
00898     # Set values of the original translation, we may have copied it when
00899     # we transformed the transcript.
00900     $original_translation->dbID( $translation->dbID() );
00901     $original_translation->adaptor( $translation->adaptor() );
00902   } ## end if ( defined($translation...))
00903 
00904   #
00905   # Store the alternative translations, if there are any.
00906   #
00907 
00908   if ( defined($alt_translations)
00909        && scalar( @{$alt_translations} ) > 0 )
00910   {
00911     foreach my $alt_translation ( @{$alt_translations} ) {
00912       my $start_exon = $alt_translation->start_Exon();
00913       my $end_exon   = $alt_translation->end_Exon();
00914 
00915       if ( !defined($start_exon) ) {
00916         throw("Translation does not define a start exon.");
00917       } elsif ( !defined($end_exon) ) {
00918         throw("Translation does not defined an end exon.");
00919       }
00920 
00921       if ( !defined( $start_exon->dbID() ) ) {
00922         my $key = $start_exon->hashkey();
00923         ($start_exon) = grep { $_->hashkey() eq $key } @{$exons};
00924 
00925         if ( defined($start_exon) ) {
00926           $alt_translation->start_Exon($start_exon);
00927         } else {
00928           throw(   "Translation's start_Exon does not appear "
00929                  . "to be one of the exon in"
00930                  . "its associated Transcript" );
00931         }
00932       } elsif ( !defined( $end_exon->dbID() ) ) {
00933         my $key = $end_exon->hashkey();
00934         ($end_exon) = grep { $_->hashkey() eq $key } @$exons;
00935 
00936         if ( defined($end_exon) ) {
00937           $translation->end_Exon($end_exon);
00938         } else {
00939           throw(   "Translation's end_Exon does not appear "
00940                  . "to be one of the exons in "
00941                  . "its associated Transcript." );
00942         }
00943       }
00944 
00945       $db->get_TranslationAdaptor()
00946         ->store( $alt_translation, $transc_dbID );
00947     } ## end foreach my $alt_translation...
00948   } ## end if ( defined($alt_translations...))
00949 
00950   #
00951   # Store the xrefs/object xref mapping.
00952   #
00953   my $dbEntryAdaptor = $db->get_DBEntryAdaptor();
00954 
00955   foreach my $dbe ( @{ $transcript->get_all_DBEntries() } ) {
00956     $dbEntryAdaptor->store( $dbe, $transc_dbID, "Transcript", 1 );
00957   }
00958 
00959   #
00960   # Update transcript to point to display xref if it is set.
00961   #
00962   if ( my $dxref = $transcript->display_xref() ) {
00963     my $dxref_id;
00964 
00965     if ( $dxref->is_stored($db) ) {
00966       $dxref_id = $dxref->dbID();
00967     } else {
00968       $dxref_id = $dbEntryAdaptor->exists($dxref);
00969     }
00970 
00971     if ( defined($dxref_id) ) {
00972       my $sth =
00973         $self->prepare(   "UPDATE transcript "
00974                         . "SET display_xref_id = ? "
00975                         . "WHERE transcript_id = ?" );
00976       $sth->bind_param( 1, $dxref_id,    SQL_INTEGER );
00977       $sth->bind_param( 2, $transc_dbID, SQL_INTEGER );
00978       $sth->execute();
00979       $dxref->dbID($dxref_id);
00980       $dxref->adaptor($dbEntryAdaptor);
00981       $sth->finish();
00982     } else {
00983       warning(sprintf(
00984                      "Display_xref %s:%s is not stored in database.\n"
00985                        . "Not storing relationship to this transcript.",
00986                      $dxref->dbname(), $dxref->display_id() ) );
00987       $dxref->dbID(undef);
00988       $dxref->adaptor(undef);
00989     }
00990   } ## end if ( my $dxref = $transcript...)
00991 
00992   #
00993   # Link transcript to exons in exon_transcript table
00994   #
00995   my $etst = $self->prepare(
00996              "INSERT INTO exon_transcript (exon_id,transcript_id,rank) "
00997                . "VALUES (?,?,?)" );
00998   my $rank = 1;
00999   foreach my $exon ( @{ $transcript->get_all_Exons } ) {
01000     $etst->bind_param( 1, $exon->dbID,  SQL_INTEGER );
01001     $etst->bind_param( 2, $transc_dbID, SQL_INTEGER );
01002     $etst->bind_param( 3, $rank,        SQL_INTEGER );
01003     $etst->execute();
01004     $rank++;
01005   }
01006 
01007   $etst->finish();
01008 
01009   # Now the supporting evidence
01010   my $tsf_adaptor = $db->get_TranscriptSupportingFeatureAdaptor();
01011   $tsf_adaptor->store( $transc_dbID,
01012                        $transcript->get_all_supporting_features() );
01013 
01014   # store transcript attributes if there are any
01015   my $attr_adaptor = $db->get_AttributeAdaptor();
01016 
01017   $attr_adaptor->store_on_Transcript( $transc_dbID,
01018                                     $transcript->get_all_Attributes() );
01019 
01020   # Update the original transcript object - not the transfered copy that
01021   # we might have created.
01022   $original->dbID($transc_dbID);
01023   $original->adaptor($self);
01024 
01025   return $transc_dbID;
01026 } ## end sub store
01027 
01028 
01029 =head2 get_Interpro_by_transid
01030 
01031   Arg [1]    : String $trans_stable_id
01032                The stable if of the transcript to obtain
01033   Example    : @i = $tr_adaptor->get_Interpro_by_transid($trans->stable_id()); 
01034   Description: Gets interpro accession numbers by transcript stable id.
01035                A hack really - we should have a much more structured 
01036                system than this.
01037   Returntype : listref of strings (Interpro_acc:description)
01038   Exceptions : none 
01039   Caller     : domainview? , GeneView
01040   Status     : Stable
01041 
01042 =cut
01043 
01044 sub get_Interpro_by_transid {
01045    my ($self,$trans_stable_id) = @_;
01046 
01047    my $sth = $self->prepare(qq(
01048       SELECT  STRAIGHT_JOIN i.interpro_ac, x.description
01049       FROM    transcript t,
01050               translation tl,
01051               protein_feature pf,
01052           interpro i,
01053               xref x
01054       WHERE   t.stable_id = ?
01055       AND     tl.transcript_id = t.transcript_id
01056       AND     tl.translation_id = pf.translation_id
01057       AND     i.id = pf.hit_name
01058       AND     i.interpro_ac = x.dbprimary_acc
01059       AND     t.is_current = 1
01060   ));
01061 
01062   $sth->bind_param(1, $trans_stable_id, SQL_VARCHAR);
01063   $sth->execute();
01064 
01065   my @out;
01066   my %h;
01067   while( (my $arr = $sth->fetchrow_arrayref()) ) {
01068      if( $h{$arr->[0]} ) { next; }
01069      $h{$arr->[0]}=1;
01070      my $string = $arr->[0] .":".$arr->[1];
01071      push(@out,$string);
01072   }
01073 
01074   return \@out;
01075 }
01076 
01077 =head2 is_Transcript_canonical()
01078 
01079   Arg [1]     : Bio::EnsEMBL::Transcript $transcript
01080                 The transcript to query with
01081   Example     : $tr_adaptor->is_Transcript_canonical($transcript);
01082   Description : Returns a boolean if the given transcript is considered
01083                 canonical with respect to a gene
01084   Returntype  : Boolean
01085   Exceptions  : None
01086   Caller      : Bio::EnsEMBL::Transcript
01087   Status      : Beta
01088   
01089 
01090 =cut
01091 
01092 sub is_Transcript_canonical {
01093   my ($self, $transcript) = @_;
01094   return $self->dbc()->sql_helper()->execute_single_result(
01095     -SQL => 'select count(*) from gene where canonical_transcript_id =?', 
01096     -PARAMS => [$transcript->dbID()]
01097   );
01098 }
01099 
01100 
01101 =head2 remove
01102 
01103   Arg [1]    : Bio::EnsEMBL::Transcript $transcript
01104                The transcript to remove from the database
01105   Example    : $tr_adaptor->remove($transcript);
01106   Description: Removes a transcript completely from the database, and all
01107                associated information.
01108                This method is usually called by the GeneAdaptor::remove method
01109                because this method will not preform the removal of genes
01110                which are associated with this transcript. Do not call this
01111                method directly unless you know there are no genes associated
01112                with the transcript!
01113   Returntype : none
01114   Exceptions : throw on incorrect arguments
01115                warning if transcript is not in this database
01116   Caller     : GeneAdaptor::remove
01117   Status     : Stable
01118 
01119 =cut
01120 
01121 sub remove {
01122   my $self = shift;
01123   my $transcript = shift;
01124 
01125   if(!ref($transcript) || !$transcript->isa('Bio::EnsEMBL::Transcript')) {
01126     throw("Bio::EnsEMBL::Transcript argument expected");
01127   }
01128 
01129   # sanity check: make sure nobody tries to slip past a prediction transcript
01130   # which inherits from transcript but actually uses different tables
01131   if($transcript->isa('Bio::EnsEMBL::PredictionTranscript')) {
01132     throw("TranscriptAdaptor can only remove Transcripts " .
01133           "not PredictionTranscripts");
01134   }
01135 
01136   if ( !$transcript->is_stored($self->db()) ) {
01137     warning("Cannot remove transcript ". $transcript->dbID .". Is not stored ".
01138             "in this database.");
01139     return;
01140   }
01141 
01142   # remove the supporting features of this transcript
01143 
01144   my $prot_adp = $self->db->get_ProteinAlignFeatureAdaptor;
01145   my $dna_adp = $self->db->get_DnaAlignFeatureAdaptor;
01146 
01147   my $sfsth = $self->prepare("SELECT feature_type, feature_id  " .
01148                              "FROM transcript_supporting_feature " .
01149                              "WHERE transcript_id = ?");
01150 
01151   $sfsth->bind_param(1, $transcript->dbID, SQL_INTEGER);
01152   $sfsth->execute();
01153 
01154   # statements to check for shared align_features
01155   my $sth1 = $self->prepare("SELECT count(*) FROM supporting_feature " .
01156                 "WHERE feature_type = ? AND feature_id = ?");
01157   my $sth2 = $self->prepare("SELECT count(*) " .
01158                             "FROM transcript_supporting_feature " .
01159                 "WHERE feature_type = ? AND feature_id = ?");
01160 
01161   SUPPORTING_FEATURE:
01162   while(my ($type, $feature_id) = $sfsth->fetchrow()){
01163     
01164     # only remove align_feature if this is the last reference to it
01165     $sth1->bind_param(1, $type, SQL_VARCHAR);
01166     $sth1->bind_param(2, $feature_id, SQL_INTEGER);
01167     $sth1->execute;
01168     $sth2->bind_param(1, $type, SQL_VARCHAR);
01169     $sth2->bind_param(2, $feature_id, SQL_INTEGER);
01170     $sth2->execute;
01171     my ($count1) = $sth1->fetchrow;
01172     my ($count2) = $sth2->fetchrow;
01173     if ($count1 + $count2 > 1) {
01174       #warn "transcript: shared feature, not removing $type|$feature_id\n";
01175       next SUPPORTING_FEATURE;
01176     }
01177     
01178     #warn "transcript: removing $type|$feature_id\n";
01179   
01180     if($type eq 'protein_align_feature'){
01181       my $f = $prot_adp->fetch_by_dbID($feature_id);
01182       $prot_adp->remove($f);
01183     }
01184     elsif($type eq 'dna_align_feature'){
01185       my $f = $dna_adp->fetch_by_dbID($feature_id);
01186       $dna_adp->remove($f);
01187     }
01188     else {
01189       warning("Unknown supporting feature type $type. Not removing feature.");
01190     }
01191   }
01192   $sfsth->finish();
01193   $sth1->finish();
01194   $sth2->finish();
01195 
01196   # delete the association to supporting features
01197 
01198   $sfsth = $self->prepare("DELETE FROM transcript_supporting_feature WHERE transcript_id = ?");
01199   $sfsth->bind_param(1, $transcript->dbID, SQL_INTEGER);
01200   $sfsth->execute();
01201   $sfsth->finish();
01202 
01203   # remove all xref linkages to this transcript
01204 
01205   my $dbeAdaptor = $self->db->get_DBEntryAdaptor();
01206   foreach my $dbe (@{$transcript->get_all_DBEntries}) {
01207     $dbeAdaptor->remove_from_object($dbe, $transcript, 'Transcript');
01208   }
01209 
01210   # remove the attributes associated with this transcript
01211   my $attrib_adp = $self->db->get_AttributeAdaptor;  
01212   $attrib_adp->remove_from_Transcript($transcript);
01213 
01214   # remove the translation associated with this transcript
01215 
01216   my $translationAdaptor = $self->db->get_TranslationAdaptor();
01217   if( defined($transcript->translation()) ) {
01218     $translationAdaptor->remove( $transcript->translation );
01219   }
01220 
01221   # remove exon associations to this transcript
01222 
01223   my $exonAdaptor = $self->db->get_ExonAdaptor();
01224   foreach my $exon ( @{$transcript->get_all_Exons()} ) {
01225     # get the number of transcript references to this exon
01226     # only remove the exon if this is the last transcript to
01227     # reference it
01228 
01229     my $sth = $self->prepare( "SELECT count(*)
01230                                FROM   exon_transcript
01231                                WHERE  exon_id = ?" );
01232     $sth->bind_param(1, $exon->dbID, SQL_INTEGER);
01233     $sth->execute();
01234     my ($count) = $sth->fetchrow_array();
01235     $sth->finish();
01236 
01237     if($count == 1){
01238       $exonAdaptor->remove( $exon );
01239     }
01240   }
01241 
01242   my $sth = $self->prepare( "DELETE FROM exon_transcript
01243                              WHERE transcript_id = ?" );
01244   $sth->bind_param(1, $transcript->dbID, SQL_INTEGER);
01245   $sth->execute();
01246   $sth->finish();
01247 
01248 
01249   $sth = $self->prepare( "DELETE FROM transcript
01250                           WHERE transcript_id = ?" );
01251   $sth->bind_param(1, $transcript->dbID, SQL_INTEGER);
01252   $sth->execute();
01253   $sth->finish();
01254 
01255   $transcript->dbID(undef);
01256   $transcript->adaptor(undef);
01257 
01258   return;
01259 }
01260 
01261 
01262 =head2 update
01263 
01264   Arg [1]    : Bio::EnsEMBL::Transcript $transcript
01265                The transcript to update
01266   Example    : $tr_adaptor->update($transcript);
01267   Description: Updates a transcript in the database.
01268   Returntype : None
01269   Exceptions : thrown if the $transcript is not a Bio::EnsEMBL::Transcript.
01270                warn if the method is called on a transcript that does not exist 
01271                in the database.
01272                Should warn if trying to update the number of attached exons, but
01273                this is a far more complex process and is not yet implemented.
01274   Caller     : general
01275   Status     : Stable
01276 
01277 =cut
01278 
01279 sub update {
01280   my ( $self, $transcript ) = @_;
01281 
01282   if (    !defined($transcript)
01283        || !ref($transcript)
01284        || !$transcript->isa('Bio::EnsEMBL::Transcript') )
01285   {
01286     throw("Must update a transcript object, not a $transcript");
01287   }
01288 
01289   my $update_transcript_sql = qq(
01290        UPDATE transcript
01291           SET analysis_id = ?,
01292               display_xref_id = ?,
01293               description = ?,
01294               biotype = ?,
01295               status = ?,
01296               is_current = ?,
01297               canonical_translation_id = ?
01298         WHERE transcript_id = ?
01299   );
01300 
01301   my $display_xref = $transcript->display_xref();
01302   my $display_xref_id;
01303 
01304   if ( defined($display_xref) && $display_xref->dbID() ) {
01305     $display_xref_id = $display_xref->dbID();
01306   } else {
01307     $display_xref_id = undef;
01308   }
01309 
01310   my $sth = $self->prepare($update_transcript_sql);
01311 
01312   $sth->bind_param( 1, $transcript->analysis()->dbID(), SQL_INTEGER );
01313   $sth->bind_param( 2, $display_xref_id, SQL_INTEGER );
01314   $sth->bind_param( 3, $transcript->description(), SQL_LONGVARCHAR );
01315   $sth->bind_param( 4, $transcript->biotype(),     SQL_VARCHAR );
01316   $sth->bind_param( 5, $transcript->status(),      SQL_VARCHAR );
01317   $sth->bind_param( 6, $transcript->is_current(),  SQL_TINYINT );
01318   $sth->bind_param( 7, (
01319                       defined( $transcript->translation() )
01320                       ? $transcript->translation()->dbID()
01321                       : undef ),
01322                     SQL_INTEGER );
01323   $sth->bind_param( 8, $transcript->dbID(), SQL_INTEGER );
01324 
01325   $sth->execute();
01326 } ## end sub update
01327 
01328 
01329 =head2 list_dbIDs
01330 
01331   Example    : @transcript_ids = @{ $t_adaptor->list_dbIDs };
01332   Description: Gets a list of internal ids for all transcripts in the db.
01333   Arg[1]     : <optional> int. not 0 for the ids to be sorted by the seq_region.  Returntype : Listref of Ints
01334   Exceptions : none
01335   Caller     : general
01336   Status     : Stable
01337 
01338 =cut
01339 
01340 sub list_dbIDs {
01341    my ($self, $ordered) = @_;
01342 
01343    return $self->_list_dbIDs("transcript",undef, $ordered);
01344 }
01345 
01346 
01347 =head2 list_stable_ids
01348 
01349   Example    : @stable_trans_ids = @{ $transcript_adaptor->list_stable_ids };
01350   Description: Gets a list of stable ids for all transcripts in the current
01351                database.
01352   Returntype : Listref of Strings
01353   Exceptions : none
01354   Caller     : general
01355   Status     : Stable
01356 
01357 =cut
01358 
01359 sub list_stable_ids {
01360    my ($self) = @_;
01361 
01362    return $self->_list_dbIDs("transcript", "stable_id");
01363 }
01364 
01365 
01366 #_objs_from_sth
01367 
01368 #  Arg [1]    : StatementHandle $sth
01369 #  Arg [2]    : Bio::EnsEMBL::AssemblyMapper $mapper
01370 #  Arg [3]    : Bio::EnsEMBL::Slice $dest_slice
01371 #  Description: PROTECTED implementation of abstract superclass method.
01372 #               Responsible for the creation of Transcripts.
01373 #  Returntype : Listref of Bio::EnsEMBL::Transcripts in target coord system
01374 #  Exceptions : none
01375 #  Caller     : internal
01376 #  Status     : Stable
01377 
01378 sub _objs_from_sth {
01379   my ($self, $sth, $mapper, $dest_slice) = @_;
01380 
01381   #
01382   # This code is ugly because an attempt has been made to remove as many
01383   # function calls as possible for speed purposes.  Thus many caches and
01384   # a fair bit of gymnastics is used.
01385   #
01386 
01387   my $sa = $self->db()->get_SliceAdaptor();
01388   my $aa = $self->db->get_AnalysisAdaptor();
01389   my $dbEntryAdaptor = $self->db()->get_DBEntryAdaptor();
01390 
01391   my @transcripts;
01392   my %analysis_hash;
01393   my %slice_hash;
01394   my %sr_name_hash;
01395   my %sr_cs_hash;
01396 
01397   my (
01398     $transcript_id,  $seq_region_id,      $seq_region_start,
01399     $seq_region_end, $seq_region_strand,  $analysis_id,
01400     $gene_id,        $is_current,         $stable_id,
01401     $version,        $created_date,       $modified_date,
01402     $description,    $biotype,            $status,
01403     $external_db,    $external_status,    $external_db_name,
01404     $xref_id,        $xref_display_label, $xref_primary_acc,
01405     $xref_version,   $xref_description,   $xref_info_type,
01406     $xref_info_text
01407   );
01408 
01409   $sth->bind_columns(
01410     \(
01411       $transcript_id,  $seq_region_id,      $seq_region_start,
01412       $seq_region_end, $seq_region_strand,  $analysis_id,
01413       $gene_id,        $is_current,         $stable_id,
01414       $version,        $created_date,       $modified_date,
01415       $description,    $biotype,            $status,
01416       $external_db,    $external_status,    $external_db_name,
01417       $xref_id,        $xref_display_label, $xref_primary_acc,
01418       $xref_version,   $xref_description,   $xref_info_type,
01419       $xref_info_text
01420     ) );
01421 
01422   my $asm_cs;
01423   my $cmp_cs;
01424   my $asm_cs_vers;
01425   my $asm_cs_name;
01426   my $cmp_cs_vers;
01427   my $cmp_cs_name;
01428   if($mapper) {
01429     $asm_cs = $mapper->assembled_CoordSystem();
01430     $cmp_cs = $mapper->component_CoordSystem();
01431     $asm_cs_name = $asm_cs->name();
01432     $asm_cs_vers = $asm_cs->version();
01433     $cmp_cs_name = $cmp_cs->name();
01434     $cmp_cs_vers = $cmp_cs->version();
01435   }
01436 
01437   my $dest_slice_start;
01438   my $dest_slice_end;
01439   my $dest_slice_strand;
01440   my $dest_slice_length;
01441   my $dest_slice_cs;
01442   my $dest_slice_sr_name;
01443   my $dest_slice_sr_id;
01444 
01445   my $asma;
01446   if($dest_slice) {
01447     $dest_slice_start  = $dest_slice->start();
01448     $dest_slice_end    = $dest_slice->end();
01449     $dest_slice_strand = $dest_slice->strand();
01450     $dest_slice_length = $dest_slice->length();
01451     $dest_slice_cs     = $dest_slice->coord_system();
01452     $dest_slice_sr_name = $dest_slice->seq_region_name();
01453     $dest_slice_sr_id   = $dest_slice->get_seq_region_id();
01454     $asma              = $self->db->get_AssemblyMapperAdaptor();
01455   }
01456 
01457   FEATURE: while($sth->fetch()) {
01458 
01459     #get the analysis object
01460     my $analysis = $analysis_hash{$analysis_id} ||=
01461       $aa->fetch_by_dbID($analysis_id);
01462     #need to get the internal_seq_region, if present
01463     $seq_region_id = $self->get_seq_region_id_internal($seq_region_id);
01464     my $slice = $slice_hash{"ID:".$seq_region_id};
01465     my $dest_mapper = $mapper;
01466 
01467     if(!$slice) {
01468       $slice = $sa->fetch_by_seq_region_id($seq_region_id);
01469       $slice_hash{"ID:".$seq_region_id} = $slice;
01470       $sr_name_hash{$seq_region_id} = $slice->seq_region_name();
01471       $sr_cs_hash{$seq_region_id} = $slice->coord_system();
01472     }
01473 
01474     #obtain a mapper if none was defined, but a dest_seq_region was
01475     if(!$dest_mapper && $dest_slice && 
01476        !$dest_slice_cs->equals($slice->coord_system)) {
01477       $dest_mapper = $asma->fetch_by_CoordSystems($dest_slice_cs,
01478                                                  $slice->coord_system);
01479       $asm_cs = $dest_mapper->assembled_CoordSystem();
01480       $cmp_cs = $dest_mapper->component_CoordSystem();
01481       $asm_cs_name = $asm_cs->name();
01482       $asm_cs_vers = $asm_cs->version();
01483       $cmp_cs_name = $cmp_cs->name();
01484       $cmp_cs_vers = $cmp_cs->version();
01485     }
01486 
01487     my $sr_name = $sr_name_hash{$seq_region_id};
01488     my $sr_cs   = $sr_cs_hash{$seq_region_id};
01489     #
01490     # remap the feature coordinates to another coord system 
01491     # if a mapper was provided
01492     #
01493     if($dest_mapper) {
01494 
01495       ($seq_region_id,$seq_region_start,$seq_region_end,$seq_region_strand) =
01496         $dest_mapper->fastmap($sr_name, $seq_region_start, $seq_region_end,
01497                               $seq_region_strand, $sr_cs);
01498 
01499       #skip features that map to gaps or coord system boundaries
01500       next FEATURE if(!defined($seq_region_id));
01501 
01502       #get a slice in the coord system we just mapped to
01503       if($asm_cs == $sr_cs || ($cmp_cs != $sr_cs && $asm_cs->equals($sr_cs))) {
01504         $slice = $slice_hash{"ID:".$seq_region_id} ||=
01505           $sa->fetch_by_seq_region_id($seq_region_id);
01506       } else {
01507         $slice = $slice_hash{"ID:".$seq_region_id} ||=
01508           $sa->fetch_by_seq_region_id($seq_region_id);
01509       }
01510     }
01511 
01512     #
01513     # If a destination slice was provided convert the coords.
01514     #
01515     if (defined($dest_slice)) {
01516       if ( $dest_slice_strand == 1 ) {
01517         $seq_region_start = $seq_region_start - $dest_slice_start + 1;
01518         $seq_region_end   = $seq_region_end - $dest_slice_start + 1;
01519 
01520         if ( $dest_slice->is_circular ) {
01521           if ( $seq_region_start > $seq_region_end ) {
01522             # Looking at a feature overlapping the chromsome origin.
01523             if ( $seq_region_end > $dest_slice_start ) {
01524               # Looking at the region in the beginning of the chromosome
01525               $seq_region_start -= $dest_slice->seq_region_length();
01526             }
01527             if ( $seq_region_end < 0 ) {
01528               $seq_region_end += $dest_slice->seq_region_length();
01529             }
01530           } else {
01531             if (    $dest_slice_start > $dest_slice_end
01532                  && $seq_region_end < 0 )
01533             {
01534               # Looking at the region overlapping the chromosome
01535               # origin and a feature which is at the beginning of the
01536               # chromosome.
01537               $seq_region_start += $dest_slice->seq_region_length();
01538               $seq_region_end   += $dest_slice->seq_region_length();
01539             }
01540           }
01541         }
01542       } else {
01543         if (    $dest_slice->is_circular()
01544              && $seq_region_start > $seq_region_end )
01545         {
01546           if ( $seq_region_end > $dest_slice_start ) {
01547             # Looking at the region in the beginning of the chromosome.
01548             $seq_region_start = $dest_slice_end - $seq_region_end + 1;
01549             $seq_region_end =
01550               $seq_region_end -
01551               $dest_slice->seq_region_length() -
01552               $dest_slice_start + 1;
01553           } else {
01554             my $tmp_seq_region_start = $seq_region_start;
01555             $seq_region_start =
01556               $dest_slice_end -
01557               $seq_region_end -
01558               $dest_slice->seq_region_length() + 1;
01559             $seq_region_end =
01560               $dest_slice_end - $tmp_seq_region_start + 1;
01561           }
01562 
01563         } else {
01564           my $tmp_seq_region_start = $seq_region_start;
01565           $seq_region_start = $dest_slice_end - $seq_region_end + 1;
01566           $seq_region_end = $dest_slice_end - $tmp_seq_region_start + 1;
01567         }
01568 
01569         $seq_region_strand = -$seq_region_strand;
01570       } ## end else [ if ( $dest_slice_strand...)]
01571 
01572       # Throw away features off the end of the requested slice
01573       if (    $seq_region_end < 1
01574            || $seq_region_start > $dest_slice_length
01575            || ( $dest_slice_sr_id ne $seq_region_id ) )
01576       {
01577         next FEATURE;
01578       }
01579 
01580       $slice = $dest_slice;
01581     }
01582 
01583     my $display_xref;
01584 
01585     if ($xref_id) {
01586       $display_xref = Bio::EnsEMBL::DBEntry->new_fast( {
01587           'dbID'            => $xref_id,
01588           'display_id'      => $xref_display_label,
01589           'primary_id'      => $xref_primary_acc,
01590           'version'         => $xref_version,
01591           'description'     => $xref_description,
01592           'info_type'       => $xref_info_type,
01593           'info_text'       => $xref_info_text,
01594           'adaptor'         => $dbEntryAdaptor,
01595           'db_display_name' => $external_db_name,
01596           'dbname'          => $external_db
01597       } );
01598     }
01599 
01600 
01601     # Finally, create the new Transcript.
01602     push(
01603       @transcripts,
01604       $self->_create_feature_fast(
01605         'Bio::EnsEMBL::Transcript',
01606         {
01607           'analysis'              => $analysis,
01608           'start'                 => $seq_region_start,
01609           'end'                   => $seq_region_end,
01610           'strand'                => $seq_region_strand,
01611           'adaptor'               => $self,
01612           'slice'                 => $slice,
01613           'dbID'                  => $transcript_id,
01614           'stable_id'             => $stable_id,
01615           'version'               => $version,
01616           'created_date'          => $created_date || undef,
01617           'modified_date'         => $modified_date || undef,
01618           'external_name'         => $xref_display_label,
01619           'external_db'           => $external_db,
01620           'external_status'       => $external_status,
01621           'external_display_name' => $external_db_name,
01622           'display_xref'          => $display_xref,
01623           'description'           => $description,
01624           'biotype'               => $biotype,
01625           'status'                => $status,
01626           'is_current'            => $is_current,
01627           'edits_enabled'         => 1
01628         } ) );
01629 
01630   }
01631 
01632   return \@transcripts;
01633 }
01634 
01635 
01636 =head2 fetch_all_by_exon_supporting_evidence
01637 
01638   Arg [1]    : String $hit_name
01639                Name of supporting feature
01640   Arg [2]    : String $feature_type 
01641                one of "dna_align_feature" or "protein_align_feature"
01642   Arg [3]    : (optional) Bio::Ensembl::Analysis
01643   Example    : $tr = $tr_adaptor->fetch_all_by_exon_supporting_evidence
01644                   ('XYZ', 'dna_align_feature');
01645   Description: Gets all the transcripts with exons which have a specified hit
01646                on a particular type of feature. Optionally filter by analysis.
01647   Returntype : Listref of Bio::EnsEMBL::Transcript objects
01648   Exceptions : If feature_type is not of correct type.
01649   Caller     : general
01650   Status     : Stable
01651 
01652 =cut
01653 
01654 sub fetch_all_by_exon_supporting_evidence {
01655   my ($self, $hit_name, $feature_type, $analysis) = @_;
01656 
01657   if($feature_type !~ /(dna)|(protein)_align_feature/) {
01658     throw("feature type must be dna_align_feature or protein_align_feature");
01659   }
01660 
01661   my $anal_from = "";
01662   $anal_from = ", analysis a " if ($analysis);
01663   my $anal_where = "";
01664   $anal_where = "AND a.analysis_id = f.analysis_id AND a.analysis_id=? "
01665     if ($analysis);
01666 
01667   my $sql = qq(
01668       SELECT DISTINCT(t.transcript_id)
01669         FROM transcript t,
01670              exon_transcript et,
01671              supporting_feature sf,
01672              $feature_type f
01673              $anal_from
01674        WHERE t.transcript_id = et.transcript_id
01675          AND t.is_current = 1
01676          AND et.exon_id = sf.exon_id
01677          AND sf.feature_id = f.${feature_type}_id
01678          AND sf.feature_type = ?
01679          AND f.hit_name=?
01680          $anal_where
01681   );
01682 
01683   my $sth = $self->prepare($sql);
01684 
01685   $sth->bind_param(1, $feature_type, SQL_VARCHAR);
01686   $sth->bind_param(2, $hit_name, SQL_VARCHAR);
01687   $sth->bind_param(3, $analysis->dbID(), SQL_INTEGER) if ($analysis);
01688 
01689   $sth->execute();
01690 
01691   my @transcripts;
01692 
01693   while( my $id = $sth->fetchrow_array ) {
01694     my $transcript = $self->fetch_by_dbID( $id  );
01695     push(@transcripts, $transcript) if $transcript;
01696   }
01697 
01698   return \@transcripts;
01699 }
01700 
01701 
01702 =head2 fetch_all_by_transcript_supporting_evidence
01703 
01704   Arg [1]    : String $hit_name
01705                Name of supporting feature
01706   Arg [2]    : String $feature_type 
01707                one of "dna_align_feature" or "protein_align_feature"
01708   Arg [3]    : (optional) Bio::Ensembl::Analysis
01709   Example    : $transcripts = $transcript_adaptor->fetch_all_by_transcript_supporting_evidence('XYZ', 'dna_align_feature');
01710   Description: Gets all the transcripts with evidence from a specified hit_name on a particular type of feature, stored in the  
01711                transcript_supporting_feature table. Optionally filter by analysis.  For hits stored in the supporting_feature 
01712                table (linked to exons) use fetch_all_by_exon_supporting_evidence instead.
01713   Returntype : Listref of Bio::EnsEMBL::Transcript objects
01714   Exceptions : If feature_type is not of correct type.
01715   Caller     : general
01716   Status     : Stable
01717 
01718 =cut
01719 
01720 sub fetch_all_by_transcript_supporting_evidence {
01721   
01722   my ($self, $hit_name, $feature_type, $analysis) = @_;
01723 
01724   if($feature_type !~ /(dna)|(protein)_align_feature/) {
01725     throw("feature type must be dna_align_feature or protein_align_feature");
01726   }
01727 
01728   my $anal_from = "";
01729   $anal_from = ", analysis a " if ($analysis);
01730   my $anal_where = "";
01731   $anal_where = "AND a.analysis_id = f.analysis_id AND a.analysis_id=? "
01732     if ($analysis);
01733 
01734   my $sql = qq(
01735       SELECT DISTINCT(t.transcript_id)
01736         FROM transcript t,
01737              transcript_supporting_feature sf,
01738              $feature_type f
01739              $anal_from
01740        WHERE t.transcript_id = sf.transcript_id
01741          AND t.is_current = 1
01742          AND sf.feature_id = f.${feature_type}_id
01743          AND sf.feature_type = ?
01744          AND f.hit_name=?
01745          $anal_where
01746   );
01747 
01748   my $sth = $self->prepare($sql);
01749 
01750   $sth->bind_param(1, $feature_type, SQL_VARCHAR);
01751   $sth->bind_param(2, $hit_name, SQL_VARCHAR);
01752   $sth->bind_param(3, $analysis->dbID(), SQL_INTEGER) if ($analysis);
01753 
01754   $sth->execute();
01755 
01756   my @transcripts;
01757 
01758   while( my $id = $sth->fetchrow_array ) {
01759     my $transcript = $self->fetch_by_dbID( $id  );
01760     push(@transcripts, $transcript) if $transcript;
01761   }
01762 
01763   return \@transcripts;
01764 }
01765 
01766 
01767 ##########################
01768 #                        #
01769 #  DEPRECATED METHODS    #
01770 #                        #
01771 ##########################
01772 
01773 
01774 =head2 get_display_xref
01775 
01776   Description: DEPRECATED. Use $transcript->display_xref() instead.
01777 
01778 =cut
01779 
01780 sub get_display_xref {
01781   my ($self, $transcript) = @_;
01782     
01783   deprecate("display_xref should be retreived from Transcript object directly.");
01784   
01785   if ( !defined $transcript ) {
01786     throw("Must call with a Transcript object");
01787   }
01788 
01789   my $sth = $self->prepare(qq(
01790       SELECT e.db_name,
01791              x.display_label,
01792              e.db_external_name,
01793              x.xref_id
01794       FROM   transcript t, 
01795              xref x, 
01796              external_db e
01797       WHERE  t.transcript_id = ?
01798         AND  t.display_xref_id = x.xref_id
01799         AND  x.external_db_id = e.external_db_id
01800   ));
01801   
01802   $sth->bind_param(1, $transcript->dbID, SQL_INTEGER);
01803   $sth->execute();
01804 
01805   my ($db_name, $display_label, $xref_id, $display_db_name ) =
01806     $sth->fetchrow_array();
01807   
01808   if ( !defined $xref_id ) {
01809     return undef;
01810   }
01811 
01812   my $db_entry = Bio::EnsEMBL::DBEntry->new(
01813      -dbid => $xref_id,
01814      -adaptor => $self->db->get_DBEntryAdaptor(),
01815      -dbname => $db_name,
01816      -display_id => $display_label
01817      -db_display_name => $display_db_name
01818   );
01819 
01820   return $db_entry;
01821 }
01822 
01823 
01824 =head2 get_stable_entry_info
01825 
01826   Description: DEPRECATED. Use $transcript->stable_id() instead.
01827 
01828 =cut
01829 
01830 sub get_stable_entry_info {
01831   my ($self, $transcript) = @_;
01832 
01833   deprecate("Stable ids should be loaded directly now");
01834 
01835   unless ( defined $transcript && ref $transcript && 
01836       $transcript->isa('Bio::EnsEMBL::Transcript') ) {
01837     throw("Needs a Transcript object, not a $transcript");
01838   }
01839 
01840   my $sth = $self->prepare(qq(
01841       SELECT stable_id, version 
01842       FROM   transcript
01843       WHERE  transcript_id = ?
01844   ));
01845                             
01846   $sth->bind_param(1, $transcript->dbID, SQL_INTEGER);
01847   $sth->execute();
01848 
01849   my @array = $sth->fetchrow_array();
01850   $transcript->{'_stable_id'} = $array[0];
01851   $transcript->{'_version'}   = $array[1];
01852 
01853   return 1;
01854 }
01855 
01856 
01857 =head2 fetch_all_by_DBEntry
01858 
01859   Description: DEPRECATED. Use fetch_all_by_external_name() instead.
01860 
01861 =cut
01862 
01863 sub fetch_all_by_DBEntry {
01864   my $self = shift;
01865   deprecate('Use fetch_all_by_external_name instead.');
01866   return $self->fetch_all_by_external_name(@_);
01867 }
01868 
01869 
01870 1;