001/** 002 * Copyright (C) 2010-2015 The Roslin Institute <contact andy.law@roslin.ed.ac.uk> 003 * 004 * This file is part of JEnsembl: a Java API to Ensembl data sources developed by the 005 * Bioinformatics Group at The Roslin Institute, The Royal (Dick) School of 006 * Veterinary Studies, University of Edinburgh. 007 * 008 * Project hosted at: http://jensembl.sourceforge.net 009 * 010 * This is free software: you can redistribute it and/or modify 011 * it under the terms of the GNU General Public License (version 3) as published by 012 * the Free Software Foundation. 013 * 014 * This software is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public License 020 * in this software distribution. If not, see: http://opensource.org/licenses/gpl-3.0.html 021 */ 022package uk.ac.roslin.ensembl.dao.database.coreaccess; 023 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.List; 027import org.apache.ibatis.session.SqlSession; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030import uk.ac.roslin.ensembl.config.DBConnection; 031import uk.ac.roslin.ensembl.config.EnsemblCoordSystemType; 032import uk.ac.roslin.ensembl.config.FeatureType; 033import uk.ac.roslin.ensembl.dao.coreaccess.TranscriptDAO; 034import uk.ac.roslin.ensembl.dao.factory.DAOCollectionCoreFactory; 035import uk.ac.roslin.ensembl.dao.factory.DAOCoreFactory; 036import uk.ac.roslin.ensembl.dao.factory.DAOSingleSpeciesCoreFactory; 037import uk.ac.roslin.ensembl.datasourceaware.DAXRef; 038import uk.ac.roslin.ensembl.datasourceaware.core.*; 039import uk.ac.roslin.ensembl.exception.DAOException; 040import uk.ac.roslin.ensembl.mapper.core.TranscriptMapper; 041import uk.ac.roslin.ensembl.mapper.handler.DBResultHandler; 042import uk.ac.roslin.ensembl.mapper.handler.HashMapHolder; 043import uk.ac.roslin.ensembl.mapper.query.FeatureQuery; 044import uk.ac.roslin.ensembl.model.*; 045import uk.ac.roslin.ensembl.model.core.CoordinateSystem; 046import uk.ac.roslin.ensembl.model.core.Gene; 047 048public class DBTranscriptDAO extends DBCoreObjectDAO implements TranscriptDAO { 049 050 //if the meta table tells us that transcript.build.level is 'toplevel' 051 //we can grab the top ranked CS and use this for all genes 052 //there may be other values than 'toplevel' - i dont know what :) 053 //if the build level isn't annotated we have to get the possible CS levels 054 //from meta_coord and use which ever one is approriate! 055 CoordinateSystem transcriptBuildCS = null; 056 final static Logger LOGGER = LoggerFactory.getLogger(DBTranscriptDAO.class); 057 058 public DBTranscriptDAO() { 059 super(); 060 } 061 062 public DBTranscriptDAO(DAOSingleSpeciesCoreFactory factory) throws DAOException { 063 super(factory); 064 065 //we want to initialize the GeneDAO so that it knows which COordinate system 066 //it needs to quwery about genes 067 068 transcriptBuildCS = this.ssFactory.getDatabase().getBuildCoordSystem(FeatureType.transcript.toString()); 069 //what if this is null or throws an exception? 070 //if it throws an exception we are probably in some sort of bad factory environment 071 if (transcriptBuildCS == null) { 072 transcriptBuildCS = this.ssFactory.getDatabase().getTopLevelCoordSystem(); 073 } 074 } 075 076 public DBTranscriptDAO(DAOCollectionCoreFactory factory) throws DAOException { 077 super(factory); 078 transcriptBuildCS = this.collFactory.getDatabase().getBuildCoordSystem(species, FeatureType.transcript.toString()); 079 //what if this is null or throw an exception? 080 //if it throws an exception we are probably in some sort of bad factory environment 081 if (transcriptBuildCS == null) { 082 transcriptBuildCS = this.collFactory.getDatabase().getTopLevelCS(species); 083 } 084 } 085 086 /** 087 * Uses the stableid of an object to fill in missing data 088 * @param object 089 * @throws DAOException 090 */ 091 @Override 092 public void reInitialize(IdentifiableObject object) throws DAOException { 093 094 if (object == null || !(object instanceof DATranscript)) { 095 throw new DAOException("Object not a DATranscript"); 096 } 097 098 DATranscript transcript = (DATranscript) object; 099 100 //the DAO method requires a stableID 101 if (transcript.getStableID() == null || transcript.getStableID().isEmpty()) { 102 return; 103 } 104 105 DATranscript temp = this.getTranscriptByStableID(transcript.getStableID()); 106 107 if (temp != null) { 108 transcript.setAnalysisID(temp.getAnalysisID()); 109 transcript.setDisplayXRef(temp.getDisplayXRef()); 110 transcript.setBiotype(temp.getBiotype()); 111 transcript.setId(temp.getId()); 112 transcript.setCreationDate(temp.getCreationDate()); 113 transcript.setModificationDate(temp.getModificationDate()); 114 transcript.setDescription(temp.getDescription()); 115 transcript.setCanonicalTranslationID(temp.getCanonicalTranslationID()); 116 transcript.setCurrent(temp.isCurrent()); 117 transcript.setDisplayName(temp.getDisplayName()); 118 transcript.setStatus(temp.getStatus()); 119 transcript.setGeneID(temp.getGeneID()); 120 121 122 for (Mapping tempMapping : temp.getLoadedMappings()) { 123 124 Mapping tempRevMapping = tempMapping.getReverseMapping(); 125 if (tempRevMapping != null) { 126 tempRevMapping.setTarget(transcript); 127 } 128 tempMapping.setSource(transcript); 129 transcript.addMapping(tempMapping); 130 131 } 132 } 133 transcript.setInitialized(true); 134 135 } 136 137 /** 138 * @param id 139 * 140 * @throws DAOException 141 */ 142 @Override 143 public DATranscript getTranscriptByID(Integer id) throws DAOException { 144 145 if (id == null) { 146 return null; 147 } 148 149 FeatureQuery q = new FeatureQuery(); 150 q.setFeatureID(id); 151 if (!singleSpecies) { 152 try { 153 q.setSpeciesID(species.getDBSpeciesID(this.getFactory().getDBVersion())); 154 } catch (Exception e) { 155 throw new DAOException("No species ID context for this query!", e); 156 } 157 } 158 return this.getTranscript(q); 159 } 160 161 /** 162 * @param stableID 163 * 164 * @throws DAOException 165 */ 166 @Override 167 public DATranscript getTranscriptByStableID(String stableID) throws DAOException { 168 if (stableID == null || stableID.isEmpty()) { 169 return null; 170 } 171 172 FeatureQuery q = new FeatureQuery(); 173 if (!singleSpecies) { 174 try { 175 q.setSpeciesID(species.getDBSpeciesID(this.getFactory().getDBVersion())); 176 } catch (Exception e) { 177 throw new DAOException("No species ID context for this query!", e); 178 } 179 } 180 q.setFeatureStableID(stableID.trim()); 181 return this.getTranscript(q); 182 } 183 184 @Override 185 public List<DATranscript> getTranscriptsForGene(Gene gene) throws DAOException { 186 if (gene == null || !(gene instanceof DAGene) || gene.getId() == null) { 187 return null; 188 } 189 190 DAGene daGene = (DAGene) gene; 191 192 FeatureQuery q = new FeatureQuery(); 193 if (!singleSpecies) { 194 try { 195 q.setSpeciesID(species.getDBSpeciesID(this.getFactory().getDBVersion())); 196 } catch (Exception e) { 197 throw new DAOException("No species ID context for this query!", e); 198 } 199 } 200 201 q.setGeneID(daGene.getId()); 202 203 return this.getTranscripts(q, daGene); 204 205 } 206 207 /** 208 * Returns a list of Ensembl Transcripts matching the query VegaID string. 209 * Vega currently curates gene, transcript and protein annotations for a human 210 * and and a few key regions of other vertebrate species. Calling this method 211 * on EnsemblGenomes (invertebrate) species will return an empty list by default. 212 * @param id a valid vega ID (these begin 'OTT...') 213 * 214 * @throws DAOException 215 */ 216 @Override 217 public List<DATranscript> getTranscriptsForVegaID(String id) throws DAOException { 218 219 //return an empty list rather than null 220 List<DATranscript> out = new ArrayList<DATranscript>(); 221 222 if (id==null || id.isEmpty() 223 || !this.getFactory().getRegistry().getDatasourceType().equals(DBConnection.DataSource.ENSEMBLDB)) { 224 return out; 225 } 226 227 List<HashMapHolder> l = null; 228 TranscriptRowHandler handler = null; 229 230 List<HashMap> result = new ArrayList<HashMap>(); 231 232 SqlSession session = null; 233 234 try { 235 session = this.getFactory().getNewSqlSession(); 236 TranscriptMapper mapper = session.getMapper(TranscriptMapper.class); 237 l = mapper.getVegaTranscripts(id); 238 } catch (Exception e) { 239 throw new DAOException("Failed to call getTranscriptsForVegaID", e); 240 } finally { 241 if (session != null) { 242 session.close(); 243 } 244 } 245 if (l != null) { 246 for (HashMapHolder h : l) { 247 248 result.add(h.getMap()); 249 250 } 251 252 if (result != null && !result.isEmpty()) { 253 254 handler = new TranscriptRowHandler(result); 255 handler.handleResult(); 256 out = handler.getListResult(); 257 } 258 } 259 260 261 if (out == null) { 262 out = new ArrayList<DATranscript>(); 263 } 264 265 /* 266 * NOTE: we don't set the query VegaID on the return gene as this being null 267 * is the trigger for lazyloading a vega XRef. 268 * NOR can we make a valid Vega XRef at this point as we dont have enough information 269 * (ie we dont have its database ID). 270 */ 271 272// if (!out.isEmpty()) { 273// ExternalDB edb = this.daoFactory.getDatabase().getExternalDB(ExternalDBType.VegaTranscript.toString()); 274// DAXRef xref = new DAXRef(); 275// xref.setDaoFactory(this.getFactory()); 276// xref.setDB(edb); 277// xref.setPrimaryAccession(id); 278// xref.setDisplayID(id); 279// xref.setDBName(edb.getDBName()); 280// 281// for (DATranscript g : out) { 282// g.setVegaTranscriptID(id); 283// g.addTypedXRefs(ExternalDBType.VegaTranscript, new ArrayList<DAXRef>(Arrays.asList(xref))); 284// } 285// } 286 return out; 287 } 288 289 /** 290 * Returns a list of Ensembl Transcripts matching the query CCDS ID string. 291 * CCDS currently curates consensus canonical transcripts for human and mouse. 292 * Calling this method 293 * on EnsemblGenomes (invertebrate) species will return an empty list by default. 294 * @param id a valid CCDS accession or accession.version (these begin 'CCDS...') 295 * 296 * @throws DAOException 297 */ 298 @Override 299 public List<DATranscript> getTranscriptsForCcdsID(String id) throws DAOException { 300 301 //return an empty list rather than null 302 List<DATranscript> out = new ArrayList<DATranscript>(); 303 304 if (id==null || id.isEmpty() 305 || !this.getFactory().getRegistry().getDatasourceType().equals(DBConnection.DataSource.ENSEMBLDB)) { 306 return out; 307 } 308 309 List<HashMapHolder> l = null; 310 TranscriptRowHandler handler = null; 311 312 List<HashMap> result = new ArrayList<HashMap>(); 313 314 SqlSession session = null; 315 316 try { 317 session = this.getFactory().getNewSqlSession(); 318 TranscriptMapper mapper = session.getMapper(TranscriptMapper.class); 319 l = mapper.getCcdsTranscripts(id); 320 } catch (Exception e) { 321 throw new DAOException("Failed to call getTranscriptsForCcdsID", e); 322 } finally { 323 if (session != null) { 324 session.close(); 325 } 326 } 327 if (l != null) { 328 for (HashMapHolder h : l) { 329 330 result.add(h.getMap()); 331 332 } 333 334 if (result != null && !result.isEmpty()) { 335 336 handler = new TranscriptRowHandler(result); 337 handler.handleResult(); 338 out = handler.getListResult(); 339 } 340 } 341 342 343 if (out == null) { 344 out = new ArrayList<DATranscript>(); 345 } 346 347 /* 348 * NOTE: we don't set the query ccdsID on the return gene as this being null 349 * is the trigger for lazyloading a CCDS XRef. 350 * NOR can we make a valid Ccds XRef at this point as we dont have enough information 351 * (ie we dont have its database ID). 352 */ 353 354// if (!out.isEmpty()) { 355// ExternalDB edb = this.daoFactory.getDatabase().getExternalDB(ExternalDBType.CCDS.toString()); 356// DAXRef xref = new DAXRef(); 357// xref.setDaoFactory(this.getFactory()); 358// xref.setDB(edb); 359// xref.setPrimaryAccession(id); 360// xref.setDisplayID(id); 361// xref.setDBName(edb.getDBName()); 362// 363// for (DATranscript g : out) { 364// g.setCCDSTranscriptID(id); 365// g.addTypedXRefs(ExternalDBType.CCDS, new ArrayList<DAXRef>(Arrays.asList(xref))); 366// } 367// } 368 return out; 369 } 370 371 372 //******************************* 373 private DATranscript getTranscript(FeatureQuery query) throws DAOException { 374 DATranscript out = null; 375 TranscriptRowHandler handler = null; 376 List<HashMap> result; 377 378 SqlSession session = null; 379 380 try { 381 session = this.getFactory().getNewSqlSession(); 382 TranscriptMapper mapper = session.getMapper(TranscriptMapper.class); 383 result = mapper.getTranscript(query); 384 } catch (Exception e) { 385 throw new DAOException("Failed to call getTranscript", e); 386 } finally { 387 if (session != null) { 388 session.close(); 389 } 390 } 391 392 if (result != null && !result.isEmpty()) { 393 394 handler = new TranscriptRowHandler(result); 395 handler.handleResult(); 396 out = handler.getObjectResult(); 397 } 398 399 return out; 400 } 401 402 private List<DATranscript> getTranscripts(FeatureQuery query, DAGene gene) throws DAOException { 403 List<DATranscript> out = null; 404 TranscriptRowHandler handler = null; 405 List<HashMap> result; 406 407 SqlSession session = null; 408 409 try { 410 session = this.getFactory().getNewSqlSession(); 411 TranscriptMapper mapper = session.getMapper(TranscriptMapper.class); 412 result = mapper.getTranscript(query); 413 } catch (Exception e) { 414 throw new DAOException("Failed to call getTranscripts", e); 415 } finally { 416 if (session != null) { 417 session.close(); 418 } 419 } 420 421 if (result != null && !result.isEmpty()) { 422 423 handler = new TranscriptRowHandler(result, gene); 424 handler.handleResult(); 425 out = handler.getListResult(); 426 } 427 428 return out; 429 } 430 431 //********************************* 432 //as an inner class it has access to the factory etc of the enclosing class 433 public class TranscriptRowHandler implements DBResultHandler { 434 435 // the magic strings to be used as property keys for HashMap in Ibatis 436 private final String transcript = "transcript"; 437 private final String xref = "xref"; 438 private final String target = "target"; 439 private final String coords = "coords"; 440 private final String coordSystemID = "csID"; 441// private final String xdb = "xdb"; 442 private DADNASequence parentSeq = null; 443 private DATranscript objectResult = null; 444 private List<DATranscript> listResult = new ArrayList<DATranscript>(); 445 private List<HashMap> rawResults = null; 446 private DAGene daGene = null; 447 448 public TranscriptRowHandler(List<HashMap> results, DAGene g) { 449 daGene = g; 450 rawResults = results; 451 if (daGene != null) { 452 try { 453 MappingSet m = daGene.getTopLevelMappings(); 454 parentSeq = (DADNASequence) m.first().getTarget(); 455 } catch (DAOException ex) { 456 LOGGER.info("DAOException trying to retrieve the Chromosome for the Gene", ex); 457 } 458 } 459 460 } 461 462 public TranscriptRowHandler(List<HashMap> results) { 463 rawResults = results; 464 } 465 466 @Override 467 public List<DATranscript> getListResult() { 468 return listResult; 469 } 470 471 @Override 472 public DATranscript getObjectResult() { 473 return objectResult; 474 } 475 476 public void handleResult() throws DAOException { 477 478 if (rawResults == null || rawResults.isEmpty()) { 479 return; 480 } 481 482 for (HashMap map : rawResults) { 483 handleRow(map); 484 } 485 486 } 487 488 private void handleRow(HashMap result) throws DAOException { 489 490 DATranscript rowTranscript = this.parseResult(result); 491 492 if (rowTranscript != null) { 493 this.objectResult = rowTranscript; 494 this.listResult.add(rowTranscript); 495 } 496 497 } 498 499 private DATranscript parseResult(HashMap result) throws DAOException { 500 501 if (result == null || result.isEmpty()) { 502 return null; 503 } 504 505 DADNASequence pparentSeq = parentSeq; 506 DATranscript ptranscript = null; 507 DADNASequence ptarget = null; 508 Coordinate pcoords = null; 509 DAXRef pxref = null; 510 Integer pcsID = null; 511// ExternalDB db = null; 512 513 ptranscript = (DATranscript) result.get(this.transcript); 514 pcoords = (Coordinate) result.get(this.coords); 515 ptarget = (DADNASequence) result.get(this.target); 516 pxref = (DAXRef) result.get(this.xref); 517 pcsID = (Integer) result.get(this.coordSystemID); 518// db = (ExternalDB) result.get(this.xdb); 519 520 if (ptranscript == null 521 || pcoords == null 522 || ptarget == null) { 523 return null; 524 } 525 526// if (db != null) { 527// int originalHashCode = db.originalHashCode(); 528// db = ((CoreDatabase) daoFactory.getDatabase()).validateExternalDB(db); 529// int checkedHashCode = db.originalHashCode(); 530// if (originalHashCode-checkedHashCode !=0) { 531// System.out.println("*** FAILED TO REUSE EXTERNALDB"); 532// } else { 533// System.out.println("*** successfully reused externaldb"); 534// } 535// } 536 537 //if we havent got a target DNA for the Gene, we can make and use one 538 //for the transcript, beware - may make >1 identically id'd sequence 539 //here tho cos not caching 540 if (pparentSeq == null) { 541 542 CoordinateSystem targetCS = null; 543 544 if (ptarget.getDaoFactory() == null) { 545 ptarget.setDaoFactory(daoFactory); 546 } 547 548 targetCS = ptarget.getCoordSystem(); 549 550 if (targetCS == null) { 551 if (singleSpecies) { 552 ptarget.setCoordSystem(ssFactory.getDatabase().getCSByID(pcsID)); 553 } else { 554 ptarget.setCoordSystem(collFactory.getDatabase().getCSByID(species, pcsID)); 555 } 556 targetCS = ptarget.getCoordSystem(); 557 } 558 559 560 if (targetCS.isSequenceLevel()) { 561 ptarget.setCoordSystem(targetCS); 562 } else if (targetCS.getType().equals(EnsemblCoordSystemType.chromosome)) { 563 ptarget = new DAChromosome((DAOCoreFactory) daoFactory); 564 ptarget.setId(((DADNASequence) result.get(this.target)).getId()); 565 ptarget.setName(((DADNASequence) result.get(this.target)).getName()); 566 ptarget.setDBSeqLength(((DADNASequence) result.get(this.target)).getDBSeqLength()); 567 ptarget.setSpecies(species); 568 ptarget.setCoordSystem(targetCS); 569 570 //loook to see if we have this sequence in cache 571 ptarget = species.getCachedChromosome((DAChromosome) ptarget); 572 573 } else { 574 ptarget = new DAAssembledDNASequence((DAOCoreFactory) daoFactory); 575 ptarget.setId(((DADNASequence) result.get(this.target)).getId()); 576 ptarget.setName(((DADNASequence) result.get(this.target)).getName()); 577 ptarget.setDBSeqLength(((DADNASequence) result.get(this.target)).getDBSeqLength()); 578 ptarget.setCoordSystem(targetCS); 579 580 //not got a cache of assembled sequences yet to check 581 } 582 583 584 } else { 585 //if the target DNASequence isn't the sequence that the gene is on 586 //throw this result away 587 //ultimately will need to modify this to map from alternate CS/sequences 588 if (!ptarget.getId().equals(pparentSeq.getId())) { 589 return null; 590 } 591 } 592 593 if (ptranscript.getDaoFactory() == null) { 594 //pgene.setType(FeatureType.transcript); 595 ptranscript.setDaoFactory(daoFactory); 596 } 597 598 if (ptranscript.getDisplayXRef() == null) { 599 pxref = (DAXRef) result.get(this.xref); 600 if (pxref != null) { 601 pxref.setDaoFactory(daoFactory); 602// pxref.setDB(db); 603 ptranscript.setDisplayXRef(pxref); 604 } 605 } 606 607 608 609 610 611// //check if the transcript object has somehow been returned from a cache and it 612// //has a mapping to a seq region with the same id as the parentSeq: if 613// //so, swap it for the parentSeq 614// for (Mapping m : pgene.getLoadedMappings()) { 615// if (m.getTarget().getId().equals(pparentSeq.getId())) { 616// if (pparentSeq.getDaoFactory() == null) { 617// pparentSeq.setDaoFactory(daoFactory); 618// } 619// if (pparentSeq.getCoordSystem() == null) { 620// if (singleSpecies) { 621// pparentSeq.setCoordSystem(ssFactory.getDatabase().getCSByID(pcsID)); 622// } else { 623// pparentSeq.setCoordSystem(collFactory.getDatabase().getCSByID(species, pcsID)); 624// } 625// } 626// //do the switch 627// m.setTarget(pparentSeq); 628// m.setTargetCoordinates(pcoords); 629// Mapping.addReverseMapping(m); 630// return pgene; 631// } 632// } 633// 634// 635 Mapping mapping = new Mapping(); 636 mapping.setSource(ptranscript); 637 mapping.setTargetCoordinates(pcoords); 638 639 if (pparentSeq != null) { 640 mapping.setTarget(pparentSeq); 641 } else { 642 mapping.setTarget(ptarget); 643 } 644 if (ptranscript.addMapping(mapping)) { 645 Mapping.addReverseMapping(mapping); 646 } 647 if (daGene != null) { 648 if (daGene.getCanonicalTranscriptID() != null 649 && ptranscript.getId() != null 650 && daGene.getCanonicalTranscriptID().compareTo(ptranscript.getId()) == 0) { 651 ptranscript.setCanonical(true); 652 daGene.setCanonicalTranscript(ptranscript); 653 } 654 daGene.addTranscript(ptranscript); 655 ptranscript.setGene(daGene); 656 } 657 return ptranscript; 658 659 660 } 661 } 662}