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;
023
024import java.util.*;
025import java.util.Map.Entry;
026import org.apache.ibatis.session.SqlSession;
027import uk.ac.roslin.ensembl.config.EnsemblCoordSystemType;
028import uk.ac.roslin.ensembl.config.EnsemblDBType;
029import uk.ac.roslin.ensembl.config.FeatureType;
030import uk.ac.roslin.ensembl.dao.database.factory.DBDAOSingleSpeciesCoreFactory;
031import uk.ac.roslin.ensembl.dao.database.factory.DBDAOSingleSpeciesFactory;
032import uk.ac.roslin.ensembl.datasourceaware.DAAnalysis;
033import uk.ac.roslin.ensembl.datasourceaware.core.DAAssembledDNASequence;
034import uk.ac.roslin.ensembl.datasourceaware.core.DAChromosome;
035import uk.ac.roslin.ensembl.datasourceaware.core.DACoordinateSystem;
036import uk.ac.roslin.ensembl.exception.ConfigurationException;
037import uk.ac.roslin.ensembl.exception.DAOException;
038import uk.ac.roslin.ensembl.mapper.XRefMapper;
039import uk.ac.roslin.ensembl.model.ExternalDB;
040import uk.ac.roslin.ensembl.model.ObjectType;
041import uk.ac.roslin.ensembl.model.core.CoordinateSystem;
042import uk.ac.roslin.ensembl.model.database.Registry;
043import uk.ac.roslin.ensembl.model.database.SingleSpeciesCoreDatabase;
044
045/**
046 *
047 * @author paterson
048 */
049public class DBSingleSpeciesCoreDatabase extends DBSingleSpeciesDatabase implements SingleSpeciesCoreDatabase {
050
051    private List<DACoordinateSystem> coordSystems = new ArrayList<DACoordinateSystem>();
052    private TreeMap<Integer, DACoordinateSystem> rankedCoordSystemsHash = new TreeMap<Integer, DACoordinateSystem>();
053    private TreeMap<Integer, DACoordinateSystem> idCoordSystemsHash = new TreeMap<Integer, DACoordinateSystem>();
054    private Integer chromosomeLevelID = null;
055    private Integer sequenceLevelID = null;
056    private Integer topLevelID = null;
057    private DACoordinateSystem chromosomeLevelCS = null;
058    private DACoordinateSystem sequenceLevelCS = null;
059    private DACoordinateSystem topLevelCS = null;
060    private HashMap<FeatureType, String> defaultBuildLevels = new HashMap<FeatureType, String>();
061    private HashMap<FeatureType, HashMap<DACoordinateSystem, Integer>> featureCSHash = null;
062    private HashMap<DACoordinateSystem, List<FeatureType>> CSFeatureHash = null;
063    private HashMap<Integer,DAAnalysis> analyses = null;
064    /**
065     * A Map of ExternalDB objects on their id (previously used the db_name field in the database
066     * table 'external_db' - but this was not unique due to versioning)
067     */
068    HashMap<Integer, ExternalDB> externalDBsByID = new HashMap<Integer, ExternalDB>();
069    //unfortunately names may not be unique - e.g. multiple versions of PFAM
070    HashMap<String, Set<ExternalDB>> externalDBsByName = new HashMap<String, Set<ExternalDB>>();
071    boolean externalDBsInitialized = false;   
072    
073    protected String assemblyName = null;
074    protected String assemblyAccession = null;
075    protected String comparaName = null;
076        
077    public DBSingleSpeciesCoreDatabase(String db_name, EnsemblDBType type, Registry registry) throws ConfigurationException {
078        super(db_name, type, registry);
079    }
080
081    @Override
082    public DBDAOSingleSpeciesCoreFactory getCoreFactory() {
083        if (this.factory == null) {
084            try {
085                this.factory = DBDAOSingleSpeciesFactory.makeFactory(this);
086            } catch (Exception ex) {
087            }
088        }
089
090        if (this.factory != null) {
091            return (DBDAOSingleSpeciesCoreFactory) this.factory;
092        } else {
093            return null;
094        }
095    }
096    
097    private List<DACoordinateSystem> fetchCS() throws DAOException {
098        List<DACoordinateSystem> coords = new ArrayList<DACoordinateSystem>();
099        //can throw DAOException
100        if ( this.getCoreFactory()!=null) {
101            coords = this.getCoreFactory().getCoordinateSystemDAO().
102                getCoordinateSystems();
103        } else {
104           return coords;
105        }        
106        return coords;
107    }
108    
109    private void setCoordinateSystems(List<DACoordinateSystem> coords) throws DAOException {
110
111        this.coordSystems.clear();
112        this.coordSystems.addAll(coords);
113
114        //set  the ids for the top, chromosome and sequence levels
115
116        for (DACoordinateSystem cs : coordSystems) {
117
118            this.rankedCoordSystemsHash.put(cs.getRank(), cs);
119            this.idCoordSystemsHash.put(cs.getId(), cs);
120
121        }
122        
123        for (Entry<Integer, DACoordinateSystem> e : this.rankedCoordSystemsHash.entrySet() ) {
124            if (e.getValue().isDefaultVersion()) {
125                    e.getValue().setTopLevel(true);
126                    this.topLevelCS = e.getValue();
127                    this.topLevelID = e.getValue().getId();
128                    break;
129                }
130        }
131        
132        for (Entry<Integer, DACoordinateSystem> e : this.rankedCoordSystemsHash.entrySet() ) {
133            if (e.getValue().isDefaultVersion() 
134                    && e.getValue().getType() == EnsemblCoordSystemType.chromosome) {             
135                    this.chromosomeLevelCS = e.getValue();
136                    this.chromosomeLevelID = e.getValue().getId();
137                    break;
138                }
139        }
140        
141        for (Entry<Integer, DACoordinateSystem> e : this.rankedCoordSystemsHash.entrySet() ) {
142            if (e.getValue().isDefaultVersion() 
143                    && e.getValue().isSequenceLevel()) {
144                    this.sequenceLevelCS = e.getValue();
145                    this.sequenceLevelID = e.getValue().getId();
146                    break;
147                }
148        }
149
150    }
151
152    private void lazyLoadCoordinateSystems() throws DAOException {
153
154        this.coordSystems.clear();
155
156        List<DACoordinateSystem> coords = this.fetchCS();
157
158        if (!coords.isEmpty()) {
159               this.setCoordinateSystems(coords);
160        }
161
162        //set nil values to prevent repeating lazy load
163
164        if (this.chromosomeLevelID == null) {
165            this.chromosomeLevelID = 0;
166        }
167        if (this.topLevelID == null) {
168            this.topLevelID = 0;
169        }
170        if (this.sequenceLevelID == null) {
171            this.sequenceLevelID = 0;
172        }
173       
174    }
175
176    @Override
177    public DACoordinateSystem getCSByID(Integer id) throws DAOException {
178        DACoordinateSystem out = null;
179        if (topLevelID == null && this.coordSystems.isEmpty()) {
180
181                //lazyload can throw DAOException
182                lazyLoadCoordinateSystems();
183
184        }
185
186        out = this.idCoordSystemsHash.get(id);
187
188        return out;
189    }
190        
191    public List<DACoordinateSystem> getCoordinateSystems() throws DAOException {
192        
193        if (topLevelID == null && this.coordSystems.isEmpty()) {
194                //lazyload can throw DAOException
195                lazyLoadCoordinateSystems();
196        }
197        return this.coordSystems;
198    }   
199
200    @Override
201    public DACoordinateSystem getTopLevelCoordSystem() throws DAOException {
202        if (topLevelID == null && this.coordSystems.isEmpty()) {
203            //lazyload can throw DAOException
204            lazyLoadCoordinateSystems();
205        }
206        return topLevelCS;
207    }
208
209    @Override
210    public DACoordinateSystem getChromosomeLevelCoordSystem() throws DAOException {
211        if (chromosomeLevelID == null && this.coordSystems.isEmpty()) {
212            //lazyload can throw DAOException
213            lazyLoadCoordinateSystems();
214        }
215        return chromosomeLevelCS;
216    }
217
218    @Override
219    public DACoordinateSystem getSequenceLevelCoordSystem() throws DAOException {
220        if (sequenceLevelID == null && this.coordSystems.isEmpty()) {
221            //lazyload can throw DAOException
222            lazyLoadCoordinateSystems();
223        }
224        return sequenceLevelCS;
225    }
226
227    @Override
228    public DAChromosome getChromosomeByName(String name) throws DAOException {
229        if (this.getCoreFactory()!=null){
230            return this.getCoreFactory().getChromosomeDAO().getChromosomeByName(name);
231        } else {
232            return null;
233        }
234
235    }
236    
237    /**
238     * a Fragment is TopLevelAssembledDNASequence ( where there are no chromosomes )
239     * @param name
240     * 
241     * @throws DAOException 
242     */
243    @Override
244    public DAAssembledDNASequence getFragmentByName(String name) throws DAOException {
245        if (this.getCoreFactory()!=null){
246            return this.getCoreFactory().getChromosomeDAO().getFragmentByName(name);
247        } else {
248            return null;
249        }
250
251    }
252
253    /**
254     * Note that Chromosomes are not cached by the Database object - but by the Species Object
255     * 
256     * @throws DAOException 
257     */
258    @Override
259    public List<DAChromosome> getChromosomes() throws DAOException {
260        if (this.getCoreFactory()!=null){
261            return this.getCoreFactory().getChromosomeDAO().getChromosomes();
262         } else {
263            return new ArrayList<DAChromosome>();
264        }
265    }
266        
267    /**
268     * Fragments are TopLevelAssembledDNASequences
269     * 
270     * @throws DAOException 
271     */
272    @Override
273    public List<DAAssembledDNASequence> getFragments() throws DAOException {
274        if (this.getCoreFactory()!=null){
275            return this.getCoreFactory().getChromosomeDAO().getFragments();
276         } else {
277            return new ArrayList<DAAssembledDNASequence>();
278        }
279    }
280
281    private void setFeatureCSHash() throws DAOException {
282
283        if (sequenceLevelID == null && this.coordSystems.isEmpty()) {
284            //lazyload can throw DAOException
285            lazyLoadCoordinateSystems();
286        }
287
288        featureCSHash = new HashMap<FeatureType, HashMap<DACoordinateSystem, Integer>>();
289        CSFeatureHash = new HashMap<DACoordinateSystem, List<FeatureType>>();
290
291        for (DACoordinateSystem c : this.coordSystems) {
292            CSFeatureHash.put(c, new ArrayList<FeatureType>());
293
294        }
295
296
297        if (this.getCoreFactory() != null) {
298
299            try {
300
301                this.getCoreFactory().getCoordinateSystemDAO().setFeatureCS();
302
303            } catch (DAOException e) {
304                featureCSHash = null;
305                CSFeatureHash = null;
306                throw e;
307            }
308        }
309
310    }
311
312    @Override
313    public Set<DACoordinateSystem> getCSForFeature(ObjectType featureType) throws DAOException {
314
315            if (this.featureCSHash == null) {
316                this.setFeatureCSHash();
317            }
318
319
320        if (this.featureCSHash != null
321                && this.featureCSHash.containsKey(featureType)) {
322            return this.featureCSHash.get(featureType).keySet();
323        } else {
324
325            return null;
326        }
327    }
328
329    @Override
330    public List<FeatureType> getFeaturesForCS(CoordinateSystem coordSys) throws DAOException{
331
332
333            if (this.featureCSHash == null) {
334                this.setFeatureCSHash();
335            }
336
337
338        if (this.CSFeatureHash != null
339                && this.CSFeatureHash.containsKey(coordSys)) {
340            return this.CSFeatureHash.get(coordSys);
341        } else {
342            return null;
343        }
344    }
345
346    @Override
347    public Integer getMaxLengthForFeature(ObjectType featureType, CoordinateSystem cs) throws DAOException {
348
349        if ( featureType == null || cs == null ) {
350            return null;
351        }
352
353            if (this.featureCSHash == null) {
354                this.setFeatureCSHash();
355            }
356
357
358
359        if (this.featureCSHash != null
360                && this.featureCSHash.containsKey(featureType)
361                && this.featureCSHash.get(featureType).containsKey(cs)) {
362            return this.featureCSHash.get(featureType).get(cs);
363        } else {
364            return null;
365        }
366
367    }
368
369    @Override
370    public void addFeatureCS(String featureType, Integer csID, Integer maxLength) {
371
372        FeatureType type = null;
373        DACoordinateSystem cs = null;
374
375        type = FeatureType.getFeatureType(featureType);
376        cs = this.idCoordSystemsHash.get(csID);
377
378        if (type == null || cs == null) {
379            return;
380        } else {
381            if (!this.featureCSHash.containsKey(type)) {
382                this.featureCSHash.put(type, new HashMap<DACoordinateSystem, Integer>());
383            }
384            this.featureCSHash.get(type).put(cs, maxLength);
385
386            this.CSFeatureHash.get(cs).add(type);
387
388        }
389
390
391    }
392
393    @Override
394    public void setBuildLevel(String featureKey, String level) {
395        String temp = featureKey.replace("build.level", "");
396
397        if (FeatureType.getFeatureType(temp) != null) {
398            this.defaultBuildLevels.put(FeatureType.getFeatureType(temp), level);
399        }
400    }
401
402    @Override
403    public String getBuildLevel(String featureType) throws DAOException{
404        if (FeatureType.getFeatureType(featureType) != null) {
405            if (defaultBuildLevels.isEmpty()) {
406                this.getRegistry().setSpeciesMetadata(this);
407            }
408            return this.defaultBuildLevels.get(FeatureType.getFeatureType(featureType));
409        } else {
410            return null;
411        }
412    }
413
414    @Override
415    public DACoordinateSystem getBuildCoordSystem(String featureType) throws DAOException {
416
417            if (FeatureType.getFeatureType(featureType) != null) {
418                if (defaultBuildLevels.isEmpty()) {
419                    this.getRegistry().setSpeciesMetadata(this);
420                }
421                String level = this.defaultBuildLevels.get(FeatureType.getFeatureType(featureType));
422                if (level != null && level.equalsIgnoreCase("toplevel")) {
423                    return this.getTopLevelCoordSystem();
424                } else {
425                    return null;
426                }
427
428//                //are there other values possible?
429//                else if (level.equalsIgnoreCase("chromosomelevel")) {
430//                    return this.getChromosomeLevelCoordSystem();
431//                } else if (level.equalsIgnoreCase("sequencelevel")) {
432//                    return this.getSequenceLevelCoordSystem();
433//                }
434            }
435
436        return null;
437
438    }
439
440    //dont really want this public - just for testing
441    public HashMap<FeatureType, String> getBuildLevels() {
442        if (defaultBuildLevels.isEmpty()) {
443            try {
444                
445                //is this correct - dont i want to do 
446                //this.getRegistry().setCoreDBBuildLevels(this);
447                lazyLoadCoordinateSystems();
448            } catch (DAOException ex) {
449                
450            }
451        }
452        return defaultBuildLevels;
453    }
454
455    /**
456     * Analyses are cached at the Database level
457     * 
458     * @throws DAOException 
459     */
460    @Override
461    public HashMap<Integer, DAAnalysis> getAnalyses() throws DAOException {
462        if (analyses!= null) {
463            return analyses;
464        }
465        if (this.getCoreFactory()!=null){
466            analyses =  this.getCoreFactory().getAnalysisDAO().getAnalyses();
467         } else {
468            analyses = new HashMap<Integer, DAAnalysis>();
469        }
470        return analyses;
471    }
472    
473    /**
474     * Validates a givenExternalDB object, to return a pre-existing version id present. 
475     * @param db
476     *  
477     */
478    @Override
479    public ExternalDB validateExternalDB(ExternalDB db) {
480        
481        if (db==null) {
482            return null;
483        }
484        
485        this.initializeExternalDBs();
486        
487        if (this.externalDBsByID.containsKey(db.getId())) {
488            return this.externalDBsByID.get(db.getId());
489        } else {
490            this.externalDBsByID.put(db.getId(), db);
491            return db;
492        }
493    }
494    
495    @Override
496    public void initializeExternalDBs()  {
497        if (this.externalDBsInitialized) {
498            return;
499        }
500        
501        List<ExternalDB> result = null;
502        SqlSession session = null;
503
504        try {
505            session = this.getNewSqlSession();
506            XRefMapper mapper = session.getMapper(XRefMapper.class);
507            result= mapper.getExternalDBs();
508        } catch (Exception e) {
509            LOGGER.debug("Failed to call getExternalDBs on DBDatabase", e);
510            return;
511            //throw new DAOException("Failed to call getExternalDBs on DBDatabase", e);
512        } finally {
513            if (session != null) {
514                session.close();
515            }
516        }
517
518        if (result!=null && !result.isEmpty()) { 
519            for (ExternalDB d : result) {
520                this.externalDBsByID.put(d.getId(), d);                
521                
522                if (!this.externalDBsByName.containsKey(d.getDBName())) {
523                    this.externalDBsByName.put(d.getDBName(), new HashSet<ExternalDB>());
524                }
525                this.externalDBsByName.get(d.getDBName()).add(d);                
526            }
527        }
528        
529        this.externalDBsInitialized = true;
530    }
531    
532    @Override
533    public ExternalDB getExternalDB(Integer id) {
534        this.initializeExternalDBs();
535        return this.externalDBsByID.get(id);
536    }
537
538    @Override
539    public String getAssemblyName() {
540        if (assemblyName == null) {
541            try {
542                this.lazyLoadMetadata();
543            } catch (DAOException e) {
544                //probably should throw this
545            }
546        }
547        return assemblyName;
548    }   
549    
550    @Override
551    public String getComparaName() {
552        if (comparaName == null) {
553            try {
554                this.lazyLoadMetadata();
555            } catch (DAOException e) {
556                //probably should throw this
557            }
558        }
559        return comparaName;
560    }
561    
562    @Override
563    public String getAssemblyAccession() {        
564        if (assemblyAccession == null) {
565            try {
566                this.lazyLoadMetadata();
567            } catch (DAOException e) {
568                //probably should throw this
569            }
570        }
571        return assemblyAccession;
572    }
573
574    public void lazyLoadMetadata() throws DAOException  {
575        this.getRegistry().setSpeciesMetadata(this);
576        if (assemblyName==null) {assemblyName ="";}
577        if (assemblyAccession==null) {assemblyAccession ="";}
578        if (comparaName==null) {comparaName ="";}
579    }
580
581    @Override
582    public void setAssemblyName(String value) {
583        assemblyName = value;
584    }
585    
586    @Override
587    public void setAssemblyAccession(String value) {
588        assemblyAccession = value;
589    }
590    
591    @Override
592    public void setComparaName(String value) {
593        comparaName = value;
594    }
595    
596    
597}