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.List;
026import java.util.HashMap;
027import org.apache.ibatis.session.SqlSession;
028import uk.ac.roslin.ensembl.biojava3.EnsemblDNASequenceReader;
029import uk.ac.roslin.ensembl.dao.coreaccess.DNASequenceDAO;
030import uk.ac.roslin.ensembl.datasourceaware.core.DAAssembledDNASequence;
031import uk.ac.roslin.ensembl.datasourceaware.core.DAChromosome;
032import uk.ac.roslin.ensembl.datasourceaware.core.DADNASequence;
033import uk.ac.roslin.ensembl.mapper.handler.DBResultHandler;
034import uk.ac.roslin.ensembl.mapper.core.SequenceMapper;
035import uk.ac.roslin.ensembl.exception.DAOException;
036import uk.ac.roslin.ensembl.model.core.DNASequence;
037import uk.ac.roslin.ensembl.config.EnsemblCoordSystemType;
038import uk.ac.roslin.ensembl.dao.factory.DAOCollectionCoreFactory;
039import uk.ac.roslin.ensembl.dao.factory.DAOSingleSpeciesCoreFactory;
040import uk.ac.roslin.ensembl.model.core.CoordinateSystem;
041
042/**
043 *
044 * @author paterson
045 */
046public class DBDNASequenceDAO extends DBCoreObjectDAO implements DNASequenceDAO{
047    
048    public DBDNASequenceDAO()  {
049        super();
050    }
051
052    public DBDNASequenceDAO(DAOSingleSpeciesCoreFactory factory)  {
053        super(factory);
054    }
055
056    public DBDNASequenceDAO(DAOCollectionCoreFactory factory) {
057        super(factory);
058    }
059
060    @Override
061    public String getSequenceByStartStop(EnsemblDNASequenceReader reader, Integer start, Integer stop) throws DAOException {
062
063
064        if (reader==null || reader.getSeqRegionID()==null) {
065            throw new DAOException("Invalid parameter handed to getSequenceByStartStop(EnsemblDNASequenceReader reader, Integer start, Integer stop)");
066        }
067
068        String out = null;
069
070        Integer id = reader.getSeqRegionID();
071        Integer begin = start;
072        Integer end = stop;
073
074        HashMap parameters = new HashMap();
075        parameters.put("id", id);
076
077        SqlSession session = null;
078
079        try {
080            session = this.getFactory().getNewSqlSession();
081            SequenceMapper mapper = session.getMapper(SequenceMapper.class);
082
083            if (stop ==null && start ==null) {
084                out = mapper.getSequence(parameters);
085            } else if (stop==null && start != null) {
086                parameters.put("start", begin);
087                out = mapper.getSequenceStart(parameters);
088            } else {
089                parameters.put("start", begin);
090                parameters.put("stop", end);
091                out = mapper.getSequenceStartStop(parameters);
092            }
093
094
095
096        } catch (Exception e) {
097            throw new DAOException("Failed to call getSequenceByStartStop", e);
098        } finally {
099            if (session != null) {
100                session.close();
101            }
102        }
103
104        return out;
105    }
106
107   
108    @Override
109    public String getFullSequence(EnsemblDNASequenceReader reader) throws DAOException {
110        return this.getSequenceByStartStop(reader, null, null);
111    }
112
113    @Override
114    public DADNASequence getSequenceByID(Integer id) throws DAOException {
115
116        if (id==null ) {
117            throw new DAOException("Invalid parameter handed to getSequenceByID(Integer id)");
118        }
119
120        DADNASequence seq = null;
121
122        HashMap parameters = new HashMap();
123        List<HashMap> results = null;
124        parameters.put("id", id);
125
126        SequenceRowHandler handler = null;
127        SqlSession session = null;
128
129        try {
130            session = this.getFactory().getNewSqlSession();
131            SequenceMapper mapper = session.getMapper(SequenceMapper.class);
132            results = mapper.getSequenceByID(parameters);
133        } catch (Exception e) {
134            throw new DAOException("Failed to call getSequenceByID", e);
135        } finally {
136            if (session != null) {
137                session.close();
138            }
139        }
140
141        if (results ==null || results.isEmpty()) {
142            return null;
143        }
144
145        handler = new SequenceRowHandler(results);
146        handler.handleResult();
147        seq = handler.getObjectResult();
148
149
150        return seq;
151    }
152    
153    @Override
154    public DADNASequence getValidatedSequence(DNASequence seq) throws DAOException {
155
156        if (seq==null ||  seq.getId()==null) {
157            return null;
158        }
159
160       if ( !(seq instanceof DADNASequence) ) {
161           throw new DAOException("Illegal attempt to validate a non DA sequence ");
162       }
163
164        DADNASequence inseq = (DADNASequence) seq;
165        List<HashMap> results = null;
166
167        DADNASequence newSeq = null;
168
169        HashMap parameters = new HashMap();
170        parameters.put("id", inseq.getId());
171
172        SequenceRowHandler handler = null;
173        SqlSession session = null;
174
175        try {
176            session = this.getFactory().getNewSqlSession();
177            SequenceMapper mapper = session.getMapper(SequenceMapper.class);
178            results = mapper.getSequenceByID(parameters);
179        } catch (Exception e) {
180            throw new DAOException("Failed to call getValidatedSequence", e);
181        } finally {
182            if (session != null) {
183                session.close();
184            }
185        }
186
187        if (results ==null || results.isEmpty()) {
188            //this punts back the original sequence - ie it has not been validated
189            //so would probably be the wrong behavious
190            //return inseq;
191            return null;
192        }
193
194        handler = new SequenceRowHandler(results);
195        handler.handleResult();
196        newSeq = handler.getObjectResult();
197
198        if (newSeq==null) {
199            //this punts back the original sequence - ie it has not been validated
200            //so would probably be the wrong behavious
201            //return inseq;
202            return null;
203        }
204
205        //if object is a DAChromosome, that will be the cached version,
206        //so return it
207        if (newSeq instanceof DAChromosome) {
208            return newSeq;
209        }
210
211        //if the objects are same class, re-intitialize the original
212        if (inseq.getClass().equals(newSeq.getClass()) ) {
213
214            inseq.setDBSeqLength(newSeq.getDBSeqLength());
215            inseq.setCoordSystem(newSeq.getCoordSystem());
216            inseq.setName(newSeq.getName());
217            inseq.setAttributes(newSeq.getAttributes());
218            inseq.setLazyLoaded(true);
219
220            return inseq;
221            
222        }
223        //otherwise retrun the new object
224        else {
225            //beware may want to copy some data from inseq to newSeq
226            return newSeq;
227        }
228
229
230    }
231
232
233    @Override
234    public List<? extends DNASequence> getValidatedSequences(List<? extends DNASequence> in_sequences) throws DAOException {
235
236        List<DADNASequence> out_sequences = new ArrayList<DADNASequence>();
237
238        if (in_sequences==null || in_sequences.isEmpty()) {
239            return out_sequences;
240        }
241
242        List<HashMap> results = null;
243        List<DADNASequence> retrievedSeqs = null;
244        List<Integer> outIDs = new ArrayList();
245
246        // make a map of the incoming sequences that are to be validated (that have an id)
247        HashMap<Integer, DNASequence> in_idHash = new HashMap<Integer, DNASequence>();
248        for (DNASequence s: in_sequences ) {
249            if (s.getId()!= null //&& s instanceof DADNASequence
250                    ) {
251                in_idHash.put(s.getId(), s);
252            } 
253        }
254
255
256        //if we dont have any sequences to validate - return empty list
257        if (in_idHash.isEmpty() || in_idHash.keySet().isEmpty() ) {
258            return out_sequences;
259        }
260
261        List<Integer> in_ids = new ArrayList<Integer>() {};
262
263        for (Integer i: in_idHash.keySet()) {
264            in_ids.add(i);
265        }
266
267        HashMap parameters = new HashMap();
268        parameters.put("ids", in_ids);
269
270        SequenceRowHandler handler = null;
271        SqlSession session = null;
272
273        try {
274            session = this.getFactory().getNewSqlSession();
275            SequenceMapper mapper = session.getMapper(SequenceMapper.class);
276            results = mapper.getSequenceByID(parameters);
277        } catch (Exception e) {
278            throw new DAOException("Failed to call getValidatedSequences", e);
279        } finally {
280            if (session != null) {
281                session.close();
282            }
283        }
284
285        if (results ==null || results.isEmpty()) {
286             return  in_sequences;
287        }
288
289        handler = new SequenceRowHandler(results);
290        handler.handleResult();
291        retrievedSeqs  = handler.getListResult();
292
293
294        if (retrievedSeqs == null || retrievedSeqs.isEmpty() ) {
295            return  in_sequences;
296        }
297
298
299        for (DADNASequence newSeq : retrievedSeqs) {
300
301            //if object is a DAChromosome, that will be the cached version,
302            //so use it
303            if (newSeq instanceof DAChromosome) {
304                out_sequences.add(newSeq);
305                outIDs.add(newSeq.getId());
306            }
307
308
309            else if (in_idHash.get(newSeq.getId()).getClass().equals(newSeq.getClass())) {
310
311                //if the new sequence is just the same type as the original
312                //just modify the original and use it in the reretruned List
313                in_idHash.get(newSeq.getId()).setDBSeqLength(newSeq.getDBSeqLength());
314                in_idHash.get(newSeq.getId()).setCoordSystem(newSeq.getCoordSystem());
315                in_idHash.get(newSeq.getId()).setName(newSeq.getName());               
316                try {
317                    ((DADNASequence)in_idHash.get(newSeq.getId())).setAttributes(newSeq.getAttributes());
318                    out_sequences.add((DADNASequence) in_idHash.get(newSeq.getId()));
319                    outIDs.add(newSeq.getId());
320                } catch (ClassCastException cce) { }
321                
322            }
323
324
325            else {
326
327                //if the new sequence is a subclass, use it in the return List
328                //beware - we may need to copy other properties fromold to new
329                out_sequences.add(newSeq);
330                outIDs.add(newSeq.getId());
331
332            }
333
334
335        }
336
337        // ADD BACK any input sequences that werent pulled back from database
338        for (Integer i : in_idHash.keySet()) {
339            if (!outIDs.contains(i)) {
340                try {
341                    out_sequences.add((DADNASequence) in_idHash.get(i));
342                    outIDs.add(i);
343                } catch (ClassCastException cce) { }
344            }
345        }
346
347        return out_sequences;
348
349    }
350
351
352
353
354    //***********************************************
355
356
357    public class SequenceRowHandler implements DBResultHandler {
358
359        // the magic strings to be used as property keys for HashMap in Ibatis
360        private final String id = "id";
361        private final String name = "name";
362        private final String length = "length";
363        private final String coordSystemID = "csID";
364        private final String attributes = "attributes";
365        private List<HashMap> rawResults = null;
366
367        //this may be a subclass according to type
368        private DADNASequence objectResult = null;
369
370
371        private List<DADNASequence> listResult = new ArrayList<DADNASequence>();
372
373        public SequenceRowHandler(List<HashMap> results) {
374            rawResults = results;
375        }
376
377
378        @Override
379        public List<DADNASequence> getListResult() {
380            return listResult;
381        }
382
383        @Override
384        public DADNASequence getObjectResult() {
385            return objectResult;
386        }
387
388        public void handleResult() throws DAOException {
389
390            if (rawResults == null || rawResults.isEmpty()) {
391                return;
392            }
393
394            for (HashMap map : rawResults) {
395                handleRow(map);
396            }
397
398        }
399
400
401        private void handleRow(HashMap result) throws DAOException {
402
403
404            if (result == null || result.isEmpty()) {
405                return;
406            }
407
408            DADNASequence rowSequence = null;
409
410            rowSequence = this.parseResult(result);
411
412
413            if (rowSequence!= null) {
414                this.objectResult = rowSequence;
415                this.listResult.add((DADNASequence) rowSequence);
416            }
417
418        }
419
420
421        private DADNASequence parseResult(HashMap result) throws DAOException {
422
423            if (result == null) {
424                return null;
425            }
426
427            DADNASequence pseq = null;
428            Integer length = null;
429            Integer id = null;
430            Integer pcsID = null;
431            String name = null;
432            CoordinateSystem cs = null;
433            List<HashMap> attribs = null;
434
435
436            pcsID = (Integer) result.get(this.coordSystemID);
437            length = (Integer) result.get(this.length);
438            id = (Integer) result.get(this.id);
439            name = (String) result.get(this.name);
440            attribs = (List<HashMap> ) result.get(this.attributes);
441
442
443            if(singleSpecies) {
444                cs = ssFactory.getDatabase().getCSByID(pcsID);
445            } else {
446                cs= collFactory.getDatabase().getCSByID(species, pcsID);
447            }
448
449
450            if (cs.getType()==EnsemblCoordSystemType.chromosome ) {
451                pseq = new DAChromosome();
452            } else if (! cs.isSequenceLevel()) {
453                pseq = new DAAssembledDNASequence();
454            } else {
455                pseq = new DADNASequence();
456            }
457
458
459            pseq.setId(id);
460            pseq.setCoordSystem(cs);
461            pseq.setDBSeqLength(length);
462            pseq.setName(name);
463            pseq.setDaoFactory(daoFactory);
464            
465            if (attribs!=null && !attribs.isEmpty()) {
466                HashMap<String,String> temp = new HashMap<String,String>();
467                for (HashMap hm: attribs) {
468                    temp.put((String) hm.get("key"), (String) hm.get("value"));
469                }
470                pseq.setAttributes(temp);
471            }
472
473            //do check for cached chromosomes here
474            if (pseq instanceof DAChromosome)
475            {
476                pseq = species.getCachedChromosome((DAChromosome)pseq);
477            }
478            
479            //look to see if we have this sequence in cache
480            if (pseq instanceof DAAssembledDNASequence)
481            {
482                pseq = species.getCachedFragment((DAAssembledDNASequence) pseq);
483            }
484            
485            return pseq;
486
487
488        }
489    }
490
491}