separate Amplification and AmplificationResult.java #4541
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / molecular / Sequence.java
index d18d21767a6846db02a4f925d97cca709fb1835e..87eef90d3b23bca80a80b4630f08ca2a104e5052 100644 (file)
@@ -1,41 +1,26 @@
 /**
 * Copyright (C) 2007 EDIT
-* European Distributed Institute of Taxonomy 
+* European Distributed Institute of Taxonomy
 * http://www.e-taxonomy.eu
-* 
+*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * See LICENSE.TXT at the top of this package for the full license terms.
 */
-
 package eu.etaxonomy.cdm.model.molecular;
 
 
-import eu.etaxonomy.cdm.jaxb.DateTimeAdapter;
-import eu.etaxonomy.cdm.model.media.IMediaDocumented;
-import eu.etaxonomy.cdm.model.media.Media;
-import eu.etaxonomy.cdm.model.reference.ReferenceBase;
-import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
-import eu.etaxonomy.cdm.model.common.IReferencedEntity;
-import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
-import eu.etaxonomy.cdm.strategy.cache.common.IdentifiableEntityDefaultCacheStrategy;
-import eu.etaxonomy.cdm.strategy.match.Match;
-import eu.etaxonomy.cdm.strategy.match.MatchMode;
-
-import org.apache.log4j.Logger;
-import org.hibernate.annotations.Cascade;
-import org.hibernate.annotations.CascadeType;
-import org.hibernate.annotations.Index;
-import org.hibernate.annotations.Table;
-import org.hibernate.annotations.Type;
-import org.hibernate.envers.Audited;
-import org.hibernate.search.annotations.Field;
-import org.hibernate.search.annotations.Indexed;
-import org.joda.time.DateTime;
-import org.springframework.beans.factory.annotation.Configurable;
-
-import java.util.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.Set;
 
-import javax.persistence.*;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Transient;
+import javax.validation.constraints.Size;
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
@@ -44,217 +29,590 @@ import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlIDREF;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlTransient;
 import javax.xml.bind.annotation.XmlType;
-import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import org.apache.log4j.Logger;
+import org.codehaus.plexus.util.StringUtils;
+import org.hibernate.annotations.Cascade;
+import org.hibernate.annotations.CascadeType;
+import org.hibernate.envers.Audited;
+import org.hibernate.search.annotations.IndexedEmbedded;
+import org.springframework.beans.factory.annotation.Configurable;
+
+import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
+import eu.etaxonomy.cdm.model.common.DefinedTerm;
+import eu.etaxonomy.cdm.model.common.TermType;
+import eu.etaxonomy.cdm.model.media.Media;
+import eu.etaxonomy.cdm.model.reference.Reference;
 
 /**
+ * Alignment of multiple {@link SingleRead single sequences} to a consensus sequence.
+ * This sequence is a part of (or the complete) DNA sequences of the related {@link DnaSample DNA Sample},
+ * while
+ *
+ * <BR>This class holds information about both the combining process of
+ * {@link SingleRead single sequences} to one consensus sequence
+ * ({@link #getSingleReads() singleReads} , {@link #getContigFile() contigFile} )
+ * as well as sequence related information.
+ * The later includes the {@link #getConsensusSequence() sequence string} itself,
+ * important genetic information about the DNA that has been sequenced
+ * ({@link #getDnaMarker() marker} , {@link #getHaplotype()} haplotype) as well as
+ * registration information ({@link #getGeneticAccessionNumber() genetic accession number} ),
+ * citations, and barcoding information ({@link #getBoldProcessId() BOLD-id},
+ * {@link #getBarcodeSequencePart() barcode sequence}, ...).
+ *
  * @author m.doering
- * @version 1.0
  * @created 08-Nov-2007 13:06:51
+ * @author a.mueller
+ * @updated 11-Jul-2013
  */
 @XmlAccessorType(XmlAccessType.FIELD)
 @XmlType(name = "Sequence", propOrder = {
-    "sequence",
-    "length",
-    "dateSequenced",
-    "barcode",
-    "citationMicroReference",
-    "publishedIn",
-    "locus",
-    "citations",
-    "genBankAccession",
-    "chromatograms"
+    "dnaSample",
+       "consensusSequence",
+       "isBarcode",
+    "barcodeSequencePart",
+    "dnaMarker",
+    "geneticAccessionNumber",
+    "boldProcessId",
+    "haplotype",
+    "contigFile",
+    "singleReadAlignments",
+    "citations"
 })
-@XmlRootElement(name = "Sequence")
+@XmlRootElement(name = "Sequencing")
 @Entity
 @Audited
 @Configurable
-@Table(appliesTo="Sequence", indexes = { @Index(name = "sequenceTitleCacheIndex", columnNames = { "titleCache" }) })
-public class Sequence extends IdentifiableEntity<IIdentifiableEntityCacheStrategy<Sequence>> implements IReferencedEntity, IMediaDocumented{
+//@Table(appliesTo="Sequence", indexes = { @Index(name = "sequenceTitleCacheIndex", columnNames = { "titleCache" }) })
+public class Sequence extends AnnotatableEntity implements Cloneable{
        private static final long serialVersionUID = 8298983152731241775L;
        private static final Logger logger = Logger.getLogger(Sequence.class);
-       
-       //the sequence as a string of base pairs. 5'->3'
-       @XmlElement(name = "Sequence")
-       private String sequence;
-       
-       //should be calculated in case sequence is set
-       @XmlElement(name = "Length")
-       private Integer length;
-       
-       //should be calculated in case sequence is set
-       @XmlElement (name = "DateSequenced", type= String.class)
-       @XmlJavaTypeAdapter(DateTimeAdapter.class)
-       @Type(type="dateTimeUserType")
-       @Basic(fetch = FetchType.LAZY)
-       private DateTime dateSequenced;
-       
-       //should be calculated in case sequence is set
-       @XmlAttribute(name = "isBarcode")
-       private boolean barcode;
-       
-       //the sequence as a string of base pairs. 5'->3'
-       @XmlElement(name = "CitationMicroReference")
-       private String citationMicroReference;
-       
-       @XmlElement(name = "PublishedIn")
+
+       //TODO move to cdmlib-ext?
+       private static final String GENBANK_BASE_URI = "http://www.ncbi.nlm.nih.gov/nuccore/%s";
+       private static final String EMBL_BASE_URI = "http://www.ebi.ac.uk/ena/data/view/%s";
+       private static final String DDBJ_BASE_URI = "http://getentry.ddbj.nig.ac.jp/getentry/na/%s/?filetype=html";
+       private static final String BOLD_BASE_URI = "http://www.boldsystems.org/index.php/Public_RecordView?processid=%s";
+
+    @XmlElement( name = "DnaSample")
     @XmlIDREF
     @XmlSchemaType(name = "IDREF")
     @ManyToOne(fetch = FetchType.LAZY)
-    @Cascade(CascadeType.SAVE_UPDATE)
-       private ReferenceBase publishedIn;
-       
-       @XmlElementWrapper(name = "Citations")
-       @XmlElement(name = "Citation")
+    @IndexedEmbedded
+    private DnaSample dnaSample;
+
+
+       /** @see #getContigFile() */
+       @XmlElement(name = "ContigFile")
     @XmlIDREF
     @XmlSchemaType(name = "IDREF")
-    @OneToMany(fetch = FetchType.LAZY)
-       private Set<ReferenceBase> citations = new HashSet<ReferenceBase>();
-       
-       @XmlElementWrapper(name = "GenBankAccessions")
-       @XmlElement(name = "GenBankAccession")
-    @OneToMany(fetch = FetchType.LAZY)
-       private Set<GenBankAccession> genBankAccession = new HashSet<GenBankAccession>();
-       
-       @XmlElement(name = "Locus")
+    @ManyToOne(fetch = FetchType.LAZY)
+       @Cascade({CascadeType.SAVE_UPDATE})
+       private Media contigFile;
+
+       /** @see #getConsensusSequence() */
+       @XmlElement(name = "ConsensusSequence")
+    private SequenceString consensusSequence = SequenceString.NewInstance();
+
+       @XmlAttribute(name = "isBarcode")
+       private Boolean isBarcode = null;
+
+       /** @see #getBarcodeSequence()*/
+       @XmlElement(name = "BarcodeSequencePart")
+    private SequenceString barcodeSequencePart = SequenceString.NewInstance();
+
+       /** @see #getGeneticAccessionNumber()*/
+       @XmlElement(name = "GeneticAccessionNumber")
+       @Size(max=20)
+       private String geneticAccessionNumber;
+
+       /** @see #getBoldProcessId() */
+       @XmlElement(name = "BoldProcessId")
+       @Size(max=20)
+       private String boldProcessId;
+
+    @XmlElementWrapper(name = "SingleReadAlignments")
+    @XmlElement(name = "SingleReadAlignment")
+    @XmlIDREF
+    @XmlSchemaType(name = "IDREF")
+    @OneToMany(mappedBy="consensusAlignment", fetch = FetchType.LAZY)
+    @Cascade({CascadeType.SAVE_UPDATE})
+       private Set<SingleReadAlignment> singleReadAlignments = new HashSet<SingleReadAlignment>();
+
+       /** @see #getDnaMarker() */
+       @XmlElement(name = "DnaMarker")
     @XmlIDREF
     @XmlSchemaType(name = "IDREF")
     @ManyToOne(fetch = FetchType.LAZY)
-    @Cascade(CascadeType.SAVE_UPDATE)
-       private Locus locus;
-       
-       @XmlElementWrapper(name = "Chromatograms")
-       @XmlElement(name = "Chromatogram")
+       //no cascade as it is a defined term
+       private DefinedTerm dnaMarker;
+
+
+       /** @see #getHaplotype() */
+       @XmlElement(name = "Haplotype")
+       @Size(max=100)
+       private String haplotype;
+
+       /** @see #getCitations() */
+       @XmlElementWrapper(name = "Citations")
+    @XmlElement(name = "Citation")
     @XmlIDREF
     @XmlSchemaType(name = "IDREF")
-    @OneToMany(fetch = FetchType.LAZY)
-       private Set<Media> chromatograms = new HashSet<Media>();
-       
-       protected Sequence() {
-               super(); // FIXME I think this is explicit - do we really need to call this?
-               this.cacheStrategy = new IdentifiableEntityDefaultCacheStrategy<Sequence>();
+    @ManyToMany(fetch = FetchType.LAZY)
+    @Cascade({CascadeType.SAVE_UPDATE})
+       private Set<Reference> citations = new HashSet<Reference>();
+
+//     //should be calculated in case sequence is set
+//     @XmlElement (name = "DateSequenced", type= String.class)
+//     @XmlJavaTypeAdapter(DateTimeAdapter.class)
+//     @Type(type="dateTimeUserType")
+//     @Basic(fetch = FetchType.LAZY)
+//     private DateTime dateSequenced;
+
+
+//*********************** FACTORY ****************************************************/
+
+       public static Sequence NewInstance(String consensusSequence){
+               Sequence result = new Sequence();
+               result.setSequenceString(consensusSequence);
+               return result;
        }
-       
-       public Locus getLocus(){
-               logger.debug("getLocus");
-               return this.locus;
+
+
+       public static Sequence NewInstance(String consensusSequence, Integer length){
+               Sequence result = NewInstance(consensusSequence);
+               result.getConsensusSequence().setLength(length);
+               return result;
        }
 
-       public void setLocus(Locus locus){
-               this.locus = locus;
+       public static Sequence NewInstance(DnaSample dnaSample, String consensusSequence, Integer length){
+               Sequence result = NewInstance(consensusSequence);
+               result.getConsensusSequence().setLength(length);
+               dnaSample.addSequence(result);
+
+               return result;
        }
+//*********************** CONSTRUCTOR ****************************************************/
+
+       protected Sequence() {}
 
-       public ReferenceBase getPublishedIn(){
-               return this.publishedIn;
+//*********************** GETTER / SETTER ****************************************************/
+
+
+       /**
+        * The {@link DnaSample dna sample} this sequencing belongs too.
+        */
+       public DnaSample getDnaSample() {
+               return dnaSample;
        }
-       
-       public void setPublishedIn(ReferenceBase publishedIn){
-               this.publishedIn = publishedIn;
+
+       /**
+        * To be called only from {@link DnaSample#addSequence(Sequence)}
+        * @see #getDnaSample()
+        */
+       //TODO implement full bidirectionality
+       protected void setDnaSample(DnaSample dnaSample) {
+               this.dnaSample = dnaSample;
+               if (dnaSample != null && !dnaSample.getSequences().contains(this)){
+                       throw new RuntimeException("Don't use DNA setter");
+               }
+       }
+
+       /**
+        * The resulting consensus sequence represened by this {@link Sequence sequence} .
+        * The consensus is usually computed from the {@link SingleRead single reads}.
+        * The result of which is stored in a file called {@link #getContigFile() contig file}
+        *
+        * #see {@link #getContigFile()}
+        * #see {@link #getSingleReads()}
+        */
+       public SequenceString getConsensusSequence() {
+               return consensusSequence;
+       }
+
+
+       /**
+        * @see #getConsensusSequence()
+        */
+       public void setConsensusSequence(SequenceString sequenceString) {
+               if (sequenceString == null){
+                       sequenceString = SequenceString.NewInstance();
+               }
+               this.consensusSequence = sequenceString;
+       }
+
+       /**
+        * The isBarcode flag should be set to true if this (consensus) sequence is or includes
+        * a barcoding sequence. If the barcoding sequence is only a part of the consensus sequence
+        * this part shall be stored as {@link #getBarcodeSequencePart() barcoding sequence part}.
+        * A isBarcode value of <code>null</code> indicates that we do have no knowledge
+        * whether the sequence is a barcoding sequence or not.
+        *
+        * @see #getBarcodeSequencePart()
+        * @see #getSequenceString()
+        * @returns the isBarcode flag value (tri-state)
+        *
+        */
+       public Boolean getIsBarcode() {
+               return isBarcode;
+       }
+
+       /**
+        * @see #getIsBarcode()
+        * @see #getBarcodeSequencePart()
+        */
+       public void setIsBarcode(Boolean isBarcode) {
+               this.isBarcode = isBarcode;
+       }
+
+       /**
+        * If the barcode sequence string does not include 100% of the (consensus) sequence
+        * the part used as barcode is provided here. However, the barcode part
+        * should be kept empty if consensus sequence string and barcode sequence string are equal.
+        *
+        * @see #getIsBarcode()
+        */
+       public SequenceString getBarcodeSequencePart() {
+               return barcodeSequencePart;
+       }
+
+       /**
+        * @see #getBarcodeSequencePart()
+        */
+       public void setBarcodeSequencePart(SequenceString barcodeSequencePart) {
+               if (barcodeSequencePart == null){
+                       barcodeSequencePart = SequenceString.NewInstance();
+               }
+               this.barcodeSequencePart = barcodeSequencePart;
        }
 
-       public Set<ReferenceBase> getCitations() {
+       /**
+        * Sets the {@link TermType#DnaMarker DNA marker} examined and described by this sequencing.
+        * The marker should usually be similar to the one used in the according {@link Amplification
+        * amplification process}. However, it may slightly differ, or, if multiple amplifications where
+        * used to build this consensus sequence it may be the super set of the markers used in amplification.
+        *
+        * @return
+        */
+       public DefinedTerm getDnaMarker(){
+               return this.dnaMarker;
+       }
+
+       /**
+        * @see #getDnaMarker()
+        * @param marker
+        */
+       public void setDnaMarker(DefinedTerm dnaMarker){
+               this.dnaMarker = dnaMarker;
+       }
+
+       /**
+        * The accession number used in GenBank, EMBL and DDBJ.
+        * @return
+        */
+       public String getGeneticAccessionNumber() {
+               return geneticAccessionNumber;
+       }
+
+       /**
+        * Sets the genetic accession number.
+        * @see #getGeneticAccessionNumber()
+        */
+       public void setGeneticAccessionNumber(String geneticAccessionNumber) {
+               this.geneticAccessionNumber = geneticAccessionNumber;
+       }
+
+
+       /**
+        * The identifier used by the Barcode of Life Data Systems (BOLD, http://www.boldsystems.org/).
+        */
+       public String getBoldProcessId() {
+               return boldProcessId;
+       }
+
+       public void setBoldProcessId(String boldProcessId) {
+               this.boldProcessId = boldProcessId;
+       }
+
+       /**
+        * Returns the name of the haplotype.
+        * A haplotype (haploide genotype) is a variant of nucleotide sequences on the same chromosome.
+        * A certain haplotype may be specific for an individual, a population or a species.
+        * @return
+        */
+       public String getHaplotype() {
+               return haplotype;
+       }
+
+       /**
+        * @see #getHaplotype()
+        */
+       public void setHaplotype(String haplotype) {
+               this.haplotype = haplotype;
+       }
+
+       /**
+        * The contigFile containing all data and data processing for this sequencing.
+        *
+        * @see #getConsensusSequence()
+        * @see #getSingleReads()
+        */
+       public Media getContigFile() {
+               return contigFile;
+       }
+
+       /**
+        * @see #getContigFile()
+        */
+       public void setContigFile(Media contigFile) {
+               this.contigFile = contigFile;
+       }
+
+
+       /**
+        * Citations are the set of references in which this sequence was published.
+        * Unlike taxonomic names the first publication of a sequence
+        * is not so important (maybe because it is required by publishers
+        * that they are all registered at Genbank) therefore we do not have something like an
+        * "original reference" attribute.<BR>
+        * Links to these references are to be stored within the reference itself.
+        * @return the set of references in which this sequence was published.
+        */
+       public Set<Reference> getCitations() {
                return citations;
        }
-       protected void setCitations(Set<ReferenceBase> citations) {
+       /**
+        * @see #getCitations()
+        */
+       protected void setCitations(Set<Reference> citations) {
                this.citations = citations;
        }
-       public void addCitation(ReferenceBase citation) {
+       /**
+        * @see #getCitations()
+        */
+       public void addCitation(Reference citation) {
                this.citations.add(citation);
        }
-       public void removeCitation(ReferenceBase citation) {
+       /**
+        * @see #getCitations()
+        */
+       public void removeCitation(Reference citation) {
                this.citations.remove(citation);
        }
 
-       public Set<GenBankAccession> getGenBankAccession() {
-               return genBankAccession;
+       /**
+        * The {@link SingleRead single reads} that were used to build this consensus sequence.
+        *
+        * @see #getConsensusSequence()
+        * @see #getContigFile()
+        */
+       public Set<SingleReadAlignment> getSingleReadAlignments() {
+               return singleReadAlignments;
        }
-
-       public void addGenBankAccession(GenBankAccession genBankAccession) {
-               this.genBankAccession.add(genBankAccession);
+       /**
+        * @see #getSingleReads()
+        */
+       public void addSingleReadAlignment(SingleReadAlignment singleReadAlignment) {
+               this.singleReadAlignments.add(singleReadAlignment);
+               if (! this.equals(singleReadAlignment.getConsensusSequence())){
+                       singleReadAlignment.setConsensusAlignment(this);
+               };
        }
-       
-       public void removeGenBankAccession(GenBankAccession genBankAccession) {
-               this.genBankAccession.remove(genBankAccession);
+       /**
+        * @see #getSingleReads()
+        */
+       public void removeSingleReadAlignment(SingleReadAlignment singleReadAlignment) {
+               this.singleReadAlignments.remove(singleReadAlignment);
+               if (this.equals(singleReadAlignment.getConsensusSequence())){
+                       singleReadAlignment.setConsensusAlignment(null);
+               }
        }
+//     /**
+//      * @see #getSingleReads()
+//      */
+//     //TODO private as long it is unclear how bidirectionality is handled
+//     @SuppressWarnings("unused")
+//     private void setSingleReadAlignments(Set<SingleReadAlignment> singleReadAlignments) {
+//             this.singleReadAlignments = singleReadAlignments;
+//     }
        
-       public Set<Media> getChromatograms() {
-               return chromatograms;
-       }
-
-       public void addChromatogram(Media chromatogram) {
-               this.chromatograms.add(chromatogram);
+// *********************** CONVENIENCE ***********************************/
+       
+       /**
+        * Convenience method to add a single read to a consensus sequence
+        * by creating a {@link SingleReadAlignment}.
+        * @param singleRead the {@link SingleRead} to add
+        * @return the created SingleReadAlignment
+        */
+       public SingleReadAlignment addSingleRead(SingleRead singleRead) {
+               SingleReadAlignment alignment = SingleReadAlignment.NewInstance(this, singleRead);
+               return alignment;
        }
        
-       public void removeChromatogram(Media chromatogram) {
-               this.chromatograms.remove(chromatogram);
+       public void removeSingleRead(SingleRead singleRead) {
+               Set<SingleReadAlignment> toRemove = new HashSet<SingleReadAlignment>();
+               for (SingleReadAlignment align : this.singleReadAlignments){
+                       if (align.getSingleRead() != null && align.getSingleRead().equals(singleRead)){
+                               toRemove.add(align);
+                       }
+               }
+               for (SingleReadAlignment align : toRemove){
+                       removeSingleReadAlignment(align);
+               }
+               return;
        }
        
+       /**
+        * Convenience method that returns all single reads this consensus sequence
+        * is based on via {@link SingleReadAlignment}s.
+        * @return set of related single reads
+        */
+       @XmlTransient
        @Transient
-       public Set<Media> getMedia() {
-               return getChromatograms();
+       public Set<SingleRead> getSingleReads(){
+               Set<SingleRead> singleReads = new HashSet<SingleRead>();
+               for (SingleReadAlignment align : this.singleReadAlignments){
+                       if (align.getSingleRead() != null){  // == null should not happen
+                               singleReads.add(align.getSingleRead());
+                       }
+               }
+               return singleReads;
        }
 
-       public String getSequence(){
-               return this.sequence;
-       }
+
+       //*************************** Transient GETTER /SETTER *****************************/
 
        /**
-        * 
-        * @param sequence    sequence
+        * Delegate method to get the text representation of the consensus sequence
+        * @see #setSequenceString(String)
         */
-       public void setSequence(String sequence){
-               this.sequence = sequence;
+       @Transient
+       public String getSequenceString() {
+               return consensusSequence.getString();
        }
 
-       public Integer getLength(){
-               return this.length;
+       /**
+        * Delegate method to set the text representation of the {@link #getConsensusSequence()
+        * consensus sequence}.
+        */
+       @Transient
+       public void setSequenceString(String sequence) {
+               consensusSequence.setString(sequence);
        }
 
        /**
-        * 
-        * @param length    length
+        * Convenience method which computes the set of all related pherograms
+        * @return the set of pherograms.
         */
-       public void setLength(Integer length){
-               this.length = length;
+       @Transient
+       public Set<Media> getPherograms(){
+               Set<Media> result = new HashSet<Media>();
+               for (SingleReadAlignment singleReadAlign : singleReadAlignments){
+                       if (singleReadAlign.getSingleRead() != null &&  singleReadAlign.getSingleRead().getPherogram() != null){
+                               result.add(singleReadAlign.getSingleRead().getPherogram());
+                       }
+               }
+               return result;
        }
 
-       public DateTime getDateSequenced(){
-               return this.dateSequenced;
+
+       //***** Registrations ************/
+       /**
+        * Returns the computed genBank uri.
+        * @return the uri composed of {@link #GENBANK_BASE_URI} and {@link #geneticAccessionNumber}
+        * @throws URISyntaxException when URI could not be created with {@link #geneticAccessionNumber}
+        */
+       @Transient
+       public URI getGenBankUri() throws URISyntaxException {
+               return createExternalUri(GENBANK_BASE_URI, geneticAccessionNumber);
        }
 
        /**
-        * 
-        * @param dateSequenced    dateSequenced
+        * Returns the computed EMBL uri.
+        * @return the uri composed of {@link #EMBL_BASE_URI} and {@link #geneticAccessionNumber}
+        * @throws URISyntaxException when URI could not be created with {@link #geneticAccessionNumber}
         */
-       public void setDateSequenced(DateTime dateSequenced){
-               this.dateSequenced = dateSequenced;
+       @Transient
+       public URI getEmblUri() throws URISyntaxException {
+               return createExternalUri(EMBL_BASE_URI, geneticAccessionNumber);
        }
 
-       public boolean isBarcode(){
-               return this.barcode;
+       /**
+        * Returns the computed DDBJ uri.
+        * @return the uri composed of {@link #DDBJ_BASE_URI} and {@link #geneticAccessionNumber}
+        * @throws URISyntaxException when URI could not be created with {@link #geneticAccessionNumber}
+        */
+       @Transient
+       public URI getDdbjUri() throws URISyntaxException {
+               return createExternalUri(DDBJ_BASE_URI, geneticAccessionNumber);
        }
 
        /**
-        * 
-        * @param isBarcode    isBarcode
+        * Returns the URI for the BOLD entry.
+        * @return the uri composed of {@link #BOLD_BASE_URI} and {@link #boldProcessId}
+        * @throws URISyntaxException when URI could not be created with {@link #boldProcessId}
+        * @see #getBoldProcessId()
         */
-       public void setBarcode(boolean barcode){
-               this.barcode = barcode;
+       @Transient
+       public URI getBoldUri() throws URISyntaxException {
+               return createExternalUri(BOLD_BASE_URI, boldProcessId);
        }
 
-       public String getCitationMicroReference(){
-               return this.citationMicroReference;
+       private URI createExternalUri(String baseUri, String id) throws URISyntaxException{
+               if (StringUtils.isNotBlank(id)){
+                       return new URI(String.format(baseUri, id.trim()));
+               }else{
+                       return null;
+               }
        }
 
+
+
+
+       //*********************** CLONE ********************************************************/
        /**
-        * 
-        * @param citationMicroReference    citationMicroReference
+        * Clones <i>this</i> sequence. This is a shortcut that enables to create
+        * a new instance that differs only slightly from <i>this</i> sequencing by
+        * modifying only some of the attributes.<BR><BR>
+        *
+        *
+        * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
+        * @see java.lang.Object#clone()
         */
-       public void setCitationMicroReference(String citationMicroReference){
-               this.citationMicroReference = citationMicroReference;
-       }
+       @Override
+       public Object clone()  {
+               try{
+               Sequence result = (Sequence)super.clone();
+
+               //sequences
+               result.consensusSequence = (SequenceString)this.consensusSequence.clone();
+               result.barcodeSequencePart = (SequenceString)this.barcodeSequencePart.clone();
+
+
+               //single sequences
+               result.singleReadAlignments = new HashSet<SingleReadAlignment>();
+               for (SingleReadAlignment singleReadAlign: this.singleReadAlignments){
+                       SingleReadAlignment newAlignment = (SingleReadAlignment)singleReadAlign.clone();
+                       result.singleReadAlignments.add(newAlignment);
+               }
 
-       public ReferenceBase getCitation(){
-               return publishedIn;
+               //citations  //TODO do we really want to copy these ??
+               result.citations = new HashSet<Reference>();
+               for (Reference<?> ref: this.citations){
+                       result.citations.add(ref);
+               }
+
+
+
+               return result;
+               }catch (CloneNotSupportedException e) {
+                       logger.warn("Object does not implement cloneable");
+                       e.printStackTrace();
+                       return null;
+               }
        }
+
+
 }
\ No newline at end of file