001 package org.biojava3.core.sequence.views;
002
003 import java.util.Iterator;
004 import java.util.List;
005
006 import org.biojava3.core.sequence.template.Compound;
007 import org.biojava3.core.sequence.template.Sequence;
008 import org.biojava3.core.sequence.template.SequenceView;
009
010 /**
011 * A sliding window view of a sequence which does not implement any
012 * interfaces like {@link Sequence} because they do not fit how this works.
013 * For each index requested we return a SequenceView or List of compounds back.
014 *
015 * If you perform a view on a Sequence whose length is not a multiple of the
016 * window the final window will be omitted i.e. if we have the sequence AGCGG
017 * and a window of 3 then you will only see AGC since GG exceeds the calculated
018 * length of this sequence.
019 *
020 * Because this does not implement a Sequence interface we do not recommend
021 * passing this class around. If you need to represent a windowed sequence
022 * as a real Sequence then translate it into a new Compound
023 *
024 * @author ayates
025 *
026 * @param <C> The type of compound we return from a window
027 */
028 public class WindowedSequence<C extends Compound> implements Iterable<SequenceView<C>> {
029
030 private final Sequence<C> sequence;
031 private final int windowSize;
032
033 public WindowedSequence(Sequence<C> sequence, int windowSize) {
034 this.sequence = sequence;
035 this.windowSize = windowSize;
036 }
037
038 /**
039 * Access the current window size
040 */
041 public int getWindowSize() {
042 return windowSize;
043 }
044
045 /**
046 * Access the sequence which backs this window
047 */
048 public Sequence<C> getBackingSequence() {
049 return sequence;
050 }
051
052 /**
053 * Calculates start index according to the equation start = ( (index-1) -
054 * windowSize) +1
055 */
056 protected int toStartIndex(int index) {
057 return ((index - 1) * getWindowSize()) + 1;
058 }
059
060 /**
061 * Returns the size of the windowed sequence which is the length by the
062 * window size. Trailing Compounds are omitted.
063 */
064 public int getLength() {
065 return getBackingSequence().getLength() / getWindowSize();
066 }
067
068 /**
069 * For a given position into the windowed view this will return those
070 * compounds we can see in the window. i.e. in the sequence AGGCCT requesting
071 * index 1 returns AGG and requesting index 2 return CCT.
072 *
073 * @param index Windowed index position
074 * @return The List of compounds
075 */
076 public List<C> getCompounds(int index) {
077 return get(index).getAsList();
078 }
079
080 /**
081 * Returns the window specified at the given index in offsets i.e. asking
082 * for position 2 in a moving window sequence of size 3 will get you
083 * the window starting at position 4.
084 */
085 public SequenceView<C> get(int index) {
086 int start = toStartIndex(index);
087 int end = index + (getWindowSize() - 1);
088 return getBackingSequence().getSubSequence(start, end);
089 }
090
091 /**
092 * Returns an iterator which will return the windows in a sequence in
093 * sequential order.
094 */
095 @Override
096 public Iterator<SequenceView<C>> iterator() {
097 return new WindowedSequenceIterator<C>(this);
098 }
099
100 /**
101 * Iterator of all List of compounds available in a windowed sequence.
102 */
103 private static class WindowedSequenceIterator<C extends Compound> implements Iterator<SequenceView<C>> {
104
105 private final int end;
106 private final int window;
107 private final int offset;
108 private int currentIndex = 1;
109 private final Sequence<C> seq;
110
111 public WindowedSequenceIterator(WindowedSequence<C> sequence) {
112 this.window = sequence.getWindowSize();
113 this.offset = window - 1;
114 this.seq = sequence.getBackingSequence();
115 this.end = seq.getLength();
116 }
117
118 @Override
119 public boolean hasNext() {
120 return (currentIndex+offset) <= end;
121 }
122
123 @Override
124 public SequenceView<C> next() {
125 SequenceView<C> v = seq.getSubSequence(currentIndex, currentIndex + offset);
126 currentIndex = currentIndex + window;
127 return v;
128 }
129
130 @Override
131 public void remove() {
132 throw new UnsupportedOperationException("Cannot remove from a Windowed view");
133 }
134 }
135 }