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;
023
024import org.biojava.bio.BioError;
025import org.biojava.bio.BioException;
026import org.biojava.bio.seq.impl.ViewSequence;
027import org.biojava.bio.symbol.CircularLocation;
028import org.biojava.bio.symbol.Edit;
029import org.biojava.bio.symbol.Location;
030import org.biojava.bio.symbol.SimpleSymbolList;
031import org.biojava.bio.symbol.Symbol;
032import org.biojava.bio.symbol.SymbolList;
033import org.biojava.utils.ChangeVetoException;
034
035/**
036 * <p>
037 * A circular view onto another Sequence object.  The class allows for
038 * reinterpretation of locations and indices onto the sequence to allow for
039 * overlapping of the origin. The origin is assumed to be the first symbol.
040 * Future versions may support changing the origin.
041 * </p>
042 * In biojavax {@link org.biojavax.bio.seq.RichSequence RichSequences} intrinsicly know about
043 * circularity. No view is required. We strongly recommend using RichSequence
044 * if possible.
045 *
046 *
047 * @author Mark Schreiber
048 * @version 1.2
049 * @since 1.1
050 * @see org.biojavax.bio.seq.RichSequence 
051 */
052
053public class CircularView extends ViewSequence{
054  public CircularView(Sequence seq, FeatureRealizer fr){
055    super(seq, fr);
056  }
057
058  public CircularView(Sequence seq){
059    super(seq);
060  }
061
062  private int realValue(int val){
063    val = (val % length());
064    if(val < 1) val = length() + val;
065    return val;
066  }
067
068  /**
069   * <p>
070   * Over rides ViewSequence. Allows any integer index, positive or negative
071   * to return a symbol via the equation
072   * <CODE>val = (val % length);</CODE>
073   * Note that base zero is the base immediately before base 1 which is of course
074   * the last base of the sequence.
075   * </p>
076   * @param index the index of the <code>Symbol </code>requested.
077   * @return the <code>Symbol </code>specified by the <code>index.</code>
078   */
079  public Symbol symbolAt(int index){
080    return super.symbolAt(realValue(index));
081  }
082
083  /**
084   * <p>
085   * Over rides ViewSequence. Allows any integer index, positive or negative
086   * to return a symbol via the equation
087   * <CODE>val = (val % length);</CODE>
088   * </p>
089   *
090   * <p>
091   * Will return a linear String which can, if nescessary, span the origin.
092   * </p>
093   * @param start the index of the fist base
094   * @param end the index of the last base
095   * @return a <code>String </code>representation of the tokenized <code>Symbol</code>s
096   */
097  public String subStr(int start, int end){
098
099    start = realValue(start);
100    end = realValue(end);
101
102    if(start <= end){
103      return super.subStr(start, end);
104    }
105    else{
106      String toEnd = super.subStr(start,super.length());
107      String fromStart = super.subStr(1,end);
108      return toEnd + fromStart;
109    }
110  }
111
112  /**
113   * Over rides ViewSequence to allow the use of locations that have
114   * coordinates outside of the sequence length (which are needed to
115   * describe locations that overlap the origin of a circular sequence).
116   *
117   * @since 1.2
118   * @throws BioException if a non circular location is added that exceeds the
119   * 'boundaries' of the sequence.
120   * @throws ChangeVetoException if the sequence is locked.
121   * @param template the template of the feature to be created.
122   * @return the feature created you can use the template of the returned feature
123   * to create another of the same type.
124   */
125  public Feature createFeature(Feature.Template template)
126        throws ChangeVetoException, BioException
127    {
128      Location loc = template.location;
129      if(loc.getMax() > length() && (loc instanceof CircularLocation == false)){
130        throw new BioException("Only CircularLocations may exceed sequence length");
131      }
132      Feature f = realizeFeature(this, template);
133      ((SimpleFeatureHolder)getAddedFeatures()).addFeature(f);
134      return f;
135    }
136
137  /**
138   * <p>
139   * Over rides ViewSequence. Allows any integer index, positive or negative
140   * to return a symbol via the equation
141   * <CODE>index = ((index -1) % length)+1</CODE>
142   * </p>
143   *
144   * <p>
145   * Will return a linear SymbolList which can ,if nescessary, span the origin.
146   * </p>
147   * @param start the first base of the sublist
148   * @param end the last base of the sublist
149   * @return a <code>SymbolList </code>containing the <code>Symbols</code> from
150   * <code>start</code> to <code>end</code> inclusive
151   */
152  public SymbolList subList(int start, int end){
153
154    start = realValue(start);
155    end = realValue(end);
156
157    if(start <= end){
158      return super.subList(start, end);
159    }
160    else{
161      SimpleSymbolList fromStart = new SimpleSymbolList(super.subList(1,end));
162      SimpleSymbolList toEnd = new SimpleSymbolList(super.subList(start,length()));
163      Edit edit = new Edit(toEnd.length() +1, 0, fromStart);
164      try{
165        toEnd.edit(edit);
166      }catch(Exception e){
167        throw new BioError("Couldn't construct subList, this shouldn't happen",e);
168      }
169      return toEnd;
170    }
171  }
172}
173