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    }