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