001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021
022package org.biojava.bio.seq.impl;
023
024import java.util.Iterator;
025
026import org.biojava.bio.Annotation;
027import org.biojava.bio.BioError;
028import org.biojava.bio.BioException;
029import org.biojava.bio.seq.DNATools;
030import org.biojava.bio.seq.Feature;
031import org.biojava.bio.seq.FeatureFilter;
032import org.biojava.bio.seq.FeatureHolder;
033import org.biojava.bio.seq.Sequence;
034import org.biojava.bio.seq.SequenceTools;
035import org.biojava.bio.seq.projection.ProjectedFeatureHolder;
036import org.biojava.bio.seq.projection.Projection;
037import org.biojava.bio.seq.projection.TranslateFlipContext;
038import org.biojava.bio.symbol.Edit;
039import org.biojava.bio.symbol.IllegalAlphabetException;
040import org.biojava.bio.symbol.SimpleSymbolList;
041import org.biojava.bio.symbol.SymbolList;
042import org.biojava.utils.ChangeVetoException;
043
044/**
045 * A reverse complement view onto <code>Sequence</code> interface.
046 * <p>
047 * All features of the underlying sequence are reflected onto the RevCompSequence using a ProjectedFeatureHolder</p>
048 * calling createFeature() on a RevCompSequence creates a feature on the underlying
049 * sequence. Non-Stranded features will return the reverse compemented view of the sequence
050 * when getSymbols() is called that is to say if you get what you expect as if your RevCompSequence
051 * was a regular Sequence.
052 *
053 * @author David Waring
054 * @author Thomas Down
055 */
056public class RevCompSequence
057    extends SimpleSequence
058{
059    private ProjectedFeatureHolder pfh;
060    protected Sequence origSeq;
061    
062    
063    /**
064    *  URN, Name and Annotation are copied as is from the original Sequence, unless you use the
065    *  the other contructor that sets these.
066    */
067    
068    public RevCompSequence(Sequence seq)
069        throws IllegalAlphabetException
070    {
071        this(seq,seq.getURN(),seq.getName(),seq.getAnnotation());
072    }
073    
074    
075    public RevCompSequence(Sequence seq, String urn, String name, Annotation annotation)throws IllegalAlphabetException {
076        super(DNATools.reverseComplement(seq),urn,name,annotation);
077        pfh = new ProjectedFeatureHolder(new TranslateFlipContext(this,seq,seq.length()+1,true));
078        origSeq = seq;
079    }
080    
081
082    // SymbolList stuff
083    /**
084    * edit() will try to edit the underlying Sequence. So if it is editable this will be too
085    * <p>Since I have not seen and editable Sequence I have not tested this </p>
086    *
087    */
088    public void edit(Edit e)throws ChangeVetoException,IndexOutOfBoundsException{
089        int pos = (this.length() - (e.pos + e.length)) + 2;
090        Edit newE = null;
091        try {
092            newE = new Edit (pos,e.length,DNATools.reverseComplement(e.replacement));
093            origSeq.edit(newE);
094        }catch (IllegalAlphabetException iae){
095            throw new BioError("Error while editing RevCompSequence " + iae.getMessage());
096        }
097        
098    }
099    
100    // Sequence stuff
101    public Iterator features(){
102        return pfh.features();
103    }
104    
105    public int countFeatures(){
106        return pfh.countFeatures();
107    }
108    
109    public FeatureHolder filter(FeatureFilter ff) {
110        return pfh.filter(ff);
111    }
112    
113    public FeatureHolder filter(FeatureFilter ff, boolean recurse) {
114        return pfh.filter(ff, recurse);
115    }
116    
117    /**
118    * containsFeature() will return true if this seq contains the feature in question, or
119    * if if the original (non reverse complement) sequence contains the feature;
120    */ 
121    
122    public boolean containsFeature(Feature f) {
123        return pfh.containsFeature(f) || origSeq.containsFeature(f);
124    }
125    
126    public void removeFeature(Feature f)
127            throws ChangeVetoException, BioException {
128        pfh.removeFeature(f);
129    }
130
131    /**
132    * createFeature() will call createFeature() on the underlying Sequence.
133    * returns the feature as it will be projected onto the reverse complement sequence 
134    * not the actual feature that was created.
135    *
136    */
137    public Feature createFeature(Feature.Template ft) throws ChangeVetoException,BioException{
138         return pfh.getContext().createFeature(ft);
139    }
140    
141    /**
142    * getFeatureFromOriginal() Since you can not create a feature on a projectedFeature at this time, I am 
143    * including this method so that you can get the corresponding feature from the original sequence.
144    * (which is not projected) and do something with that such as createFeature().
145    */
146    
147    public Feature getFeatureFromOriginal(Feature f){
148        return ((Projection) f).getViewedFeature();
149    }
150    
151    /**
152    * clone() should make a complete copy of the Sequence with  all features (and children) and return
153    * a SimpleSequence that is unconnected from the original sequence.
154    */
155    
156    public Object clone(){
157        SymbolList sl = new SimpleSymbolList(this);
158        Sequence newSeq = new SimpleSequence(sl,this.getURN(),this.getName(),this.getAnnotation());
159        try{
160            SequenceTools.addAllFeatures(newSeq, this);
161        } catch ( BioException e){
162            throw new BioError( "Error while cloning RevCompSequenece: " + e.getMessage());
163        } catch (ChangeVetoException cve) {
164            throw new BioError("Couldn't modify newly created SimpleSequence", cve);
165        }
166            
167        return newSeq;
168        
169    }     
170}