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 uk.ac.roslin.ensembl.model.Mapping;
029import uk.ac.roslin.ensembl.model.MappingSet;
030import uk.ac.roslin.ensembl.dao.coreaccess.AssemblyDAO;
031import uk.ac.roslin.ensembl.datasourceaware.core.DAAssembledDNASequence;
032import uk.ac.roslin.ensembl.datasourceaware.core.DADNASequence;
033import uk.ac.roslin.ensembl.dao.factory.DAOCollectionCoreFactory;
034import uk.ac.roslin.ensembl.dao.factory.DAOSingleSpeciesCoreFactory;
035import uk.ac.roslin.ensembl.datasourceaware.core.DAChromosome;
036import uk.ac.roslin.ensembl.mapper.core.SequenceMapper;
037import uk.ac.roslin.ensembl.exception.DAOException;
038import uk.ac.roslin.ensembl.mapper.handler.DBResultHandler;
039import uk.ac.roslin.ensembl.model.Coordinate;
040import uk.ac.roslin.ensembl.model.core.Chromosome;
041import uk.ac.roslin.ensembl.model.core.CoordinateSystem;
042import uk.ac.roslin.ensembl.model.core.DNASequence;
043
044/**
045 *
046 * @author paterson
047 */
048public class DBAssemblyDAO extends DBCoreObjectDAO implements AssemblyDAO {
049
050    public DBAssemblyDAO() {
051        super();
052    }
053
054    public DBAssemblyDAO(DAOSingleSpeciesCoreFactory factory) {
055        super(factory);
056    }
057
058    public DBAssemblyDAO(DAOCollectionCoreFactory factory) {
059        super(factory);
060    }
061
062    @Override
063    public MappingSet getComponentMappingsByStartStop(DNASequence assembledSequence, Integer begin, Integer stop) throws DAOException {
064
065
066        if (assembledSequence == null || begin == null || stop == null
067                || assembledSequence.getId() == null
068                || assembledSequence.getCoordSystem() == null
069                || assembledSequence.getCoordSystem().getId() == null) {
070            throw new DAOException("Invalid parameters passed to getComponentMappingsByStartStopDNASequence assembledSequence, Integer begin, Integer stop ");
071        }
072
073        MappingSet componentMappings = new MappingSet();
074        List<Mapping> results;
075        DAAssembledDNASequence parent;
076        SqlSession session = null;
077
078        try {
079            parent = (DAAssembledDNASequence) assembledSequence;
080        } catch (ClassCastException e) {
081            throw new DAOException("This sequence is not an assembly!");
082        }
083
084        Integer start = begin;
085        Integer end = stop;
086        CoordinateSystem seqLevelCS = null;
087
088        Integer seqLevelCoordSysID = null;
089        Integer parentCoordSysID = null;
090
091        try {
092            if (singleSpecies) {
093                seqLevelCS = ssFactory.getDatabase().getSequenceLevelCoordSystem();
094            } else {
095                seqLevelCS = collFactory.getDatabase().getSequenceLevelCS(species);
096            }
097
098            if (seqLevelCS == null || seqLevelCS.getId() == null) {
099                throw new DAOException("Failed to call to retrieve the CoordinateSystem for Sequences");
100            }
101
102            seqLevelCoordSysID = seqLevelCS.getId();
103            parentCoordSysID = parent.getCoordSystem().getId();
104
105            if (seqLevelCoordSysID.equals(parentCoordSysID)) {
106                //the parent is at sequence level - so dont need a projection
107                //probably prevent this call happening before here
108                throw new DAOException("This sequence is not an assembly!");
109            }
110
111            Integer seqRegionID = parent.getId();
112
113            //none of these should be null
114
115            HashMap parameters = new HashMap();
116            parameters.put("seqRegionID", seqRegionID);
117            parameters.put("start", start);
118            parameters.put("end", end);
119            parameters.put("seqLevelCoordSysID", seqLevelCoordSysID);
120
121            session = this.getFactory().getNewSqlSession();
122            SequenceMapper mapper = session.getMapper(SequenceMapper.class);
123
124            //this first call just gets sequence levelmappings
125
126            results = mapper.getComponentSequences(parameters);
127        } catch (Exception e) {
128            throw new DAOException("Failed to call getComponentMappingsByStartStop", e);
129        } finally {
130            if (session != null) {
131                session.close();
132            }
133        }
134
135        if (results == null || results.isEmpty()) {
136            try {
137
138                Integer seqRegionID = parent.getId();
139
140                //none of these should be null
141
142                HashMap parameters = new HashMap();
143                parameters.put("seqRegionID", seqRegionID);
144                parameters.put("start", start);
145                parameters.put("end", end);
146                // dont put the sequencelevel CS in as a parameter
147
148                session = this.getFactory().getNewSqlSession();
149                SequenceMapper mapper = session.getMapper(SequenceMapper.class);
150
151                //this second call gets every level of mappings
152
153                results = mapper.getComponentSequences(parameters);
154            } catch (Exception e) {
155                throw new DAOException("Failed to call getComponentMappingsByStartStop", e);
156            } finally {
157                if (session != null) {
158                    session.close();
159                }
160            }
161
162        }
163        if (results == null || results.isEmpty()) {
164            return componentMappings;
165        }
166
167        // System.out.println("query results: "+results.size());
168        //int count = 1;
169
170        if (results != null) {
171            for (Mapping mapping : results) {
172
173                mapping.setSource(parent);
174
175                //if the target sequence is not se4quence level we need to change it to
176                // an assembledSequence
177
178                DADNASequence seq = (DADNASequence) mapping.getTarget();
179
180                if (!seq.getCsID().equals(seqLevelCoordSysID)) {
181
182                    DAAssembledDNASequence newseq = new DAAssembledDNASequence();
183                    newseq.setId(seq.getId());
184                    newseq.setName(seq.getName());
185                    newseq.setDBSeqLength(seq.getDBSeqLength());
186                    newseq.setDaoFactory(daoFactory);
187
188                    //not adding the Reader cos not at sequence level
189
190                    newseq.setCsID(seq.getCsID());
191                    if (singleSpecies) {
192                        newseq.setCoordSystem(ssFactory.getDatabase().getCSByID(seq.getCsID()));
193                    } else {
194                        newseq.setCoordSystem(collFactory.getDatabase().getCSByID(species, seq.getCsID()));
195                    }
196                    
197                    //switch
198                    mapping.setTarget(newseq);
199
200                } else {
201                    //everything returned from this query is a sequence level component
202                    seq.setDaoFactory(daoFactory);
203                    ((DADNASequence) mapping.getTarget()).setCoordSystem(seqLevelCS);
204                    //this is now done by mybatis
205                    //((DADNASequence) mapping.getTarget()).setSequenceStorage(mapping.getReader());                  
206                }
207
208                //this adds the reverse mapping to the DA DNASequence -
209                //not sure if this is required, or if better stored as another type of relationship
210
211                if (componentMappings.add(mapping)) {
212                    Mapping.addReverseMapping(mapping);
213                }
214            }
215        }
216
217
218        return componentMappings;
219
220    }
221
222    @Override
223    public MappingSet getPARMappingsByStartStop(Chromosome chr, Integer begin, Integer stop) throws DAOException {
224
225        if (chr == null || !chr.isPAR() || chr.getId() == null
226                || chr.getCoordSystem() == null
227                || chr.getCoordSystem().getId() == null) {
228            throw new DAOException("Invalid chromosome for getPARMappingsByStartStop");
229        }
230
231        DAChromosome ychr = (DAChromosome) chr;
232        //i need to get the linked chromsome ( ie X if we are mapping Y)        
233        DAChromosome xchr = ychr.getParChromosome();
234
235        if (xchr == null) {
236            throw new DAOException("No PAR chromosome found for getPARMappingsByStartStop");
237        }
238
239
240
241
242        Integer start = begin != null ? begin : ychr.getBioBegin();
243        Integer end = stop != null ? stop : ychr.getBioEnd();
244
245
246        MappingSet parMappings = new MappingSet();
247        List<HashMap> results;
248        DAAssembledDNASequence parent;
249        SqlSession session = null;
250        PARMappingHandler handler = null;
251
252
253        CoordinateSystem chrLevelCS = null;
254
255        try {
256            if (singleSpecies) {
257
258                //PARs are mapped at the chromosome level
259
260                chrLevelCS = ssFactory.getDatabase().getChromosomeLevelCoordSystem();
261            } else {
262                chrLevelCS = collFactory.getDatabase().getSequenceLevelCS(species);
263            }
264
265            if (chrLevelCS == null || chrLevelCS.getId() == null) {
266                throw new DAOException("Failed to call to retrieve the CoordinateSystem for Chromosomes");
267            }
268
269            Integer chrLevelCoordSysID = chrLevelCS.getId();
270            Integer parentCoordSysID = ychr.getCoordSystem().getId();
271
272            Integer seqRegionID = ychr.getId();
273
274            //none of these should be null
275
276            HashMap parameters = new HashMap();
277            parameters.put("seqRegionID", seqRegionID);
278            parameters.put("start", start);
279            parameters.put("end", end);
280            parameters.put("parentCoordSysID", parentCoordSysID);
281            parameters.put("chrLevelCoordSysID", chrLevelCoordSysID);
282
283            session = this.getFactory().getNewSqlSession();
284            SequenceMapper mapper = session.getMapper(SequenceMapper.class);
285            results = mapper.getPARSequences(parameters);
286        } catch (Exception e) {
287            throw new DAOException("Failed to call getPARMappingsByStartStop", e);
288        } finally {
289            if (session != null) {
290                session.close();
291            }
292        }
293
294        if (results == null || results.isEmpty()) {
295            return parMappings;
296        }
297
298        if (results != null && !results.isEmpty()) {
299
300            handler = new PARMappingHandler(results, ychr, xchr);
301            handler.handleResult();
302            parMappings.addAll(handler.getListResult());
303        }
304
305        return parMappings;
306
307    }
308
309    //as an inner class it has access to the factory etc of the enclosing class
310    public class PARMappingHandler implements DBResultHandler {
311
312        // the magic strings to be used as property keys for HashMap in Ibatis
313        private final String componentID = "componentID";
314        private final String targetCoordinates = "targetCoordinates";
315        private final String sourceCoordinates = "sourceCoordinates";
316        private Mapping objectResult = null;
317        private List<Mapping> listResult = new ArrayList<Mapping>();
318        private List<HashMap> rawResults = null;
319        private DAChromosome sourceChr = null;
320        private DAChromosome targetChr = null;
321        private int targetID = 0;
322
323        public PARMappingHandler(List<HashMap> results, DAChromosome source, DAChromosome target) {
324            sourceChr = source;
325            targetChr = target;
326            rawResults = results;
327
328            targetID = targetChr.getId();
329
330        }
331
332        @Override
333        public List<Mapping> getListResult() {
334            return listResult;
335        }
336
337        @Override
338        public Mapping getObjectResult() {
339            return objectResult;
340        }
341
342        public void handleResult() throws DAOException {
343
344            if (rawResults == null || rawResults.isEmpty()) {
345                return;
346            }
347
348            for (HashMap map : rawResults) {
349                handleRow(map);
350            }
351
352        }
353
354        private void handleRow(HashMap result) throws DAOException {
355
356            Mapping rowMapping = null;
357
358            rowMapping = this.parseResult(result);
359
360
361            if (rowMapping != null) {
362                this.objectResult = rowMapping;
363                this.listResult.add(rowMapping);
364            }
365
366
367        }
368
369        private Mapping parseResult(HashMap result) throws DAOException {
370
371            if (result == null || result.isEmpty()) {
372                return null;
373            }
374
375
376            Coordinate sCoords = null;
377            Coordinate tCoords = null;
378            Integer tID = null;
379
380
381            tID = (Integer) result.get(this.componentID);
382            sCoords = (Coordinate) result.get(this.sourceCoordinates);
383            tCoords = (Coordinate) result.get(this.targetCoordinates);
384
385
386            if (tID != this.targetID
387                    || sCoords == null
388                    || tCoords == null) {
389                return null;
390            }
391
392
393            Mapping mapping = new Mapping();
394            mapping.setSource(this.sourceChr);
395            mapping.setSourceCoordinates(sCoords);
396            mapping.setTarget(this.targetChr);
397            mapping.setTargetCoordinates(tCoords);
398
399
400
401            if (this.sourceChr.addMapping(mapping)) {
402                Mapping.addReverseMapping(mapping);
403            }
404
405            return mapping;
406
407
408        }
409    }
410}