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.datasourceaware.core;
023
024import java.util.*;
025import org.biojava3.core.sequence.DNASequence;
026import org.biojava3.core.sequence.ProteinSequence;
027import org.biojava3.core.sequence.RNASequence;
028import org.biojava3.core.sequence.transcription.TranscriptionEngine;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031import uk.ac.roslin.ensembl.config.DBConnection;
032import uk.ac.roslin.ensembl.config.EnsemblCoreObjectType;
033import uk.ac.roslin.ensembl.config.ExternalDBType;
034import uk.ac.roslin.ensembl.datasourceaware.DAXRef;
035import uk.ac.roslin.ensembl.exception.DAOException;
036import uk.ac.roslin.ensembl.exception.RangeException;
037import uk.ac.roslin.ensembl.model.Coordinate.Strand;
038import uk.ac.roslin.ensembl.model.*;
039import uk.ac.roslin.ensembl.model.core.ProteinFeature;
040import uk.ac.roslin.ensembl.model.core.Transcript;
041import uk.ac.roslin.ensembl.model.core.Translation;
042import uk.ac.roslin.ensembl.model.core.VegaFeature;
043
044public class DATranslation extends DACoreObject implements Translation, XRefed, VegaFeature {
045
046    DATranscript transcript = null;
047    List<? extends ProteinFeature> proteinFeatures = null;
048    private boolean canonical = false;
049    private DAExon firstExon = null;
050    private Integer firstExonID = null;
051    private DAExon lastExon = null;
052    private Integer lastExonID = null;
053    private Integer firstExonStart = null;
054    private Integer lastExonEnd = null;
055    private Integer transcriptID = null;
056    private DNASequence translatedSequence = null;
057    /**
058     * Each Mapping in this TreeSet has  
059     * source: null (implicitly the translated sequence) 
060     * sourceCoordinates: the coordinate on the translated sequence - including 
061     * any 1 or 2 residue shift due to Exon1 phase issues. 
062     * target: the (unprocessed) primaryTranscript specifying this Translation
063     * targetCoordinates: the coordinates on the (unprocessed) pimaryTranscript
064     */
065    MappingSet translationMappings = null;
066    final static Logger LOGGER = LoggerFactory.getLogger(DATranslation.class);
067    private String vegaProteinID = null;
068    protected  Set<DAXRef> xrefs = new HashSet<DAXRef>();
069    protected HashMap<ExternalDBType, Set<DAXRef>> typedXRefs = new HashMap<ExternalDBType, Set<DAXRef>>();
070    boolean xrefsInitialized = false;
071    private boolean initialized;
072    private TreeSet<String> synonyms = null;
073  
074    /*
075     * Temporarily just a demo function... 
076     */
077    @Override
078    public List<? extends ProteinFeature> getProteinFeatures() {
079        if (proteinFeatures == null) {
080
081            if (this.getId() == null) {
082                return null;
083            }
084
085            try {
086                proteinFeatures = this.getDaoFactory().getProteinFeatureDAO().getProteinFeaturesByTranslationID(this.id);
087            } catch (DAOException dex) {
088                LOGGER.info("failed to get Protein Features", dex);
089            } catch (Exception ex) {
090                LOGGER.info("failed to get Protein Features", ex);
091            }
092
093        }
094        return proteinFeatures;
095    }
096
097    @Override
098    public DATranscript getTranscript() {
099        //v1.15 previously Translations were only instantiated by the MyBatis/DAO mapping
100        //TranslationDAO.getTranslationsForTranscript(Transcript transcript)
101        //and therefor the transcript were set on all translations
102        //now we have the ability to retrieve a transaltion by stableID from a species
103        //so we need to be able to load the transcript for this
104        
105        if (transcript!=null) {
106            return transcript;
107        }
108        this.reinitialize();
109        if (this.transcriptID!=null && this.getDaoFactory()!= null) {
110            try {
111                transcript = (DATranscript) this.getDaoFactory().getTranscriptDAO().getTranscriptByID(this.transcriptID);
112            } catch (DAOException ex) {
113                    LOGGER.info("Threw DAOException on trying to get Transcript: "+this.transcriptID, ex);
114                }
115        }
116        
117        return transcript;
118    }
119    
120    public boolean isInitialized() {
121        return initialized;
122    }
123    
124    public void setInitialized(boolean init) {
125        this.initialized = init;
126    }
127
128    void reinitialize()  { 
129        if (this.isInitialized()) {
130            return;
131        }
132        try {
133            //nb getDaoFactory() will try and make a factory if we have at least species and ensembl version
134            this.getDaoFactory().getTranslationDAO().reInitialize(this);
135                       
136        } catch (Exception ex) {
137            LOGGER.info("Failed to reinitialize the Gene from the Database (using its stableID: "
138                    +this.stableID+").", ex);
139        } finally {
140            //always set this so dont try again
141            this.setInitialized(true);
142        }
143    }
144    
145    @Override
146    public void setTranscript(Transcript transcript) {
147        this.transcript = (DATranscript) transcript;
148    }
149
150    @Override
151    public ObjectType getType() {
152        return EnsemblCoreObjectType.translation;
153    }
154
155    /**
156     * Returns whether this Translation is marked as the Canonical Translation for 
157     * its transcript. (Prior to Ensembl57 there was a single translation object 
158     * for each transcript, which is considered as canonical.)
159     */
160    public boolean isCanonical() {
161        return canonical;
162    }
163
164    public void setCanonical(boolean canonical) {
165        this.canonical = canonical;
166    }
167
168    /**
169     * Returns the internal database ID of the first Exon for this translation.
170     */
171    public Integer getFirstExonID() {
172        return firstExonID;
173    }
174    
175    public void setFirstExonID(Integer firstExonID) {
176        this.firstExonID = firstExonID;
177    }
178
179    /**
180     * Returns the (relative) position of the translational start in the first exon of this Translation.
181     */
182    public Integer getFirstExonStart() {
183        return firstExonStart;
184    }
185
186    public void setFirstExonStart(Integer firstExonStart) {
187        this.firstExonStart = firstExonStart;
188    }
189
190    /**
191     * Returns the internal database ID of the last Exon for this translation.
192     */
193    public Integer getLastExonID() {
194        return lastExonID;
195    }
196
197    public void setLastExonID(Integer lastExonID) {
198        this.lastExonID = lastExonID;
199    }
200
201    @Override
202    public String getStableID() {
203        return stableID;
204    }
205
206    @Override
207    public void setStableID(String stableID) {
208        this.stableID = stableID;
209    }
210
211    /**
212     * Returns the first Exon of this Translation.
213     */
214    public DAExon getFirstExon() {
215        if (this.firstExon != null) {
216            return this.firstExon;
217        }
218
219        for (DAExon e : this.getTranscript().getExons()) {
220            if (e.getId().equals(this.getFirstExonID())) {
221                this.firstExon = e;
222            }
223            if (e.getId().equals(this.getLastExonID())) {
224                this.lastExon = e;
225            }
226        }
227        return firstExon;
228    }
229
230    /**
231     * Gets the Gene associated with this Translation via the associated Transcript.
232     */
233    public DAGene getGene() {
234        if (this.getTranscript() != null) {
235            return this.getTranscript().getGene();
236        } else {
237            return null;
238        }
239    }
240
241    /**
242     * Returns the last Exon of this Translation.
243     */
244    public DAExon getLastExon() {
245
246        if (this.lastExon != null) {
247            return this.lastExon;
248        }
249
250        for (DAExon e : this.getTranscript().getExons()) {
251            if (e.getId().equals(this.getFirstExonID())) {
252                this.firstExon = e;
253            }
254            if (e.getId().equals(this.getLastExonID())) {
255                this.lastExon = e;
256            }
257        }
258        return lastExon;
259
260    }
261
262    
263    /**
264     * Returns the (relative) position of the translational end in the last exon 
265     * of this Translation. This is inclusive of the stop codon.
266     */    
267    public Integer getLastExonEnd() {
268        return lastExonEnd;
269    }
270
271    public void setLastExonEnd(Integer lastExonEnd) {
272        this.lastExonEnd = lastExonEnd;
273    }
274
275    /**
276     * Returns a (BioJava) ProteinSequence for this Translation using the specified 
277     * Codon Table ID to retrieve the appropriate  (BioJava)  TranscriptionEngine.
278     */
279    public ProteinSequence getProteinSequence(Integer codonTable) {
280        /*
281         * By default BioJava uses  a Transcription engine singleton that uses 
282         * the universal codon table, we can create alternative codon usage 
283         * engines from the common IDed tables
284         * 1 - UNIVERSAL
285         * 2 - VERTEBRATE_MITOCHONDRIAL
286         * 3 - YEAST_MITOCHONDRIAL
287         * 4 - MOLD_MITOCHONDRIAL
288         * 5 - INVERTEBRATE_MITOCHONDRIAL
289         * 6 - CILIATE_NUCLEAR
290         * 9 - ECHINODERM_MITOCHONDRIAL
291         * 10 - EUPLOTID_NUCLEAR
292         * 11 - BACTERIAL
293         * 12 - ALTERNATIVE_YEAST_NUCLEAR
294         * 13 - ASCIDIAN_MITOCHONDRIAL
295         * 14 - FLATWORM_MITOCHONDRIAL
296         * 15 - BLEPHARISMA_MACRONUCLEAR
297         * 16 - 2CHLOROPHYCEAN_MITOCHONDRIAL
298         * 21 - TREMATODE_MITOCHONDRIAL
299         * 23 - SCENEDESMUS_MITOCHONDRIAL 
300         */
301        return this.getProteinSequence(this.getRegistry().getTranscriptionEngine(codonTable));
302    }
303
304    /**
305     * Returns a (BioJava) ProteinSequence for this Translation using the specified (BioJava) 
306     * TranscriptionEngine.
307     *  
308     */
309    public ProteinSequence getProteinSequence(TranscriptionEngine trancriptionEngine) {
310
311        if (trancriptionEngine == null) {
312            return null;
313        }
314
315        if (this.translatedSequence == null) {
316            this.translate();
317        }
318
319        if (this.translatedSequence != null
320                && this.translatedSequence.getLength() > 2) {
321            //vanilla BioJava Translation
322            //a bug in BioJava-core 3.0 which fails to translate some codons with N in, needs shift up to v3.0.3
323            return  this.translatedSequence.getRNASequence(trancriptionEngine).getProteinSequence(trancriptionEngine);
324            
325        } else {
326            return null;
327        }
328    }
329
330    /**
331     * Returns a (BioJava) ProteinSequence for this Translation. IF possible it will use a 
332     * TranscriptionEngine configured to use the Codon table specified by the annotated Chromosome.
333     * Otherwise it will use the default Universal codon table.
334     *  
335     */
336    @Override
337    public ProteinSequence getProteinSequence() {
338
339        Integer codonTable = 1;
340
341        if (this.getTranscript() != null) {
342
343            try {
344                codonTable = this.getTranscript().getTargetSequence().getCodonTableID();
345            } catch (Exception ex) {
346            }
347        }
348        //if no codon_table attribute has been set for the DNASequence we just use the defualt
349        return this.getProteinSequence(this.getRegistry().getTranscriptionEngine(codonTable));
350    }
351
352   /**
353    * Convenience method to get the Amino Acid sequence of the protein product of
354    * this translation. Wraps BioJava ProteinSequence functionality. Note that no 
355    * terminator (*) is appended. 
356    *  
357    */
358    public String getProteinSequenceAsString() {
359        return this.getProteinSequence().getSequenceAsString();
360    }
361    
362    /**
363     * Convenience method to get the Amino Acid sequence of the protein product of
364     * this translation. Wraps BioJava ProteinSequence functionality, using the specified 
365     * Codon Table ID to retrieve the appropriate  (BioJava)  TranscriptionEngine.
366     * Note that no terminator (*) is appended. 
367     * @param codonTable 
368     *  
369     */
370    public String getProteinSequenceAsString(Integer codonTable) {
371        return this.getProteinSequence(codonTable).getSequenceAsString();
372    }
373    
374    /**
375     * Convenience method to get the Amino Acid sequence of the protein product of
376     * this translation. Wraps BioJava ProteinSequence functionality, using the specified 
377     * (BioJava)  TranscriptionEngine. Note that no terminator (*) is appended. 
378     * @param trancriptionEngine
379     *  
380     */
381    public String getProteinSequenceAsString(TranscriptionEngine trancriptionEngine) {
382        return this.getProteinSequence(trancriptionEngine).getSequenceAsString();
383    }
384    
385    /**
386     * Convenience method to get the Amino Acid sequence of the given range of the 
387     * protein product of this translation. Wraps BioJava ProteinSequence functionality.
388     * Note that terminators (*) will be appended for positions beyond the end of the peptide,
389     * but within the translation (i.e. stop codons). 
390     * @param start
391     * @param end
392     * @throws RangeException if position does not lie within the peptide and stop codons.  
393     *  
394     */
395    public String getProteinSequenceAsString(Integer start, Integer end) {
396        
397        org.biojava3.core.sequence.Strand strand = org.biojava3.core.sequence.Strand.POSITIVE;       
398        if (start!=null && end !=null && start>end) {
399             //Integers are assigned by value not as pointers
400             Integer temp = end;
401             end = start;
402             start = temp;
403             strand = org.biojava3.core.sequence.Strand.NEGATIVE;
404        }
405        
406         if (start==null || start<1 
407                 || end == null || end < 1 || 3*end > this.getLength()) {
408            throw new RangeException("An amino acid position must lie between 1 and the length of the peptide. ");
409        }       
410         
411        String stop = "";
412         
413        if (start > this.getProteinSequence().getLength() ) {
414            return GapSequence.getGapString((end-start)+1, '*');
415        } 
416         
417         
418        for (int i=0; i<end-this.getProteinSequence().getLength();i++) {
419            stop += "*";
420        } 
421        end -= stop.length();
422        if (org.biojava3.core.sequence.Strand.NEGATIVE.equals(strand)) {
423            return stop+this.getProteinSequence().getSequenceAsString(start,end,strand);
424        }
425        return this.getProteinSequence().getSequenceAsString(start,end,org.biojava3.core.sequence.Strand.POSITIVE)+stop;        
426    }
427    
428    /**
429     * Convenience method to get the Amino Acid sequence of the given range of the 
430     * protein product of this translation. 
431     * Wraps BioJava ProteinSequence functionality, using the specified 
432     * Codon Table ID to retrieve the appropriate  (BioJava)  TranscriptionEngine.
433     * For 'start'>'end' the amino acid sequence will be written in reverse.
434     * Note that terminators (*) will be appended for positions beyond the end of the peptide,
435     * but within the translation (i.e. stop codons). 
436     * @param start
437     * @param end
438     * @throws RangeException if position does not lie within the peptide 
439     *  
440     */
441    public String getProteinSequenceAsString(Integer codonTable, Integer start, Integer end) {
442        
443        org.biojava3.core.sequence.Strand strand = org.biojava3.core.sequence.Strand.POSITIVE;       
444        if (start!=null && end !=null && start>end) {
445             //Integers are assigned by value not as pointers
446             Integer temp = end;
447             end = start;
448             start = temp;
449             strand = org.biojava3.core.sequence.Strand.NEGATIVE;
450        }
451        
452        if (start==null || start<1 
453                 || end == null || end < 1 || 3*end > this.getLength()) {
454            throw new RangeException("An amino acid position must lie between 1 and the length of the peptide. ");
455        }
456         
457        if (start > this.getProteinSequence(codonTable).getLength() ) {
458            return GapSequence.getGapString((end-start)+1, '*');
459        }  
460        String stop = "";
461        for (int i=0; i<end-this.getProteinSequence(codonTable).getLength();i++) {
462            stop += "*";
463        } 
464        end -= stop.length();         
465
466        if (org.biojava3.core.sequence.Strand.NEGATIVE.equals(strand)) {
467            return stop+this.getProteinSequence(codonTable).getSequenceAsString(start,end,strand);
468        }
469        return this.getProteinSequence(codonTable).getSequenceAsString(start,end,org.biojava3.core.sequence.Strand.POSITIVE)+stop;        
470        
471    }
472    
473    /**
474     * Convenience method to get the Amino Acid sequence of the given range of the 
475     * protein product of this translation. 
476     * Wraps BioJava ProteinSequence functionality, using the specified 
477     * (BioJava)  TranscriptionEngine.
478     * For 'start'>'end' the amino acid sequence will be written in reverse.
479     * Note that terminators (*) will be appended for positions beyond the end of the peptide,
480     * but within the translation (i.e. stop codons). 
481     * @param start
482     * @param end
483     * @throws RangeException if position does not lie within the peptide 
484     *  
485     */    
486    public String getProteinSequenceAsString(TranscriptionEngine trancriptionEngine, Integer start, Integer end) {
487        
488        org.biojava3.core.sequence.Strand strand = org.biojava3.core.sequence.Strand.POSITIVE;       
489        if (start!=null && end !=null && start>end) {
490             //Integers are assigned by value not as pointers
491             Integer temp = end;
492             end = start;
493             start = temp;
494             strand = org.biojava3.core.sequence.Strand.NEGATIVE;
495        }
496        
497        if (start==null || start<1  
498                || end == null || end < 1 || 3*end > this.getLength()) {
499            throw new RangeException("An amino acid position must lie between 1 and the length of the peptide. ");
500        }
501        
502        if (start > this.getProteinSequence(trancriptionEngine).getLength() ) {
503            return GapSequence.getGapString((end-start)+1, '*');
504        } 
505        
506        String stop = "";
507        for (int i=0; i<end-this.getProteinSequence(trancriptionEngine).getLength();i++) {
508            stop += "*";
509        } 
510        end -= stop.length();  
511        
512        if (org.biojava3.core.sequence.Strand.NEGATIVE.equals(strand)) {
513            return stop+this.getProteinSequence(trancriptionEngine).getSequenceAsString(start,end,strand);
514        }
515        
516        return this.getProteinSequence(trancriptionEngine).getSequenceAsString(start,end,org.biojava3.core.sequence.Strand.POSITIVE)+stop;
517    }
518
519    /**
520     * Returns a String representation of this Translation ( i.e. the DNA Sequence...).
521     *  
522     */
523    public String getTranslatedSequenceAsString() {
524
525        if (translatedSequence == null) {
526            this.translate();
527        }
528
529        if (translatedSequence != null) {
530           return  translatedSequence.getSequenceAsString();
531        } else {
532            return "";
533        }
534    }
535    
536    /**
537     * Returns a String representation of this Translation for the given range 
538     * (i.e. the DNA Sequence...).
539     * @param start
540     * @param stop
541     * 
542     * @throws RangeException
543     * @throws IllegalArgumentException 
544     */
545    public String getTranslatedSequenceAsString(Integer start, Integer stop) throws RangeException, IllegalArgumentException {
546        
547        if (start == null || start==  0 ) {
548            throw new IllegalArgumentException("Translated sequence begins at +1.");
549        }
550        
551        if (translatedSequence == null) {
552            this.translate();
553        }
554
555        if (translatedSequence != null) {
556            if ( stop > translatedSequence.getLength()) {
557                throw new RangeException("Specified end is beyond ends of translation.");
558            }
559            //the translated sequence object is a BioJava DNASequence object so we call its methods directly
560            return  translatedSequence.getSequenceAsString(start,stop,org.biojava3.core.sequence.Strand.POSITIVE);
561        } else {
562            return "";
563        }
564    }
565        
566    /**
567     * Returns the mapped position of a translation (nucleotide) position on the unprocessed
568     * primary transcript that provides this translation.
569     * @param translationBase a nucleotide position on the translated sequence 
570     *  
571     */
572    public Integer getPrimaryTranscriptPositionFromBASE(Integer translationBase) {
573        
574        Integer result = null;
575        if (translationMappings == null) {
576            this.translate();
577        }
578                        
579        if (translationMappings != null && !translationMappings.isEmpty()) {
580            
581            if (translationBase<1) {
582                result = translationMappings.first().getTargetCoordinates().getStart()+translationBase;
583                if (result<1 && translationMappings.first().getTargetCoordinates().getStart()>0 ) {
584                    result--;
585                }
586                return result;
587            }            
588            if (translationBase > translationMappings.last().getSourceCoordinates().getEnd()) {
589                result = translationMappings.last().getTargetCoordinates().getEnd()+
590                        translationBase - translationMappings.last().getSourceCoordinates().getEnd();
591                return result;
592            }            
593            
594            for (Mapping m : translationMappings) {
595                if (m.getSourceCoordinates().containsPoint(translationBase)) {
596                    Integer offset = translationBase-m.getSourceCoordinates().getStart();
597                    return m.getTargetCoordinates().getStart()+offset;
598                }
599            }           
600        }
601        return result;
602    }
603    
604    /**
605     * Returns the mapped position of a translation (nucleotide) position on the (spliced) processed
606     * transcript that provides this translation. Querying for upstream or downstream positions 
607     * is unreliable, and may return null if falling within an intron.
608     * @param translationBase a nucleotide position on the translated sequence
609     *  
610     */
611    public Integer getProcessedTranscriptPositionFromBASE(Integer translationBase) throws DAOException {
612        
613        Integer result = null;
614        Integer primaryT = this.getPrimaryTranscriptPositionFromBASE(translationBase);
615       
616        if (primaryT != null && this.getTranscript()!=null) {
617                result = this.getTranscript().convertPrimaryToProcessedTranscriptPosition(primaryT);
618        }       
619        
620        return result;
621    }
622    
623    /**
624     * Returns the mapped chromosomal position of a translation (nucleotide) position.
625     * @param translationBase a nucleotide position on the translated sequence
626     * 
627     * @throws DAOException 
628     */
629    public Integer getChromosomePositionFromBASE(Integer translationBase) throws DAOException {
630        
631        Integer result = null;
632        Integer primaryT = this.getPrimaryTranscriptPositionFromBASE(translationBase);
633       
634        if (primaryT != null) {
635                result = this.getTranscript().convertPrimaryTranscriptPositionToChromosome(primaryT);
636        }       
637        
638        return result;
639    }
640    
641    /**
642     * Returns the mapped coordinates of an amino acid  position on the unprocessed
643     * primary transcript that provides this translation. 
644     * @param aminoAcidPosition
645     *  
646     * @throws RangeException if position does not lie within the peptide 
647     */
648    public Coordinate getPrimaryTranscriptPositionFromAA(Integer aminoAcidPosition) throws RangeException {
649        
650        if (aminoAcidPosition==null || aminoAcidPosition<1 || 3*aminoAcidPosition>this.getLength()) {
651            throw new RangeException("An amino acid position must lie between 1 and the length of the peptide. ");
652        }
653        
654        Strand strand = null;
655        try {
656            strand = this.getTranscript().getTopLevelTargetCoordinates().getStrand();
657        } catch (Exception ex) {
658
659        }
660        
661        Integer start = getPrimaryTranscriptPositionFromBASE(3*aminoAcidPosition-2);
662        Integer end = getPrimaryTranscriptPositionFromBASE(3*aminoAcidPosition);
663        if (end ==null || start ==null ) {
664            return null;
665        }
666        return new Coordinate(start,end,strand);        
667    }
668    
669    /**
670     * Returns the mapped coordinates of an amino acid position on the (spliced) processed
671     * transcript that provides this translation. Querying for upstream or downstream positions 
672     * is unreliable, and may return null if falling within an intron.
673     * @param aminoAcidPosition
674     *  
675     * @throws RangeException if position does not lie within the peptide 
676     */
677    public Coordinate getProcessedTranscriptPositionFromAA(Integer aminoAcidPosition) throws DAOException,RangeException {
678        if (aminoAcidPosition==null || aminoAcidPosition<1 || aminoAcidPosition>3*this.getLength()) {
679            throw new RangeException("An amino acid position must lie between 1 and the length of the peptide. ");
680        }
681        Strand strand = null;
682        try {
683            strand = this.getTranscript().getTopLevelTargetCoordinates().getStrand();
684        } catch (Exception ex) {
685
686        }     
687        Integer start = getProcessedTranscriptPositionFromBASE(3*aminoAcidPosition-2);
688        Integer end = getProcessedTranscriptPositionFromBASE(3*aminoAcidPosition);
689        if (end ==null || start ==null ) {
690            return null;
691        }
692        return new Coordinate(start,end,strand);     
693    }
694    
695    /**
696     * Returns the mapped chromosomal coordinates of an amino acid position.
697     * @param aminoAcidPosition
698     * 
699     * @throws DAOException 
700     * @throws RangeException if position does not lie within the peptide 
701     */
702    public Coordinate getChromosomePositionFromAA(Integer aminoAcidPosition) throws DAOException,RangeException {
703        if (aminoAcidPosition==null || aminoAcidPosition<1 || 3*aminoAcidPosition>this.getLength()) {
704            throw new RangeException("An amino acid position must lie between 1 and the length of the peptide. ");
705        }
706        Strand strand = null;
707        try {
708            strand = this.getTranscript().getTopLevelTargetCoordinates().getStrand();
709        } catch (Exception ex) {
710
711        }    
712        Integer start = getChromosomePositionFromBASE(3*aminoAcidPosition-2);
713        Integer end = getChromosomePositionFromBASE(3*aminoAcidPosition);
714        if (end ==null || start ==null ) {
715            return null;
716        }
717        return new Coordinate(start,end,strand);  
718    }
719
720    /**
721     * Returns the nucleotide position on this translation sequence for a given 
722     * Chromosomal Position
723     * @param chromosomePosition
724     *  
725     */
726    public Integer getBasePositionFromChromosome(Integer chromosomePosition) {
727       
728        DATranscript primTranscript = this.getTranscript();
729        
730        if (primTranscript == null) {
731            return null;
732        }
733        
734        if (this.translationMappings == null) {
735            this.translate();
736        }
737
738        if (this.translatedSequence == null
739                || this.translatedSequence.getLength() <1 || translationMappings==null) {
740            return null;
741        }
742        
743        Integer primTPos = primTranscript.convertChromosomeToPrimaryTranscriptPosition(chromosomePosition);
744        
745        if (primTPos<1 || primTPos>primTranscript.getLength()) {
746            return null;
747        }
748        return getBasePositionFromPrimaryTranscript(primTPos);
749    }
750    
751    /**
752     * Returns the nucleotide position on this translation sequence for a given 
753     * primary Transcript position
754     * @param primaryTranscriptPosition
755     *  
756     */
757    public Integer getBasePositionFromPrimaryTranscript(Integer primaryTranscriptPosition) {
758        
759        if (primaryTranscriptPosition==null || primaryTranscriptPosition==0) {
760            throw new IllegalArgumentException("Zero is not a valid position.");
761        }
762               
763        if (this.getTranscript() == null) {
764            return null;
765        }
766        
767        if (this.translationMappings == null) {
768            this.translate();
769        }
770
771        if (this.translatedSequence == null
772                || this.translatedSequence.getLength() <1 || translationMappings==null ) {
773            return null;
774        }
775        
776        for (Mapping m: translationMappings) {
777            if (m.getTargetCoordinates().containsPoint(primaryTranscriptPosition)) {
778                Integer offset = primaryTranscriptPosition-m.getTargetCoordinates().getStart();
779                return m.getSourceCoordinates().getStart()+offset;
780            }
781        }
782        
783        return null;
784    }
785    
786    /**
787     * Returns the nucleotide position on this translation sequence for a given 
788     * processed (spliced) transcript position.
789     * @param processedTranscriptPosition
790     *  
791     */
792    public Integer getBasePositionFromProcessedTranscript(Integer processedTranscriptPosition) {
793
794        DATranscript primTranscript = this.getTranscript();
795        
796        if (primTranscript == null) {
797            return null;
798        }
799        
800        if (this.translationMappings == null) {
801            this.translate();
802        }
803
804        if (this.translatedSequence == null
805                || this.translatedSequence.getLength() <1 || translationMappings==null) {
806            return null;
807        }
808        Integer primTPos  = null;
809        
810        try {
811            primTPos = primTranscript.convertProcessedToPrimaryTranscriptPosition(processedTranscriptPosition);
812        } catch (DAOException ex) {
813            return null;
814        }
815        
816        if (primTPos ==null || primTPos<1 || primTPos>primTranscript.getLength()) {
817            return null;
818        }
819        
820        return getBasePositionFromPrimaryTranscript(primTPos);
821    }
822    
823    /**
824     * Returns the amino acid position on this translation for a given
825     * Chromosomal Position
826     * @param chromosomePosition
827     *  
828     */
829    public Integer getAAPositionFromChromosome(Integer chromosomePosition) {
830        
831        Integer transPosition = this.getBasePositionFromChromosome(chromosomePosition);
832        
833        if (transPosition==null || transPosition <1 || transPosition> this.getLength()) {
834            return null;
835        }
836        
837        Integer two = 2;
838        Integer three = 3;
839        return (transPosition+two)/three;
840    }
841    
842    /**
843     * Returns the amino acid position on this translation for a given
844     * Primary Transcript Position
845     * @param primaryTranscriptPosition
846     *  
847     */    
848    public Integer getAAPositionFromPrimaryTranscript(Integer primaryTranscriptPosition) {
849        Integer transPosition = this.getBasePositionFromPrimaryTranscript(primaryTranscriptPosition);
850        
851        if (transPosition==null || transPosition <1 || transPosition> this.getLength()) {
852            return null;
853        }
854        
855        Integer two = 2;
856        Integer three = 3;
857        return (transPosition+two)/three;
858    }
859    
860    /**
861     * Returns the amino acid position on this translation for a given
862     * Processed Transcript Position
863     * @param processedTranscriptPosition
864     *  
865     */     
866    public Integer getAAPositionFromProcessedTranscript(Integer processedTranscriptPosition) {
867        Integer transPosition = this.getBasePositionFromProcessedTranscript(processedTranscriptPosition);
868        
869        if (transPosition==null || transPosition <1 || transPosition> this.getLength()) {
870            return null;
871        }
872        
873        Integer two = 2;
874        Integer three = 3;
875        return (transPosition+two)/three;
876    }
877    
878    
879    /**
880     * Private method that stitches together the ORF coordinates into a map of 
881     * Translation/ORF Coordinate to (unprocessed) primaryTranscript Coordinate, and initializes 
882     * a DNASequence object representing this translated sequence. 
883     */
884    private void translate() {
885        
886        //note it was easier to stitch together the processedTranscript-->translation map
887        //however this 'lacks' any notion of the introns - so is no good for mapping
888        //back from translation to primaryTranscript
889
890        translationMappings = new MappingSet();
891        boolean intranslated = false;
892        String seq = "";
893        
894        //If something is wrong and the DNASequence for the Exons cannot be retrieved
895        //the seq is set to GapSequences of lengths based on the exon lengths.        
896        
897        if (this.getTranscript() == null) {
898            return;
899        }
900        Collection<DAExon> exons = this.getTranscript().getExons();
901        if (exons == null || exons.isEmpty()) {
902            return;
903        }
904        
905        Coordinate topLevelTargetCoordinates = null;
906        try {
907            topLevelTargetCoordinates = this.getTranscript().getTopLevelTargetCoordinates();
908        } catch (DAOException ex) {
909            translatedSequence = new DNASequence(seq);
910            return;
911        }
912        
913        if (topLevelTargetCoordinates==null || topLevelTargetCoordinates.getEnd()==null
914                || topLevelTargetCoordinates.getStart()==null) {
915            translatedSequence = new DNASequence(seq);
916            return;
917        }
918        
919        Strand strand = topLevelTargetCoordinates.getStrand();
920
921        Integer chromosomalStart;
922        if (Strand.REVERSE_STRAND.equals(strand)) {
923            chromosomalStart = topLevelTargetCoordinates.getEnd(); 
924        } else {
925            chromosomalStart = topLevelTargetCoordinates.getStart();
926        }
927        
928        Integer transcriptPosition=0;
929        
930        for (DAExon ex : exons) {
931            
932            if (Strand.REVERSE_STRAND.equals(strand)) {
933                transcriptPosition = chromosomalStart - ex.convertToTargetPosition(1) + 1;
934            } else {
935                transcriptPosition = ex.convertToTargetPosition(1) - chromosomalStart + 1;
936            }
937            
938            
939            if (!intranslated && ex.getId().equals(this.getFirstExonID())) {
940
941                Coordinate transcript ;
942                Coordinate translation ;
943                
944                
945                intranslated = true;
946
947                //case of a single exon translation
948                if (ex.getId().equals(this.getLastExonID())) {
949                    try {                       
950//                        if (Strand.REVERSE_STRAND.equals(strand)) {
951//                            seq = ex.getSequence().getReverseComplementSequenceAsString( ex.getLength() - this.getLastExonEnd() + 1, ex.getLength()-this.getFirstExonStart() + 1);
952//                        } else {
953//                            seq = ex.getSequence().getSequenceAsString(this.getFirstExonStart(), this.getLastExonEnd());
954//                        }
955                        seq = ex.getSequenceAsString(this.getFirstExonStart(), this.getLastExonEnd());
956                    } catch (Exception e) {
957                        seq = GapSequence.getGapString( this.getLastExonEnd()-  this.getFirstExonStart()+1);
958                    }
959                    Mapping m = new Mapping();
960                    m.setTarget(this.getTranscript());
961                    
962                    //case where we are missing one or two 5'bases for the correct phase
963                    if (ex.getPhase() == 1) {
964                        seq = "N" + seq;
965                        translation = new Coordinate(1+ex.getPhase(), seq.length()+ex.getPhase());
966                    } else if (ex.getPhase() == 2) {
967                        seq = "NN" + seq;
968                        translation = new Coordinate(1+ex.getPhase(), seq.length()+ex.getPhase());
969                    } else {
970                        translation = new Coordinate(1, seq.length());
971                    }
972                    transcript = new Coordinate(transcriptPosition+this.getFirstExonStart()-1,transcriptPosition+this.getLastExonEnd()-1); 
973                    m.setSourceCoordinates(translation);
974                    m.setTargetCoordinates(transcript);
975                    translationMappings.add(m);                    
976                    
977                    break;
978                }
979                try {                   
980//                      if (Strand.REVERSE_STRAND.equals(strand)) {
981//                          
982//                          System.out.println("ex.getSequenceAsString(1,30): "+ex.getSequenceAsString(1,30));
983//                          System.out.println("ex.getSequence().getSequenceAsString(1,30): "+ex.getSequence().getSequenceAsString(1,30));
984//                          System.out.println("ex.getSequence().getReverseComplementSequenceAsString(1,30): "+ex.getSequence().getReverseComplementSequenceAsString(1,30));
985//                          
986//                            seq = ex.getSequence().getReverseComplementSequenceAsString(1, ex.getLength() - this.getFirstExonStart() + 1);
987//                        } else {
988//                            seq = ex.getSequence().getSequenceAsString(this.getFirstExonStart(), ex.getSequence().getLength());
989//                        }
990                          seq = ex.getSequenceAsString(this.getFirstExonStart(), ex.getLength());
991                } catch (Exception e) {
992                    seq = GapSequence.getGapString( ex.getLength() -  this.getFirstExonStart()+1);
993                }   
994                Mapping m = new Mapping();
995                m.setTarget(this.getTranscript());
996                
997                //case where we are missing one or two 5'bases for the correct phase
998                if (ex.getPhase() == 1) {
999                    seq = "N" + seq;
1000                    //correct the mapping coordinate...by adding the shift
1001                    translation = new Coordinate(1+ex.getPhase(),seq.length()+ex.getPhase());
1002                } else if (ex.getPhase() == 2) {
1003                    seq = "NN" + seq;
1004                    translation = new Coordinate(1+ex.getPhase(),seq.length()+ex.getPhase());
1005                } else {
1006                    translation = new Coordinate(1,seq.length());
1007                }
1008
1009                transcript = new Coordinate(transcriptPosition+this.getFirstExonStart()-1,transcriptPosition+ex.getLength()-1); 
1010                //transcriptPosition =transcriptPosition+ ex.getLength()-1;
1011                m.setSourceCoordinates(translation);
1012                m.setTargetCoordinates(transcript);
1013                translationMappings.add(m);
1014                
1015                continue;
1016            } 
1017//            else if (!intranslated && !ex.getId().equals(this.getFirstExonID())) {
1018//                
1019//                
1020//                continue;
1021//            }
1022            
1023            else if (intranslated && !ex.getId().equals(this.getLastExonID())) {
1024                Integer start = seq.length()+1;
1025                try {
1026//                       if (Strand.REVERSE_STRAND.equals(strand)) {
1027//                            seq = seq.concat(ex.getSequence().getReverseComplementSequenceAsString());
1028//                        } else {
1029//                            seq = seq.concat(ex.getSequence().getSequenceAsString());
1030//                        }
1031                    seq = seq.concat(ex.getSequenceAsString());
1032                } catch (Exception e) {
1033                    seq = seq.concat(GapSequence.getGapString( ex.getLength()));
1034                }   
1035                Mapping m = new Mapping();
1036                m.setTarget(this.getTranscript());
1037             
1038                Coordinate translation = new Coordinate(start,seq.length());
1039                Coordinate transcript = new Coordinate(transcriptPosition,transcriptPosition+ex.getLength()-1); 
1040                m.setSourceCoordinates(translation);
1041                m.setTargetCoordinates(transcript);
1042                //transcriptPosition=transcriptPosition+ex.getLength()-1;
1043                translationMappings.add(m);
1044                continue;
1045            }
1046            else if (intranslated && ex.getId().equals(this.getLastExonID())) {
1047                Integer start = seq.length()+1;
1048                try {
1049//                     if (Strand.REVERSE_STRAND.equals(strand)) {
1050//                            seq = seq.concat(ex.getSequence().getReverseComplementSequenceAsString(ex.getLength()- this.getLastExonEnd() +1 , ex.getLength()));
1051//                        } else {
1052//                            seq = seq.concat(ex.getSequence().getSequenceAsString(1,this.getLastExonEnd()));
1053//                     }
1054                    seq = seq.concat(ex.getSequenceAsString(1,this.getLastExonEnd()));
1055                } catch (Exception e) {
1056                    seq = seq.concat(GapSequence.getGapString( this.getLastExonEnd()));
1057                }   
1058                Mapping m = new Mapping();
1059                m.setTarget(this.getTranscript());
1060              
1061                Coordinate translation = new Coordinate(start,seq.length());
1062                Coordinate transcript = new Coordinate(transcriptPosition,transcriptPosition+this.getLastExonEnd()-1); 
1063                m.setSourceCoordinates(translation);
1064                m.setTargetCoordinates(transcript);
1065                translationMappings.add(m);          
1066                break;
1067            }
1068            else {
1069                //this must be a 5'UTR
1070                //transcriptPosition += ex.getLength()-1;
1071            }
1072        } 
1073        translatedSequence = new DNASequence(seq);
1074    }
1075
1076    /**
1077     * Returns the length of the translated nucleotide sequence, i.e. from first 
1078     * nucleotide of the start codon to end of the stop codon. 
1079     *  
1080     */
1081    public int getLength() {
1082        if (translatedSequence==null) {
1083            this.translate();
1084        }
1085        if (translatedSequence!= null) {
1086            return translatedSequence.getLength();
1087        }
1088        return 0;
1089    }
1090    
1091    /**
1092     * Returns a DNASequence object representing the translation.
1093     *  
1094     */
1095    public DNASequence getTranslatedSequence() {
1096        if (this.translationMappings==null) {
1097            this.translate();
1098        } 
1099        return translatedSequence;
1100    }
1101    
1102    /**
1103     * Returns the Set of Mappings between coordinates on this Translation and 
1104     * the Primary transcript.
1105     *  
1106     */
1107    public MappingSet getTranslationMappings() {
1108        if (this.translationMappings==null) {
1109            this.translate();
1110        } 
1111        return this.translationMappings;
1112    }
1113
1114    /**
1115     * Returns a (BioJava) RNASequence for this Translation using the default (BioJava) 
1116     * TranscriptionEngine or one assigned to this Gene/Organism.
1117     *  
1118     */
1119    public RNASequence getRNASequence() {
1120        
1121        Integer codonTable = 1;
1122
1123        if (this.getTranscript() != null) {
1124
1125            try {
1126               codonTable = this.getTranscript().getTargetSequence().getCodonTableID();
1127            } catch (Exception ex) {
1128            }
1129        }
1130        if (this.getRegistry()!=null) {
1131            return this.getRNASequence(this.getRegistry().getTranscriptionEngine(codonTable));
1132        }
1133        //if no Registry we just use BioJava's fallback default engine
1134        return this.getRNASequence(null);
1135    }
1136    
1137    /**
1138     * Returns a (BioJava) RNASequence for this Translation using the specified (BioJava) 
1139     * TranscriptionEngine or one assigned to this Gene/Organism.
1140     *  
1141     */
1142    public RNASequence getRNASequence(TranscriptionEngine engine) {
1143        if (this.translatedSequence == null) {
1144            this.translate();
1145        }
1146        if (this.translatedSequence == null) {
1147            return null;
1148        }
1149        if (engine!=null) {
1150            return this.translatedSequence.getRNASequence(engine);
1151        }
1152        //Fallback on BioJava's default
1153        return this.translatedSequence.getRNASequence();
1154    }
1155    
1156    public String getRNASequenceAsString() {
1157        RNASequence rnaSequence = this.getRNASequence();
1158        if (rnaSequence!=null) {
1159            return rnaSequence.getSequenceAsString();
1160        } return "";
1161    }
1162    
1163    public String getRNASequenceAsString(TranscriptionEngine engine) {
1164        RNASequence rnaSequence = this.getRNASequence(engine);
1165        if (rnaSequence!=null) {
1166            return rnaSequence.getSequenceAsString();
1167        } return "";
1168    }
1169    
1170    public String getRNASequenceAsString(Integer start, Integer stop) {
1171        if (start==null || start<1 || stop==null || stop<1 || stop>this.getLength()) {
1172            throw new RangeException("The specified position must lie between 1 and the length of the translation. ");
1173        }
1174        RNASequence rnaSequence = this.getRNASequence();
1175        if (rnaSequence!=null) {
1176            return rnaSequence.getSequenceAsString(start,stop,org.biojava3.core.sequence.Strand.POSITIVE);
1177        } return "";       
1178
1179    }
1180    
1181    public String getRNASequenceAsString(TranscriptionEngine engine, Integer start, Integer stop) {
1182        if (start==null || start<1 || stop==null || stop<1 || stop>this.getLength()) {
1183            throw new RangeException("The specified position must lie between 1 and the length of the translation. ");
1184        }        
1185        RNASequence rnaSequence = this.getRNASequence(engine);
1186        if (rnaSequence!=null) {
1187            return rnaSequence.getSequenceAsString(start,stop,org.biojava3.core.sequence.Strand.POSITIVE);
1188        } return "";
1189    }
1190    
1191    /**
1192     * Returns any curated VegaID for the Translation, forcing lazy load if not set, and defaulting 
1193     * to an empty string if absent (e.g. for all the invertebrate species in EnsemblGenomes).
1194     *  
1195     */
1196    @Override
1197    public String getVegaID() {       
1198        if (this.vegaProteinID!=null) {
1199            return this.vegaProteinID;
1200        }
1201        List<DAXRef> outList = new ArrayList<DAXRef>();
1202        
1203        if (this.getDaoFactory()!= null 
1204                && !this.getDaoFactory().getRegistry().getDatasourceType().equals(DBConnection.DataSource.ENSEMBLDB)) {
1205            this.vegaProteinID="";
1206            this.addTypedXRefs(ExternalDBType.VegaProtein, outList);
1207            return this.vegaProteinID;
1208        }
1209                      
1210        //??
1211        this.reinitialize();
1212               
1213        if (this.vegaProteinID==null ) {
1214            
1215            if (this.getDaoFactory()!= null ) {
1216
1217                  for (DAXRef dax : this.getAllXRefs()) {
1218                        if (dax.getPrimaryAccession()!=null
1219                                && dax.getPrimaryAccession().startsWith("OTT")
1220                                && dax.getDB()!= null  
1221                                && (dax.getDB().getDBName().equals(ExternalDBType.VegaProtein.toString())
1222                                    ||
1223                                    dax.getDB().getDBName().contains("Vega_translation") 
1224                                    ||
1225                                    dax.getDB().getDBName().contains("vega_translation") )
1226                                ) {
1227                            outList.add(dax);
1228                            }
1229                  }
1230                
1231                if (outList.isEmpty()) {
1232                    this.vegaProteinID = "";
1233                    this.addTypedXRefs(ExternalDBType.VegaProtein, outList);                    
1234                    return this.vegaProteinID;
1235                } else {
1236                    //add the factory to the xref - although we shouldn't need to use it as it is fully initialized
1237                    for (DAXRef xr: outList) {
1238                        xr.setDaoFactory(this.getDaoFactory());
1239                    }
1240                    this.addTypedXRefs(ExternalDBType.VegaProtein, outList);
1241                }
1242                
1243                 if (outList.size()==1) {
1244                    this.vegaProteinID = outList.get(0).getPrimaryAccession().trim();
1245                    return this.vegaProteinID;
1246                } else {
1247                    //hopefully all the IDs will be the same - but can't guarantee this!
1248                    String pre = "Multiple Vega IDs: {";
1249                    boolean multiple = false;
1250                    String out = null;
1251                    String firstID = "";
1252                    
1253                    for (XRef x: outList) {
1254                        if (out==null) {
1255                            out = x.getPrimaryAccession().trim();
1256                            firstID = x.getPrimaryAccession().trim(); 
1257                        } else if (!firstID.equals(x.getPrimaryAccession().trim())) {                        
1258                            out = out.concat(", ").concat(x.getPrimaryAccession().trim());
1259                            multiple  = true;
1260                        }
1261                    }
1262                    if (!multiple) {
1263                        vegaProteinID=out;
1264                    } else {
1265                        out = out.trim().concat("}");
1266                        vegaProteinID = pre.concat(out);
1267                    }
1268                    return this.vegaProteinID;
1269                }
1270            }
1271        }
1272        return this.vegaProteinID;
1273    }
1274
1275    @Override
1276    public Set<DAXRef> getVegaXRefs() {
1277        this.getVegaID();
1278        return this.getXRefs(ExternalDBType.VegaProtein);
1279    }  
1280    
1281    /**
1282     * Returns preloaded XRefs of given type: Note - does not lazy load these.
1283     * @param type
1284     *  
1285     */    
1286    protected Set<DAXRef> getXRefs(ExternalDBType type) {
1287        return typedXRefs.get(type);
1288    }
1289    
1290    @Override
1291    public Set<DAXRef> getAllXRefs() {
1292        if (this.xrefsInitialized) {
1293            return xrefs;
1294        }
1295
1296        this.reinitialize();
1297
1298        if (this.getDaoFactory() != null && this.getId() != null) {
1299            List<DAXRef> result = null;
1300            try {
1301                result = (List<DAXRef>) this.getDaoFactory().getXRefDAO().getAllXRefs(this);
1302
1303            } catch (DAOException ex) {
1304                LOGGER.info("Threw DAOException on trying to get Vega ID for Translation: " + this.getStableID(), ex);
1305            } finally {
1306                this.xrefsInitialized = true;
1307            }
1308
1309            if (result == null || result.isEmpty()) {
1310                return xrefs;
1311            } else {
1312                
1313                for (DAXRef xr : result) {
1314                    //add the factory to the xref - although we shouldn't need to use it as it is fully initialized
1315                    xr.setDaoFactory(this.getDaoFactory());
1316                    /* shouldn't be necessary now */  
1317                    //check that we are not creating duplicate External DBs...
1318//                    ExternalDB db = xr.getDB();
1319//                    int originalHashCode = db.originalHashCode();
1320//                    db  = this.getDaoFactory().getDatabase().validateExternalDB(db);
1321//                    int checkedHashCode = db.originalHashCode();
1322//                    if (originalHashCode-checkedHashCode !=0) {
1323//                        System.out.println("*** FAILED TO REUSE EXTERNALDB");
1324//                    } else {
1325//                        System.out.println("*** successfully reused externaldb");
1326//                    }
1327//                    xr.setDB(db);
1328                    xrefs.add(xr);
1329                    }
1330                }
1331        }
1332        return xrefs;
1333    }
1334
1335    protected void addTypedXRefs(ExternalDBType type, Collection<? extends XRef> xrefs) {
1336        if ( this.typedXRefs.get(type)==null) {
1337            this.typedXRefs.put(type, new HashSet<DAXRef>());
1338        }
1339        this.typedXRefs.get(type).addAll((Collection<DAXRef>)xrefs);
1340    }
1341
1342    public Integer getTranscriptID() {
1343        return transcriptID;
1344    }
1345
1346    public void setTranscriptID(Integer transcriptID) {
1347        this.transcriptID = transcriptID;
1348    }
1349
1350    public void setVegaProteinID(String vegaProteinID) {
1351        this.vegaProteinID = vegaProteinID;
1352    }    
1353    
1354    @Override
1355    public Integer getId() {
1356        if (id==null || id == 0) {
1357            this.reinitialize();
1358        }
1359        return id;
1360    }
1361    
1362    @Override
1363    public Integer getVersion() {
1364        if (version==null) {
1365            this.reinitialize();
1366        }
1367        return version;
1368    }
1369    
1370    @Override
1371    public Date getModificationDate() {
1372        if (modificationDate==null) {
1373            this.reinitialize();
1374        }
1375        return modificationDate;
1376    }
1377    
1378    @Override
1379    public Date getCreationDate() {
1380        if (creationDate==null) {
1381            this.reinitialize();
1382        }
1383        return creationDate;
1384    }
1385
1386    @Override
1387    public TreeSet<String> getAllSynonyms() {
1388        if (synonyms!=null) {
1389            return synonyms;
1390        }
1391        this.reinitialize();
1392        try {
1393            synonyms = this.getDaoFactory().getXRefDAO().getAllSynonyms(this);
1394        } catch (DAOException ex) {
1395            LOGGER.debug("Failed to getAllSynonyms for DAFeature", ex);
1396        }
1397        if (synonyms==null) {
1398            synonyms = new TreeSet<String>();
1399        }
1400        return synonyms;
1401    }
1402
1403    @Override
1404    public TreeSet<String> getSynonyms(XRef xref) {
1405        return xref.getSynonyms();
1406    }
1407
1408}