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.DataSource;
031import uk.ac.roslin.ensembl.config.EnsemblComparaDivision;
032import uk.ac.roslin.ensembl.config.EnsemblCoordSystemType;
033import uk.ac.roslin.ensembl.config.FeatureType;
034import uk.ac.roslin.ensembl.dao.coreaccess.GeneDAO;
035import uk.ac.roslin.ensembl.dao.database.DBCollectionSpecies;
036import uk.ac.roslin.ensembl.dao.factory.DAOCollectionCoreFactory;
037import uk.ac.roslin.ensembl.dao.factory.DAOCoreFactory;
038import uk.ac.roslin.ensembl.dao.factory.DAOSingleSpeciesCoreFactory;
039import uk.ac.roslin.ensembl.datasourceaware.DAXRef;
040import uk.ac.roslin.ensembl.datasourceaware.core.DAAssembledDNASequence;
041import uk.ac.roslin.ensembl.datasourceaware.core.DAChromosome;
042import uk.ac.roslin.ensembl.datasourceaware.core.DADNASequence;
043import uk.ac.roslin.ensembl.datasourceaware.core.DAGene;
044import uk.ac.roslin.ensembl.exception.DAOException;
045import uk.ac.roslin.ensembl.mapper.core.GeneMapper;
046import uk.ac.roslin.ensembl.mapper.handler.DBResultHandler;
047import uk.ac.roslin.ensembl.mapper.handler.HashMapHolder;
048import uk.ac.roslin.ensembl.mapper.query.FeatureQuery;
049import uk.ac.roslin.ensembl.model.Coordinate;
050import uk.ac.roslin.ensembl.model.IdentifiableObject;
051import uk.ac.roslin.ensembl.model.Mapping;
052import uk.ac.roslin.ensembl.model.core.CollectionSpecies;
053import uk.ac.roslin.ensembl.model.core.CoordinateSystem;
054import uk.ac.roslin.ensembl.model.core.CoreObject;
055import uk.ac.roslin.ensembl.model.core.DNASequence;
056
057
058public class DBGeneDAO extends DBCoreObjectDAO implements GeneDAO {
059
060    //if the meta table tells us that genebuild.level is 'toplevel'
061    //we can grab the top ranked CS and use this for all genes
062    //there may be other values than 'toplevel' - i dont know what :)
063    //if the build level isn't annotated we have to get the possible CS levels
064    //from meta_coord and use which ever one is approriate!
065    CoordinateSystem geneBuildCS = null;
066    final static Logger LOGGER = LoggerFactory.getLogger(DBGeneDAO.class);
067
068
069    public DBGeneDAO() {
070        super();
071    }
072
073    public DBGeneDAO(DAOSingleSpeciesCoreFactory factory) throws DAOException {
074        super(factory);
075
076        //we want to initialize the GeneDAO so that it knows which COordinate system
077        //it needs to quwery about genes
078
079        geneBuildCS = this.ssFactory.getDatabase().getBuildCoordSystem(FeatureType.gene.toString());
080        //what if this is null or throws an exception?
081        //if it throws an exception we are probably in some sort of bad factory environment
082        if (geneBuildCS==null) {
083            geneBuildCS=this.ssFactory.getDatabase().getTopLevelCoordSystem();
084        }
085    }
086
087    public DBGeneDAO(DAOCollectionCoreFactory factory) throws DAOException {
088        super(factory);
089        geneBuildCS= this.collFactory.getDatabase().getBuildCoordSystem(species, FeatureType.gene.toString());
090        //what if this is null  or throw an exception?
091        //if it throws an exception we are probably in some sorto f baf dfactory environment
092        if (geneBuildCS==null) {
093            geneBuildCS=this.collFactory.getDatabase().getTopLevelCS(species);
094        }
095    }
096
097    /**
098     * Uses the stableid of an object to fill in missing data that would have been
099     * present if the the gene had intially been made by a call to getGeneByStableID
100     * or getGeneByID.
101     * @param object
102     * @throws DAOException
103     */
104    @Override
105    public void reInitialize(IdentifiableObject object) throws DAOException {
106
107        if (object == null || !(object instanceof DAGene)) {
108            throw new DAOException("Object not a DAGene");
109        }
110
111        DAGene gene = (DAGene) object;
112
113        //the DAO method requires a stableID
114        if (gene.getStableID() == null || gene.getStableID().isEmpty()) {
115            return;
116        }
117
118        DAGene temp = this.getGeneByStableID(gene.getStableID());
119
120        if (temp != null) {
121            gene.setAnalysisID(temp.getAnalysisID());
122            gene.setDisplayXRef(temp.getDisplayXRef());
123            gene.setBiotype(temp.getBiotype());
124            gene.setId(temp.getId());
125            gene.setCanonicalTranscriptID(temp.getCanonicalTranscriptID());
126            gene.setCreationDate(temp.getCreationDate());
127            gene.setModificationDate(temp.getModificationDate());
128            gene.setDescription(temp.getDescription());
129            gene.setCurrent(temp.isCurrent());
130            gene.setDisplayName(temp.getDisplayName());
131            gene.setStatus(temp.getStatus());
132            gene.setVersion(temp.getVersion());
133            gene.setHomoeologueCount(temp.getHomoeologueCount());
134            gene.setOrthologueCount(temp.getOrthologueCount());
135            gene.setParalogueCount(temp.getParalogueCount());
136
137            for (Mapping tempMapping : temp.getLoadedMappings()) {
138                Mapping tempRevMapping = tempMapping.getReverseMapping();
139                if (tempRevMapping != null) {
140                    tempRevMapping.setTarget(gene);
141                }
142                tempMapping.setSource(gene);
143                gene.addMapping(tempMapping);
144
145//            Mapping mapping = new Mapping();
146//            mapping.setSource(gene);
147//            mapping.setTargetCoordinates(tempMapping.getTargetCoordinates());
148////            m.getTarget().clearAllMappings();
149//            mapping.setTarget(tempMapping.getTarget());
150//            if (gene.addMapping(mapping)) {
151//                Mapping.addReverseMapping(mapping);
152//            }
153            }
154        }
155        gene.setInitialized(true);
156    }
157
158    /**
159     * @param id
160     * 
161     * @throws DAOException
162     */
163    @Override
164    public DAGene getGeneByID(Integer id) throws DAOException {
165        
166        if (id==null) {
167            return null;
168        }
169
170        FeatureQuery q = new FeatureQuery();
171        q.setFeatureID(id);
172        if (!singleSpecies) {
173            try {
174                q.setSpeciesID(species.getDBSpeciesID(this.getFactory().getDBVersion()));
175            } catch (Exception e) {
176                throw new DAOException("No species ID context for this query!", e);
177            }
178        }
179        
180        String comparaDB = this.getComparaDB();       
181        if (comparaDB!=null && !comparaDB.isEmpty()) {
182            q.setComparaDB(comparaDB);
183        }
184        return this.getGene(q);
185    }
186
187    @Override
188    public DAGene addGeneByIDOnParent(Integer id, DNASequence parent) throws DAOException {
189        if (parent == null || !(parent instanceof DADNASequence) || ((CoreObject) parent).getId() == null) {
190            throw new DAOException("No valid parent DNA provided");
191        }
192        if (id == null ) {
193            return null;
194        }
195        FeatureQuery q = new FeatureQuery();
196        if (!singleSpecies) {
197            try {
198                q.setSpeciesID(species.getDBSpeciesID(this.getFactory().getDBVersion()));
199            } catch (Exception e) {
200                throw new DAOException("No species ID context for this query!", e);
201            }
202        }
203        q.setFeatureID(id);
204        //this is redundant - if the rowGene doesnt map on this sequence
205        //adding this parameter or not we would still get no genes back
206        //adding this parameter has the effect of
207        //making this a different query to getGeneByID and therefore
208        //will return a differnt object  - not a cached one
209        q.setParentSequenceID(parent.getId());
210        
211        if (Integer.parseInt(this.getDAOFactory().getEnsemblSchemaVersion()) > 75) {
212            String comparaDB = this.getComparaDB();
213            if (comparaDB != null && !comparaDB.isEmpty()) {
214                q.setComparaDB(comparaDB);
215            }
216        }
217        return this.getGeneOnParent(q, (DADNASequence) parent);
218    }
219
220    /**
221     * @param stableID
222     * 
223     * @throws DAOException
224     */
225    @Override
226    public DAGene getGeneByStableID(String stableID) throws DAOException {
227        if (stableID==null || stableID.isEmpty()  ) {
228            return null;
229        }
230
231        FeatureQuery q = new FeatureQuery();
232        if (!singleSpecies) {
233            try {
234                q.setSpeciesID(species.getDBSpeciesID(this.getFactory().getDBVersion()));
235            } catch (Exception e) {
236                throw new DAOException("No species ID context for this query!", e);
237            }
238        }
239        q.setFeatureStableID(stableID.trim());
240        
241        if (Integer.parseInt(this.getDAOFactory().getEnsemblSchemaVersion()) > 75) {
242            String comparaDB = this.getComparaDB();
243            if (comparaDB != null && !comparaDB.isEmpty()) {
244                q.setComparaDB(comparaDB);
245            }
246        }
247        
248        return this.getGene(q);
249    }
250
251    /**
252     * The gene returned has mapping to a sequence region - and the id for 
253     * the coordinate system that this region belongs to
254     * @param stableID
255     * @param parent
256     * 
257     * @throws DAOException
258     */
259    @Override
260    public DAGene addGeneByStableIDOnParent(String stableID, DNASequence parent) throws DAOException {
261        if (parent == null || !(parent instanceof DADNASequence) || ((CoreObject) parent).getId() == null) {
262            throw new DAOException("No valid parent DNA provided");
263        }
264        if (stableID == null || stableID.isEmpty() ) {
265            return null;
266        }
267        FeatureQuery q = new FeatureQuery();
268        if (!singleSpecies) {
269            try {
270                q.setSpeciesID(species.getDBSpeciesID(this.getFactory().getDBVersion()));
271            } catch (Exception e) {
272                throw new DAOException("No species ID context for this query!", e);
273            }
274        }
275        q.setFeatureStableID(stableID.trim());
276        //this is redundant - if the rowGene doesnt map on this sequence
277        //adding this parameter or not we would still get no genes back
278        //adding this parameter has the effect of
279        //making this a different query to getGeneByID and therefore
280        //will return a differnt object  - not a cached one
281        q.setParentSequenceID(parent.getId());
282        if (Integer.parseInt(this.getDAOFactory().getEnsemblSchemaVersion()) > 75) {
283            String comparaDB = this.getComparaDB();
284            if (comparaDB != null && !comparaDB.isEmpty()) {
285                q.setComparaDB(comparaDB);
286            }
287        }
288        return this.getGeneOnParent(q, (DADNASequence) parent);
289    }
290
291    @Override
292    public List<Mapping> getGeneMappingsOnRegion(DNASequence region, Coordinate coords) throws DAOException  {
293
294        List<Mapping> mappings = new ArrayList<Mapping>();
295        FeatureQuery q = new FeatureQuery();
296        if (!singleSpecies) {
297            try {
298                q.setSpeciesID(species.getDBSpeciesID(this.getFactory().getDBVersion()));
299            } catch (Exception e) {
300                throw new DAOException("No species ID context for this query!", e);
301            }
302        }
303        CoordinateSystem cs = null;
304
305        if (region == null || region.getId()==null || region.getCoordSystem()== null ) {
306            throw new DAOException("No valid region or coordinate system specified");
307        } else {
308            cs = region.getCoordSystem();
309            q.setParentSequenceID(region.getId());
310            q.setCoordinateSystemID(cs.getId());
311        }
312
313        if (coords != null) {
314            q.setParentStart(coords.getStart());
315            q.setParentStop(coords.getEnd());
316        }
317
318//        //what happens if we dont have a CS??
319//        //do we want to try with the gene build CS is if there is one
320//        //this is 'always' toplevel i.e. 1
321//        //but the sequence might not be at this level
322//        //so we would have to checke this before doing it
323//        //probably safest to force a lazy load on sequence.getCoordSystem()
324//        //i.e not do this....
325//        else {
326//            cs = this.geneBuildCS;
327//            //q.setCoordinateSystemID(this.geneBuildCS.getId());
328//        }
329
330        //for maximum length
331        //will need different method if constructed from CollectionDB
332
333        Integer max = null;
334        if (this.singleSpecies) {
335                max= this.ssFactory.getDatabase().getMaxLengthForFeature(FeatureType.gene, this.geneBuildCS);
336        } else {
337                max= this.collFactory.getDatabase().getMaxLengthForFeature((DBCollectionSpecies) species, FeatureType.gene, this.geneBuildCS);
338        }
339
340        if (max != null) {
341            q.setMaximumFeatureLength(max);
342        }
343        if (Integer.parseInt(this.getDAOFactory().getEnsemblSchemaVersion()) > 75) {
344            String comparaDB = this.getComparaDB();
345            if (comparaDB != null && !comparaDB.isEmpty()) {
346                q.setComparaDB(comparaDB);
347            }
348        }
349        
350        //out = this.getGenesOnParent(q, (DADNASequence) region);
351
352        mappings = this.getMappingsOnParent(q, (DADNASequence) region);
353
354
355        // this gets the desired genes filtered by stand if specified
356        // probably should be done in the calling class
357
358//        if (coords.getStrand() == null) {
359//            for (Mapping m : mappings) {
360//                out.add((DAGene) m.getSource());
361//            }
362//        } else {
363//            for (Mapping m : mappings) {
364//                if (m.getTargetCoordinates().getStrand().equals(coords.getStrand())) {
365//                    out.add((DAGene) m.getSource());
366//                }
367//            }
368//        }
369
370        return mappings;
371    }
372
373    /**
374     * Returns a list of Ensembl Genes matching the query VegaID string.
375     * Vega currently curates gene, transcript and protein annotations for a human 
376     * and and a few key regions of other vertebrate species. Calling this method
377     * on EnsemblGenomes (invertebrate) species will return an empty list by default. 
378     * @param id a valid vega ID (these begin 'OTT...')
379     * 
380     * @throws DAOException 
381     */
382    @Override
383    public List<DAGene> getGenesForVegaID(String id) throws DAOException {
384
385        //return an empty list rather than null
386        List<DAGene> out = new ArrayList<DAGene>();
387        
388        if (id==null || id.isEmpty() 
389                || !this.getFactory().getRegistry().getDatasourceType().equals(DataSource.ENSEMBLDB)) {
390            return out;
391        }
392        
393        List<HashMapHolder> l = null;
394        GeneMappingRowHandler handler = null;
395
396        List<HashMap> result = new ArrayList<HashMap>();
397
398        SqlSession session = null;
399
400        try {
401            session = this.getFactory().getNewSqlSession();
402            GeneMapper mapper = session.getMapper(GeneMapper.class);
403            l = mapper.getVegaGenes(id);
404        } catch (Exception e) {
405            throw new DAOException("Failed to call getGenesForVegaID", e);
406        } finally {
407            if (session != null) {
408                session.close();
409            }
410        }
411        if (l != null) {
412            for (HashMapHolder h : l) {
413
414                result.add(h.getMap());
415
416            }
417
418            if (result != null && !result.isEmpty()) {
419
420                handler = new GeneMappingRowHandler(result);
421                handler.handleResult();
422                out = handler.getListResult();
423            }
424        }
425        
426        if (out==null) {
427            out = new ArrayList<DAGene>();
428        }
429        
430        /*
431         * NOTE: we dont set the query VegaID on the return gene as this being null 
432         * is the trigger for lazyloading a vega XRef.
433         * NOR can we make a valid Vega XRef at this point as we dont have enough information 
434         * (ie we dont have its database ID). 
435         */
436        
437//        if (!out.isEmpty()) {
438//            ExternalDB edb = this.daoFactory.getDatabase().getExternalDB(ExternalDBType.VegaGene.toString());
439//            //note the full details of this xref won't be filled in - requiring lazy loading
440//            DAXRef xref = new DAXRef();
441//            xref.setDaoFactory(this.getFactory());
442//            xref.setDB(edb);
443//            xref.setPrimaryAccession(id);
444//            xref.setDisplayID(id);
445//            xref.setDBName(edb.getDBName());
446//
447//            for (DAGene g : out) {
448//                g.setVegaGeneID(id);
449//                g.addTypedXRefs(ExternalDBType.VegaGene, new ArrayList<DAXRef>(Arrays.asList(xref)));
450//            }
451//        }
452        return out;
453    }
454    
455    /*
456     * Looks up any Genes with a display name or XRef synonym matching the query (case insensitive).
457     */
458    @Override
459    public List<DAGene> getGenesByExactName(String name) throws DAOException {
460        
461        //return an empty list rather than null
462        List<DAGene> out = new ArrayList<DAGene>();        
463        if (name==null || name.isEmpty()) {
464            return out;
465        }
466        
467        FeatureQuery q = new FeatureQuery();
468        q.setFeatureName(name);
469        if (!singleSpecies) {
470            try {
471                q.setSpeciesID(species.getDBSpeciesID(this.getFactory().getDBVersion()));
472            } catch (Exception e) {
473                throw new DAOException("No species ID context for this query!", e);
474            }
475        }        
476        
477        SqlSession session = null;
478
479        try {
480            session = this.getFactory().getNewSqlSession();
481            GeneMapper mapper = session.getMapper(GeneMapper.class);
482            out = mapper.getGenesByName(q);
483        } catch (Exception e) {
484            throw new DAOException("Failed to call getGenesByName('"+name+"')", e);
485        } finally {
486            if (session != null) {
487                session.close();
488            }
489        }
490        if (out == null) {
491            return new ArrayList<DAGene>();
492        }
493        for (DAGene g: out) {
494            g.setDaoFactory(daoFactory);
495        }
496
497        return out;
498    }
499
500    /*
501     * Looks up any Genes with a display name or XRef synonym beginning
502     * with a match to the query (case insensitive).
503     */    
504    @Override
505    public List<DAGene> getGenesByNameBeginning(String name) throws DAOException {
506        return getGenesByExactName(name+"%");
507    }
508    
509    
510   //*******************************
511
512    private DAGene getGene(FeatureQuery query) throws DAOException {
513        DAGene out = null;
514        GeneMappingRowHandler handler = null;
515        List<HashMap> result;
516
517        SqlSession session = null;
518
519        try {
520            session = this.getFactory().getNewSqlSession();
521            GeneMapper mapper = session.getMapper(GeneMapper.class);
522            result = mapper.getGene(query);
523        } catch (Exception e) {
524            throw new DAOException("Failed to call getGene", e);
525        } finally {
526            if (session != null) {
527                session.close();
528            }
529        }
530
531        if (result != null && !result.isEmpty()) {
532
533            handler = new GeneMappingRowHandler(result);
534            handler.handleResult();
535            out = handler.getObjectResult();
536        }
537
538        return out;
539    }
540
541    private DAGene getGeneOnParent(FeatureQuery query, DADNASequence parent) throws DAOException {
542        DAGene out = null;
543        GeneMappingRowHandler handler = null;
544        List<HashMap> result;
545
546        SqlSession session = null;
547
548        try {
549            session = this.getFactory().getNewSqlSession();
550            GeneMapper mapper = session.getMapper(GeneMapper.class);
551            result = mapper.getGene(query);
552        } catch (Exception e) {
553            throw new DAOException("Failed to call getGeneOnParent", e);
554        } finally {
555            if (session != null) {
556                session.close();
557            }
558        }
559
560        if (result != null && !result.isEmpty()) {
561
562            handler = new GeneMappingRowHandler(result, parent);
563            handler.handleResult();
564            out = handler.getObjectResult();
565        }
566
567        return out;
568    }
569
570    private List<Mapping> getMappingsOnParent(FeatureQuery query, DADNASequence parent) throws DAOException {
571        List<Mapping> out = null;
572
573        MappingRowHandler handler = null;
574        List<HashMap> result;
575
576        SqlSession session = null;
577
578        try {
579            session = this.getFactory().getNewSqlSession();
580            GeneMapper mapper = session.getMapper(GeneMapper.class);
581            result = mapper.getGene(query);
582        } catch (Exception e) {
583            throw new DAOException("Failed to call getMappingsOnParent", e);
584        } finally {
585            if (session != null) {
586                session.close();
587            }
588        }
589
590        if (result != null && !result.isEmpty()) {
591
592            handler = new MappingRowHandler(result, parent);
593            handler.handleResult();
594            out = handler.getListResult();
595        }
596
597        return out;
598    }
599    
600    
601    @Override
602    public String getComparaDB() {
603
604        Integer dbVersion = Integer.parseInt(this.getDAOFactory().getDBVersion());
605        
606        //homologies for bacteria after release 16 are only calulated for the species in 
607        //pan homology set, adn are only stored in the ensembl_compara_pan_homology db, 
608        //not the ensembl_compara_bacteria db. there is no homology data for other species
609        if (species instanceof CollectionSpecies && dbVersion > 16) {
610            if (this.getDAOFactory().getRegistry().isSpeciesInPanHomology(species, this.getDAOFactory().getDBVersion())) {
611                return "ensembl_compara_pan_homology_" + this.getDAOFactory().getDBVersion()+"_"+this.getDAOFactory().getEnsemblSchemaVersion();
612            } else {
613                return null;
614            }
615        }
616
617        return this.getDAOFactory().getComparaFactory(species.getComparaDivision()).getDatabaseName();
618    }
619    
620
621    //*********************************
622
623    // I haven't decided whether it is better to get theobjects
624    //(with the mappings filled in)
625    // or to get the mappings themselves
626
627
628    //as an inner class it has access to the factory etc of the enclosing class
629    public class GeneMappingRowHandler implements DBResultHandler {
630
631        // the magic strings to be used as property keys for HashMap in Ibatis
632        private final String gene = "gene";
633        private final String xref = "xref";
634        private final String target = "target";
635        private final String coords = "coords";
636        private final String coordSystemID = "csID";
637//        private final String xdb = "xdb";
638        
639        private DADNASequence parentSeq = null;
640        private DAGene objectResult = null;
641        private List<DAGene> listResult = new ArrayList<DAGene>();
642        private List<HashMap> rawResults = null;
643
644        public GeneMappingRowHandler(List<HashMap> results) {
645            rawResults = results;
646        }
647
648        public GeneMappingRowHandler(List<HashMap> results, DADNASequence parent) {
649            rawResults = results;
650            parentSeq = parent;
651        }
652
653        @Override
654        public List<DAGene> getListResult() {
655            return listResult;
656        }
657
658        @Override
659        public DAGene getObjectResult() {
660            return objectResult;
661        }
662
663        public void handleResult() throws DAOException {
664
665            if (rawResults == null || rawResults.isEmpty()) {
666                return;
667            }
668
669            for (HashMap map : rawResults) {
670                handleRow(map);
671            }
672
673        }
674
675        private void handleRow(HashMap result) throws DAOException {
676
677            if (result == null || result.isEmpty()) {
678                return;
679            }
680
681            DAGene rowGene = null;
682            if (parentSeq != null) {
683                rowGene = this.parseResult(result, parentSeq);
684            } else {
685                rowGene = this.parseResult(result);
686            }
687
688            if (rowGene != null) {
689                this.objectResult = rowGene;
690                this.listResult.add(rowGene);
691            }
692
693        }
694
695        private DAGene parseResult(HashMap result, DADNASequence parent) throws DAOException {
696
697            if (result == null || parent == null) {
698                return null;
699            }
700
701            DADNASequence pparentSeq = parent;
702
703            DAGene pgene = null;
704            DADNASequence ptarget = null;
705            Coordinate pcoords = null;
706            DAXRef pxref = null;
707            Integer pcsID = null;
708//            ExternalDB db = null;
709
710            pgene = (DAGene) result.get(this.gene);
711            pcoords = (Coordinate) result.get(this.coords);
712            ptarget = (DADNASequence) result.get(this.target);
713            pcsID = (Integer) result.get(this.coordSystemID);
714            pxref = (DAXRef) result.get(this.xref);
715//            db = (ExternalDB) result.get(this.xdb);
716
717            //we only want genes on the parent sequence
718            //this should never fail if we have done the sql correctly
719            if (pgene == null || pcoords == null || ptarget == null || !ptarget.getId().equals(pparentSeq.getId())) {
720                return null;
721            }
722            
723            if (pgene.getDaoFactory() == null) {
724                //pgene.setType(FeatureType.gene);
725                pgene.setDaoFactory(daoFactory);
726            }
727            
728//            if (db != null) {
729//                    int originalHashCode = db.originalHashCode();
730//                    db = ((CoreDatabase) daoFactory.getDatabase()).validateExternalDB(db);  
731//                    int checkedHashCode = db.originalHashCode();
732//                    if (originalHashCode-checkedHashCode !=0) {
733//                        System.out.println("*** FAILED TO REUSE EXTERNALDB");
734//                    } else {
735//                        System.out.println("*** successfully reused externaldb");
736//                    }
737//            }
738
739            if (pgene.getDisplayXRef() == null) {
740                if (pxref != null) {
741                    pxref.setDaoFactory(daoFactory);
742//                    pxref.setDB(db);
743                    pgene.setDisplayXRef(pxref);
744                }
745            }
746
747            //check if the gene object has somehow been returned from a cache and it
748            //has a mapping to a seq region with the same id as the parentSeq: if
749            //so, swap it for the parentSeq
750            for (Mapping m : pgene.getLoadedMappings()) {
751                if (m.getTarget().getId().equals(pparentSeq.getId())) {
752                    if (pparentSeq.getDaoFactory() == null) {
753                        pparentSeq.setDaoFactory(daoFactory);
754                    }
755                    if (pparentSeq.getCoordSystem() == null) {
756                        if (singleSpecies) {
757                            pparentSeq.setCoordSystem(ssFactory.getDatabase().getCSByID(pcsID));
758                        } else {
759                            pparentSeq.setCoordSystem(collFactory.getDatabase().getCSByID(species, pcsID));
760                        }
761                    }
762                    //do the switch
763                    m.setTarget(pparentSeq);
764                    m.setTargetCoordinates(pcoords);
765                    Mapping.addReverseMapping(m);
766                    return pgene;
767                }
768            }
769
770
771            Mapping mapping = new Mapping();
772            mapping.setSource(pgene);
773            mapping.setTargetCoordinates(pcoords);
774
775            // at this point we could look to see if we have this sequence in cache
776
777            if (pparentSeq.getDaoFactory() == null) {
778                pparentSeq.setDaoFactory(daoFactory);
779            }
780
781            if (pparentSeq.getCoordSystem() == null) {
782                 if (singleSpecies) {
783                        pparentSeq.setCoordSystem(ssFactory.getDatabase().getCSByID(pcsID));
784                 } else {
785                        pparentSeq.setCoordSystem(collFactory.getDatabase().getCSByID(species, pcsID));
786                 }
787            }
788
789            mapping.setTarget(pparentSeq);
790            if(pgene.addMapping(mapping)) {
791                Mapping.addReverseMapping(mapping);
792            }
793            return pgene;
794
795
796        }
797
798        private DAGene parseResult(HashMap result) throws DAOException {
799
800            if (result == null || result.isEmpty()) {
801                return null;
802            }
803
804            DAGene pgene = null;
805            DADNASequence ptarget = null;
806            Coordinate pcoords = null;
807            DAXRef pxref = null;
808            Integer pcsID = null;
809//            ExternalDB db = null;
810            
811            pgene = (DAGene) result.get(this.gene);
812
813            if (pgene == null) {
814                return null;
815            }
816            
817            if (pgene.getDaoFactory() == null) {
818               // pgene.setType(FeatureType.gene);
819                pgene.setDaoFactory(daoFactory);
820            }
821            
822//            db = (ExternalDB) result.get(this.xdb);
823//            if (db != null) {
824//                    int originalHashCode = db.originalHashCode();
825//                    db = ((CoreDatabase) daoFactory.getDatabase()).validateExternalDB(db);  
826//                    int checkedHashCode = db.originalHashCode();
827//                    if (originalHashCode-checkedHashCode !=0) {
828//                        System.out.println("*** FAILED TO REUSE EXTERNALDB");
829//                    } else {
830//                        System.out.println("*** successfully reused externaldb");
831//                    }
832//            }
833            
834
835            if (pgene.getDisplayXRef() == null) {
836                pxref = (DAXRef) result.get(this.xref);
837                if (pxref != null) {
838                    pxref.setDaoFactory(daoFactory);
839//                    pxref.setDB(db);
840                    pgene.setDisplayXRef(pxref);
841                }
842            }
843
844            pcoords = (Coordinate) result.get(this.coords);
845
846            //later we will check to see if we already have this in cache
847            //(at moment just for chromosomes)
848            ptarget = (DADNASequence) result.get(this.target);
849
850            pcsID = (Integer) result.get(this.coordSystemID);
851
852            if (pcoords == null || ptarget == null) {
853                return pgene;
854            }
855
856            Mapping mapping = new Mapping();
857            mapping.setSource(pgene);
858            mapping.setTargetCoordinates(pcoords);
859
860            //we want to convert the DADNASequence to the correct type if it is an assembly!
861
862            CoordinateSystem targetCS  = null;
863
864            if (singleSpecies) {
865                    targetCS = ssFactory.getDatabase().getCSByID(pcsID);
866               } else {
867                    targetCS =collFactory.getDatabase().getCSByID(species, pcsID);
868               }
869
870            if (targetCS.isSequenceLevel()) {
871                ptarget.setCoordSystem(targetCS);
872            } else if (EnsemblCoordSystemType.chromosome.equals(targetCS.getType())) {
873                ptarget = new DAChromosome((DAOCoreFactory) daoFactory);
874                ptarget.setId(((DADNASequence) result.get(this.target)).getId());
875                ptarget.setSpecies(species);
876                ptarget.setCoordSystem(targetCS);
877                
878                //look to see if we have this sequence in cache
879                ptarget = species.getCachedChromosome((DAChromosome) ptarget);
880
881            } else {
882                ptarget = new DAAssembledDNASequence((DAOCoreFactory) daoFactory);
883
884                ptarget.setId(((DADNASequence) result.get(this.target)).getId());
885                ptarget.setCoordSystem(targetCS);
886
887                //look to see if we have this sequence in cache
888                ptarget = species.getCachedFragment((DAAssembledDNASequence) ptarget);
889            }
890
891            
892
893            mapping.setTarget(ptarget);
894            if (pgene.addMapping(mapping)) {
895                Mapping.addReverseMapping(mapping);
896            }
897            return pgene;
898
899        }
900    }
901
902    //as an inner class it has access to the factory etc of the enclosing class
903    public class MappingRowHandler implements DBResultHandler {
904
905        // the magic strings to be used as property keys for HashMap in Ibatis
906        private final String gene = "gene";
907        private final String xref = "xref";
908        private final String target = "target";
909        private final String coords = "coords";
910        private final String coordSystemID = "csID";
911//        private final String xdb = "xdb";
912        
913        private DADNASequence parentSeq = null;
914        private Mapping objectResult = null;
915        private List<Mapping> listResult = new ArrayList<Mapping>();
916        private List<HashMap> rawResults = null;
917
918        public MappingRowHandler(List<HashMap> results) {
919            rawResults = results;
920        }
921
922        public MappingRowHandler(List<HashMap> results, DADNASequence parent) {
923            rawResults = results;
924            parentSeq = parent;
925        }
926
927        @Override
928        public List<Mapping> getListResult() {
929            return listResult;
930        }
931
932        @Override
933        public Mapping getObjectResult() {
934            return objectResult;
935        }
936
937
938        public void handleResult() throws DAOException {
939
940            if (rawResults == null || rawResults.isEmpty()) {
941                return;
942            }
943
944            for (HashMap map : rawResults) {
945                handleRow(map);
946            }
947
948        }
949
950        private void handleRow(HashMap result) throws DAOException {
951
952            if (result == null || result.isEmpty()) {
953                return;
954            }
955
956            Mapping rowMapping = null;
957            if (parentSeq != null) {
958                rowMapping = this.parseMappingResult(result, parentSeq);
959            } else {
960                rowMapping = this.parseMappingResult(result);
961            }
962
963            if (rowMapping != null) {
964                this.objectResult = rowMapping;
965                this.listResult.add(rowMapping);
966            }
967
968        }
969
970
971
972        private Mapping parseMappingResult(HashMap result, DADNASequence parent) throws DAOException {
973
974            if (result == null || parent == null) {
975                return null;
976            }
977
978            DADNASequence pparentSeq = parent;
979
980            DAGene pgene = null;
981            DADNASequence ptarget = null;
982            Coordinate pcoords = null;
983            DAXRef pxref = null;
984            Integer pcsID = null;
985//            ExternalDB db = null;
986            
987            pgene = (DAGene) result.get(this.gene);
988            pcoords = (Coordinate) result.get(this.coords);
989            ptarget = (DADNASequence) result.get(this.target);
990            pcsID = (Integer) result.get(this.coordSystemID);
991            pxref = (DAXRef) result.get(this.xref);
992//            db = (ExternalDB) result.get(this.xdb); 
993
994            //we only want genes on the parent sequence
995            //this should never fail if we have done the sql correctly
996            if (pgene == null || pcoords == null || ptarget == null || !ptarget.getId().equals(pparentSeq.getId())) {
997                return null;
998            }
999                       
1000            if (pgene.getDaoFactory() == null) {
1001                //pgene.setType(FeatureType.gene);
1002                pgene.setDaoFactory(daoFactory);
1003            }
1004            
1005//            if (db != null) {
1006//                    int originalHashCode = db.originalHashCode();
1007//                    db = ((CoreDatabase) daoFactory.getDatabase()).validateExternalDB(db);  
1008//                    int checkedHashCode = db.originalHashCode();
1009//                    if (originalHashCode-checkedHashCode !=0) {
1010//                        System.out.println("*** FAILED TO REUSE EXTERNALDB");
1011//                    } else {
1012//                        System.out.println("*** successfully reused externaldb");
1013//                    }
1014//            }
1015                       
1016
1017            if (pgene.getDisplayXRef() == null) {
1018                pxref = (DAXRef) result.get(this.xref);
1019                if (pxref != null) {
1020                    pxref.setDaoFactory(daoFactory);
1021//                    pxref.setDB(db);
1022                    pgene.setDisplayXRef(pxref);
1023                }
1024            }
1025
1026            //check if the gene object has somehow been returned from a cache and it
1027            //has a mapping to a seq region with the same id as the parentSeq: if
1028            //so, swap it for the parentSeq
1029            for (Mapping m : pgene.getLoadedMappings()) {
1030                if (m.getTarget().getId().equals(pparentSeq.getId())) {
1031                    if (pparentSeq.getDaoFactory() == null) {
1032                        pparentSeq.setDaoFactory(daoFactory);
1033                    }
1034                    if (pparentSeq.getCoordSystem() == null) {
1035                        if (singleSpecies) {
1036                            pparentSeq.setCoordSystem(ssFactory.getDatabase().getCSByID(pcsID));
1037                        } else {
1038                            pparentSeq.setCoordSystem(collFactory.getDatabase().getCSByID(species, pcsID));
1039                        }
1040                    }
1041
1042                    //do the switch
1043                    m.setTarget(pparentSeq);
1044                    m.setTargetCoordinates(pcoords);
1045                    Mapping.addReverseMapping(m);
1046                    return m;
1047                }
1048            }
1049
1050
1051            Mapping mapping = new Mapping();
1052            mapping.setSource(pgene);
1053            mapping.setTargetCoordinates(pcoords);
1054
1055            // at this point we could look to see if we have this sequence in cache
1056
1057            if (pparentSeq.getDaoFactory() == null) {
1058                pparentSeq.setDaoFactory(daoFactory);
1059            }
1060
1061            if (pparentSeq.getCoordSystem() == null) {
1062            if (singleSpecies) {
1063                    pparentSeq.setCoordSystem(ssFactory.getDatabase().getCSByID(pcsID));
1064               } else {
1065                    pparentSeq.setCoordSystem(collFactory.getDatabase().getCSByID(species, pcsID));
1066               }
1067            }
1068
1069            mapping.setTarget(pparentSeq);
1070            if (pgene.addMapping(mapping)) {
1071                Mapping.addReverseMapping(mapping);
1072            }
1073            return mapping;
1074
1075
1076        }
1077
1078        private Mapping parseMappingResult(HashMap result) throws DAOException {
1079
1080            if (result == null || result.isEmpty()) {
1081                return null;
1082            }
1083
1084            DAGene pgene = null;
1085            DADNASequence ptarget = null;
1086            Coordinate pcoords = null;
1087            DAXRef pxref = null;
1088            Integer pcsID = null;
1089//            ExternalDB db = null;
1090
1091            pgene = (DAGene) result.get(this.gene);
1092//            db = (ExternalDB) result.get(this.xdb);
1093
1094            if (pgene == null) {
1095                return null;
1096            }
1097       
1098            if (pgene.getDaoFactory() == null) {
1099                //pgene.setType(FeatureType.gene);
1100                pgene.setDaoFactory(daoFactory);
1101            }
1102
1103//            if (db != null) {
1104//                    int originalHashCode = db.originalHashCode();
1105//                    db = ((CoreDatabase) daoFactory.getDatabase()).validateExternalDB(db);  
1106//                    int checkedHashCode = db.originalHashCode();
1107//                    if (originalHashCode-checkedHashCode !=0) {
1108//                        System.out.println("*** FAILED TO REUSE EXTERNALDB");
1109//                    } else {
1110//                        System.out.println("*** successfully reused externaldb");
1111//                    }
1112//            }
1113            
1114            if (pgene.getDisplayXRef() == null) {
1115                pxref = (DAXRef) result.get(this.xref);
1116                
1117                if (pxref != null) {
1118//                    pxref.setDB(db);
1119                    pxref.setDaoFactory(daoFactory);
1120                    pgene.setDisplayXRef(pxref);
1121                }
1122            }
1123
1124            pcoords = (Coordinate) result.get(this.coords);
1125            ptarget = (DADNASequence) result.get(this.target);
1126            pcsID = (Integer) result.get(this.coordSystemID);
1127
1128//            if (pcoords == null || ptarget == null) {
1129//                return pgene;
1130//            }
1131
1132            //check to see we are not adding a duplicate mapping
1133            //to a rowGene retreived from cache, if we already have a mapping for
1134            //this id - don't add it again
1135
1136//            for (Mapping m : pgene.getLoadedMappings()) {
1137//                if (m.getTarget().getId().equals(ptarget.getId())) {
1138//                    Mapping.addReverseMapping(m);
1139//                    return m;
1140//                }
1141//            }
1142
1143            Mapping mapping = new Mapping();
1144            mapping.setSource(pgene);
1145            mapping.setTargetCoordinates(pcoords);
1146
1147            // at this point we could look to see if we have this sequence in cache
1148
1149
1150            //we want to convert the DADNASequence to the correct type if it is an assembly!
1151
1152            CoordinateSystem targetCS  = null;
1153
1154            if (singleSpecies) {
1155                    targetCS = ssFactory.getDatabase().getCSByID(pcsID);
1156               } else {
1157                    targetCS =collFactory.getDatabase().getCSByID(species, pcsID);
1158               }
1159
1160            if (targetCS.isSequenceLevel()) {
1161                ptarget.setCoordSystem(targetCS);
1162            } else if (targetCS.getType().equals(EnsemblCoordSystemType.chromosome)) {
1163                ptarget = new DAChromosome();
1164                ptarget.setId(((DADNASequence) result.get(this.target)).getId());
1165                ptarget.setCoordSystem(targetCS);
1166            } else {
1167                ptarget = new DAAssembledDNASequence();
1168                ptarget.setId(((DADNASequence) result.get(this.target)).getId());
1169                ptarget.setCoordSystem(targetCS);
1170            }
1171
1172            ptarget.setDaoFactory(daoFactory);
1173
1174            mapping.setTarget(ptarget);
1175            if (pgene.addMapping(mapping)) {
1176                Mapping.addReverseMapping(mapping);
1177            }
1178            return mapping;
1179
1180        }
1181
1182
1183    }
1184
1185
1186}