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.EnsemblCoordSystemType;
031import uk.ac.roslin.ensembl.config.FeatureType;
032import uk.ac.roslin.ensembl.dao.coreaccess.ExonDAO;
033import uk.ac.roslin.ensembl.dao.factory.DAOCollectionCoreFactory;
034import uk.ac.roslin.ensembl.dao.factory.DAOCoreFactory;
035import uk.ac.roslin.ensembl.dao.factory.DAOSingleSpeciesCoreFactory;
036import uk.ac.roslin.ensembl.datasourceaware.core.*;
037import uk.ac.roslin.ensembl.exception.DAOException;
038import uk.ac.roslin.ensembl.mapper.core.ExonMapper;
039import uk.ac.roslin.ensembl.mapper.handler.DBResultHandler;
040import uk.ac.roslin.ensembl.mapper.query.FeatureQuery;
041import uk.ac.roslin.ensembl.model.Coordinate;
042import uk.ac.roslin.ensembl.model.IdentifiableObject;
043import uk.ac.roslin.ensembl.model.Mapping;
044import uk.ac.roslin.ensembl.model.MappingSet;
045import uk.ac.roslin.ensembl.model.core.CoordinateSystem;
046import uk.ac.roslin.ensembl.model.core.Transcript;
047
048public class DBExonDAO extends DBCoreObjectDAO implements ExonDAO {
049
050    //if the meta table tells us that exon.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 exonBuildCS = null;
056    final static Logger LOGGER = LoggerFactory.getLogger(DBExonDAO.class);
057
058    public DBExonDAO() {
059        super();
060    }
061
062    public DBExonDAO(DAOSingleSpeciesCoreFactory factory) throws DAOException {
063        super(factory);
064        exonBuildCS = this.ssFactory.getDatabase().getBuildCoordSystem(FeatureType.exon.toString());
065        //what if this is null or throws an exception?
066        //if it throws an exception we are probably in some sort of bad factory environment
067        if (exonBuildCS == null) {
068            exonBuildCS = this.ssFactory.getDatabase().getTopLevelCoordSystem();
069        }
070    }
071
072    public DBExonDAO(DAOCollectionCoreFactory factory) throws DAOException {
073        super(factory);
074        exonBuildCS = this.collFactory.getDatabase().getBuildCoordSystem(species, FeatureType.exon.toString());
075        //what if this is null  or throw an exception?
076        //if it throws an exception we are probably in some sort of bad factory environment
077        if (exonBuildCS == null) {
078            exonBuildCS = this.collFactory.getDatabase().getTopLevelCS(species);
079        }
080    }
081
082    /**
083     * Uses the stableid of an object to fill in missing data 
084     * @param object
085     * @throws DAOException
086     */
087    @Override
088    public void reInitialize(IdentifiableObject object) throws DAOException {
089
090        if (object == null || !(object instanceof DAExon)) {
091            throw new DAOException("Object not a DAExon");
092        }
093
094        DAExon exon = (DAExon) object;
095
096        //the DAO method requires a stableID
097        if (exon.getStableID() == null || exon.getStableID().isEmpty()) {
098            return;
099        }
100
101        DAExon temp = this.getExonByStableID(exon.getStableID());
102
103
104        exon.setId(temp.getId());
105        exon.setCreationDate(temp.getCreationDate());
106        exon.setModificationDate(temp.getModificationDate());
107        exon.setDescription(temp.getDescription());
108        exon.setPhase(temp.getPhase());
109        exon.setEndPhase(temp.getEndPhase());
110        exon.setRank(temp.getRank());
111        exon.setCurrent(temp.isCurrent());
112        exon.setConstitutive(temp.isConstitutive());
113        exon.setTranscriptID(temp.getTranscriptID());
114        exon.setInitialized(true);
115        for (Mapping tempMapping : temp.getLoadedMappings()) {
116
117            Mapping tempRevMapping = tempMapping.getReverseMapping();
118            if (tempRevMapping != null) {
119                tempRevMapping.setTarget(exon);
120            }
121            tempMapping.setSource(exon);
122            exon.addMapping(tempMapping);
123
124        }
125
126    }
127
128    /**
129     * @param id
130     * 
131     * @throws DAOException
132     */
133    @Override
134    public DAExon getExonByID(Integer id) throws DAOException {
135
136        if (id == null) {
137            return null;
138        }
139        
140        FeatureQuery q = new FeatureQuery();
141        q.setFeatureID(id);
142        if (!singleSpecies) {
143            try {
144                q.setSpeciesID(species.getDBSpeciesID(this.getFactory().getDBVersion()));
145            } catch (Exception e) {
146                throw new DAOException("No species ID context for this query!", e);
147            }
148        }
149        return this.getExon(q);
150    }
151
152    /**
153     * @param stableID
154     * 
155     * @throws DAOException
156     */
157    @Override
158    public DAExon getExonByStableID(String stableID) throws DAOException {
159        if (stableID == null || stableID.isEmpty()) {
160            return null;
161        }
162
163        FeatureQuery q = new FeatureQuery();
164        if (!singleSpecies) {
165            try {
166                q.setSpeciesID(species.getDBSpeciesID(this.getFactory().getDBVersion()));
167            } catch (Exception e) {
168                throw new DAOException("No species ID context for this query!", e);
169            }
170        }
171        q.setFeatureStableID(stableID.trim());
172        return this.getExon(q);
173    }
174
175    @Override
176    public List<DAExon> getExonsForTranscript(Transcript transcript) throws DAOException {
177        if (transcript == null || !(transcript instanceof DATranscript) || transcript.getId() == null) {
178            return null;
179        }
180
181        DATranscript daTranscript = (DATranscript) transcript;
182
183        FeatureQuery q = new FeatureQuery();
184        if (!singleSpecies) {
185            try {
186                q.setSpeciesID(species.getDBSpeciesID(this.getFactory().getDBVersion()));
187            } catch (Exception e) {
188                throw new DAOException("No species ID context for this query!", e);
189            }
190        }
191
192        q.setTranscriptID(daTranscript.getId());
193
194        return this.getExons(q, daTranscript);
195
196    }
197
198    //*******************************
199    private DAExon getExon(FeatureQuery query) throws DAOException {
200        DAExon out = null;
201        ExonRowHandler handler = null;
202        List<HashMap> result;
203
204        SqlSession session = null;
205
206        try {
207            session = this.getFactory().getNewSqlSession();
208            ExonMapper mapper = session.getMapper(ExonMapper.class);
209            result = mapper.getExon(query);
210        } catch (Exception e) {
211            throw new DAOException("Failed to call getExon", e);
212        } finally {
213            if (session != null) {
214                session.close();
215            }
216        }
217
218        if (result != null && !result.isEmpty()) {
219
220            handler = new ExonRowHandler(result);
221            handler.handleResult();
222            out = handler.getObjectResult();
223        }
224
225        return out;
226    }
227
228    private List<DAExon> getExons(FeatureQuery query, DATranscript transcript) throws DAOException {
229        List<DAExon> out = null;
230        ExonRowHandler handler = null;
231        List<HashMap> result;
232
233        SqlSession session = null;
234
235        try {
236            session = this.getFactory().getNewSqlSession();
237            ExonMapper mapper = session.getMapper(ExonMapper.class);
238            result = mapper.getExon(query);
239        } catch (Exception e) {
240            throw new DAOException("Failed to call getExons", e);
241        } finally {
242            if (session != null) {
243                session.close();
244            }
245        }
246
247        if (result != null && !result.isEmpty()) {
248
249            handler = new ExonRowHandler(result, transcript);
250            handler.handleResult();
251            out = handler.getListResult();
252        }
253
254        return out;
255    }
256
257    //*********************************
258    //as an inner class it has access to the factory etc of the enclosing class
259    public class ExonRowHandler implements DBResultHandler {
260
261        // the magic strings to be used as property keys for HashMap in Ibatis
262        private final String exon = "exon";
263        private final String target = "target";
264        private final String coords = "coords";
265        private final String coordSystemID = "csID";
266        private DADNASequence parentSeq = null;
267        private DAExon objectResult = null;
268        private List<DAExon> listResult = new ArrayList<DAExon>();
269        private List<HashMap> rawResults = null;
270        private DATranscript daTranscript = null;
271
272        public ExonRowHandler(List<HashMap> results, DATranscript t) {
273            daTranscript = t;
274            rawResults = results;
275            if (daTranscript != null) {
276                try {
277                    MappingSet m = daTranscript.getTopLevelMappings();
278                    parentSeq = (DADNASequence) m.first().getTarget();
279                } catch (DAOException ex) {
280                    LOGGER.info("DAOException trying to retrieve the Chromosome for the Gene", ex);
281                }
282            }
283
284        }
285
286        public ExonRowHandler(List<HashMap> results) {
287            rawResults = results;
288        }
289
290        @Override
291        public List<DAExon> getListResult() {
292            return listResult;
293        }
294
295        @Override
296        public DAExon getObjectResult() {
297            return objectResult;
298        }
299
300        public void handleResult() throws DAOException {
301
302            if (rawResults == null || rawResults.isEmpty()) {
303                return;
304            }
305
306            for (HashMap map : rawResults) {
307                handleRow(map);
308            }
309
310        }
311
312        private void handleRow(HashMap result) throws DAOException {
313
314            DAExon rowExon = this.parseResult(result);
315
316
317            if (rowExon != null) {
318                this.objectResult = rowExon;
319                this.listResult.add(rowExon);
320            }
321
322        }
323
324        private DAExon parseResult(HashMap result) throws DAOException {
325
326            if (result == null || result.isEmpty()) {
327                return null;
328            }
329
330            DADNASequence pparentSeq = parentSeq;
331            DAExon pexon = null;
332            DADNASequence ptarget = null;
333            Coordinate pcoords = null;
334            Integer pcsID = null;
335
336            pexon = (DAExon) result.get(this.exon);
337            pcoords = (Coordinate) result.get(this.coords);
338            ptarget = (DADNASequence) result.get(this.target);
339            pcsID = (Integer) result.get(this.coordSystemID);
340
341            if (pexon == null
342                    || pcoords == null
343                    || ptarget == null) {
344                return null;
345            }
346            
347            //if we havent got a target DNA for the Gene, we can make and use one
348            //for the transcript, beware - may make >1 identically id'd sequence 
349            //here tho cos not caching
350            if (pparentSeq == null) {
351
352                CoordinateSystem targetCS = null;
353
354                if (ptarget.getDaoFactory() == null) {
355                    ptarget.setDaoFactory(daoFactory);
356                }
357
358                targetCS = ptarget.getCoordSystem();
359
360                if (targetCS == null) {
361                    if (singleSpecies) {
362                        ptarget.setCoordSystem(ssFactory.getDatabase().getCSByID(pcsID));
363                    } else {
364                        ptarget.setCoordSystem(collFactory.getDatabase().getCSByID(species, pcsID));
365                    }
366                    targetCS = ptarget.getCoordSystem();
367                }
368
369
370                if (targetCS.isSequenceLevel()) {
371                    ptarget.setCoordSystem(targetCS);
372                } else if (targetCS.getType().equals(EnsemblCoordSystemType.chromosome)) {
373                    ptarget = new DAChromosome((DAOCoreFactory) daoFactory);
374                    ptarget.setId(((DADNASequence) result.get(this.target)).getId());
375                    ptarget.setName(((DADNASequence) result.get(this.target)).getName());
376                    ptarget.setDBSeqLength(((DADNASequence) result.get(this.target)).getDBSeqLength());
377                    ptarget.setSpecies(species);
378                    ptarget.setCoordSystem(targetCS);
379
380                    //loook to see if we have this sequence in cache
381                    ptarget = species.getCachedChromosome((DAChromosome) ptarget);
382
383                } else {
384                    ptarget = new DAAssembledDNASequence((DAOCoreFactory) daoFactory);
385                    ptarget.setId(((DADNASequence) result.get(this.target)).getId());
386                    ptarget.setName(((DADNASequence) result.get(this.target)).getName());
387                    ptarget.setDBSeqLength(((DADNASequence) result.get(this.target)).getDBSeqLength());
388                    ptarget.setCoordSystem(targetCS);
389
390                    //look to see if we have this sequence in cache
391                    ptarget = species.getCachedFragment((DAAssembledDNASequence) ptarget);
392                }
393
394
395            } else {
396                //if the target DNASequence isn't the sequence that the gene is on
397                //throw this result away
398                //ultimately will need to modify this to map from alternate CS/sequences
399                if (!ptarget.getId().equals(pparentSeq.getId())) {
400                    return null;
401                }
402            }
403
404            if (pexon.getDaoFactory() == null) {
405                //pgene.setType(FeatureType.transcript);
406                pexon.setDaoFactory(daoFactory);
407            }
408
409
410//            //check if the transcript object has somehow been returned from a cache and it
411//            //has a mapping to a seq region with the same id as the parentSeq: if
412//            //so, swap it for the parentSeq
413//            for (Mapping m : pgene.getLoadedMappings()) {
414//                if (m.getTarget().getId().equals(pparentSeq.getId())) {
415//                    if (pparentSeq.getDaoFactory() == null) {
416//                        pparentSeq.setDaoFactory(daoFactory);
417//                    }
418//                    if (pparentSeq.getCoordSystem() == null) {
419//                        if (singleSpecies) {
420//                            pparentSeq.setCoordSystem(ssFactory.getDatabase().getCSByID(pcsID));
421//                        } else {
422//                            pparentSeq.setCoordSystem(collFactory.getDatabase().getCSByID(species, pcsID));
423//                        }
424//                    }
425//                    //do the switch
426//                    m.setTarget(pparentSeq);
427//                    m.setTargetCoordinates(pcoords);
428//                    Mapping.addReverseMapping(m);
429//                    return pgene;
430//                }
431//            }
432//
433//
434            Mapping mapping = new Mapping();
435            mapping.setSource(pexon);
436            mapping.setTargetCoordinates(pcoords);
437            if (pparentSeq != null) {
438                mapping.setTarget(pparentSeq);
439            } else {
440                mapping.setTarget(ptarget);
441            }
442
443            if (pexon.addMapping(mapping)) {
444                Mapping.addReverseMapping(mapping);
445            }
446            if (daTranscript != null) {
447                daTranscript.addExon(pexon);
448                pexon.setTranscript(daTranscript);
449            }
450            return pexon;
451
452
453        }
454    }
455}