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 */
021package org.biojava.nbio.genome.parsers.gff;
022
023import org.biojava.nbio.genome.App;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027import java.util.Iterator;
028import java.util.NoSuchElementException;
029
030/**
031 * Move a sliding window over a Location.
032 * Window size and increment can be specified.
033 * If the increment is negative, the iteration starts
034 * at end of Location and moves toward beginning.
035 *
036 * @author Hanno Hinsch
037 */
038public class LocIterator implements Iterator<Location> {
039        private static final Logger logger = LoggerFactory.getLogger(App.class);
040
041        Location mBounds;
042        int mPosition;
043        int mWindowSize;
044        int mIncrement;
045
046        @SuppressWarnings("unused")
047        private LocIterator() {};
048
049        /**
050         * Construct an iterator that slides a window over a Location.
051         *
052         * @param bounds The location over which to iterate.
053         * @param windowSize The size of the moving window.
054         * @param increment The increment by which to move the window at each iteration.
055         * If increment is positive, the iteration starts at the beginning of the bounding location
056         * and moves toward the end; if the increment is negative, the iteration starts at the end and moves
057         * toward the begnning.
058         */
059        public LocIterator( Location bounds, int windowSize, int increment )
060        {
061                mWindowSize= windowSize;
062                mIncrement= increment;
063                mBounds= bounds;
064
065                if( windowSize <= 0 )
066                {
067                        throw new IllegalArgumentException( "Window size must be positive." );
068                }
069
070                if( increment == 0 )
071                {
072                        throw new IllegalArgumentException( "Increment must be non-zero." );
073                }
074
075                mPosition= 0;
076
077        }
078
079
080
081        /**
082         * Check if next window of specified size is available.
083         *
084         * @param windowSize Size of window. May be smaller or larger than default window size.
085         * @param increment The increment by which to move the window at each iteration. Note that this
086         * method does not actually change the position. However, it checks the sign of the increment parameter to determine
087         * the direction of the iteration.
088         * @return True if window of specified size is available.
089         * @throws IllegalArgumentException Window size parameter was not positive.
090         */
091        public boolean hasNext( int windowSize, int increment )
092        {
093                if( windowSize <= 0 )
094                {
095                        throw new IllegalArgumentException( "Window size must be positive." );
096                }
097
098                try
099                {
100                        if( increment > 0 )
101                        {
102                                return windowSize == mBounds.suffix( mPosition ).prefix( windowSize ).length();
103                        }
104                        else
105                        {
106                                if( mPosition == 0 )
107                                {
108                                        return windowSize == mBounds.suffix( - windowSize ).length();
109                                }
110                                else
111                                {
112                                        return windowSize == mBounds.prefix( mPosition ).suffix( - windowSize ).length();
113                                }
114                        }
115                }
116                catch( Exception e )
117                {
118                        return false;
119                }
120        }
121
122        /**
123         * Check if next window of default size is available.
124         *
125         * @return True if window of default size is available. The default size
126         * is the size specified in the LocIterator constructor.
127         */
128        @Override
129        public boolean hasNext()
130        {
131                return hasNext( mWindowSize, mIncrement );
132        }
133
134        /**
135         * Get portion of bounding location that has not yet been retrieved by next() method.
136         *
137         * @return The location not yet retrieved.
138         */
139        public Location remainder()
140        {
141                Location remainder= null;
142
143                if( mPosition == 0 )
144                {
145                        remainder= mBounds;
146                }
147                else
148                {
149                        if( mIncrement > 0 )
150                        {
151                                remainder = mBounds.suffix( mPosition );
152                        }
153                        else
154                        {
155                                remainder = mBounds.prefix( mPosition );
156                        }
157                }
158
159                return remainder;
160
161        }
162
163        /**
164         * Get next window of default size, then increment position by default amount. Both
165         * defaults are specified in the LocIterator constructor.
166         *
167         * @return Location of next window.
168         * @throws IndexOutOfBoundsException The next window was not within the bounding location.
169         */
170        @Override
171        public Location next()
172        {
173        if(!hasNext()){
174            throw new NoSuchElementException();
175        }
176        return next( mWindowSize, mIncrement );
177        }
178
179        /**
180         * Get next window of specified size, then increment position by specified amount.
181         *
182         * @return Location of next window.
183         * @param windowSize Size of window to get.
184         * @param increment Amount by which to shift position. If increment is positive, the position is shifted
185         * toward the end of the bounding location; if increment is negative, the position is shifted toward
186         * the beginning of the bounding location.
187         * @throws IndexOutOfBoundsException The next window was not within the bounding location.
188         * @throws IllegalArgumentException The increment was zero, or windowSize was not positive.
189         */
190        public Location next( int windowSize, int increment )
191        {
192                if( windowSize <= 0 )
193                {
194                        throw new IllegalArgumentException( "Window size must be positive." );
195                }
196
197                if( increment == 0 )
198                {
199                        throw new IllegalArgumentException( "Increment must be non-zero." );
200                }
201
202                Location r;
203
204                try
205                {
206                        if( increment > 0 )
207                        {
208                                r= mBounds.suffix( mPosition ).prefix( windowSize );
209                        }
210                        else
211                        {
212                                if( mPosition == 0 )
213                                {
214                                        r= mBounds.suffix( - windowSize );
215                                }
216                                else
217                                {
218                                        r= mBounds.prefix( mPosition ).suffix( - windowSize );
219                                }
220                        }
221
222                        mPosition+= increment;
223
224                }
225                catch( Exception e )
226                {
227                        throw new IndexOutOfBoundsException( e.toString() );
228                }
229
230                return r;
231        }
232
233        /**
234         * Get string representation of iterator.
235         *
236         * @return Description of internal state.
237         */
238        @Override
239        public String toString()
240        {
241                return "bounds=" + mBounds.toString() + "; pos=" + mPosition + "; winsize=" + mWindowSize + "; inc=" + mIncrement;
242        }
243
244        /**
245         * Unsupported.
246         *
247         * @throws UnsupportedOperationException
248         */
249        @Override
250        public void remove()
251        {
252                throw new UnsupportedOperationException();
253        }
254
255        /**
256         * @deprecated
257         */
258        @Deprecated
259        public static void main(String[] args )
260        {
261
262                Location r= new Location( 10, 21 );
263
264                logger.info( "10 to 21, 1 by 1" );
265                for( Location t: r.window( 1, 1 )) { logger.info( t.toString() ); }
266
267                logger.info( "10 to 21, 3 by 3" );
268                for( Location t: r.window( 3, 3 )) { logger.info( t.toString() ); }
269
270                logger.info( "10 to 21, 3 by 1" );
271                for( Location t: r.window( 3, 1 )) { logger.info( t.toString() ); }
272
273                logger.info( "10 to 21, 11 by 1" );
274                for( Location t: r.window( 11, 1 )) { logger.info( t.toString() ); }
275
276                logger.info( "10 to 21, 12 by 1" );
277                for( Location t: r.window( 12, 1 )) { logger.info( t.toString() ); }
278
279                logger.info( "10 to 21, 1 by -1" );
280                for( Location t: r.window( 1, -1 )) { logger.info( t.toString() ); }
281
282                logger.info( "10 to 21, 3 by -3" );
283                for( Location t: r.window( 3, -3 )) { logger.info( t.toString() ); }
284
285                logger.info( "10 to 21, 3 by -1" );
286                for( Location t: r.window( 3, -1 )) { logger.info( t.toString() ); }
287
288                logger.info( "10 to 21, 1 by 1" );
289                for( Location t: r.window( 1, 1 )) { logger.info( t.toString() ); }
290
291                logger.info( "10 to 21, 3 by 3" );
292                for( Location t: r.window( 3, 3 )) { logger.info( t.toString() ); }
293
294                logger.info( "10 to 21, 3 by 1" );
295                for( Location t: r.window( 3, 1 )) { logger.info( t.toString() ); }
296
297                logger.info( "10 to 21, 11 by 1" );
298                for( Location t: r.window( 11, 1 )) { logger.info( t.toString() ); }
299
300                logger.info( "10 to 21, 12 by 1" );
301                for( Location t: r.window( 12, 1 )) { logger.info( t.toString() ); }
302
303                logger.info( "10 to 21, 1 by -1" );
304                for( Location t: r.window( 1, -1 )) { logger.info( t.toString() ); }
305
306                //reverse strand
307                r= r.opposite();
308                logger.info( "reverse strand" );
309
310                logger.info( "10 to 21, 1 by 1" );
311                for( Location t: r.window( 1, 1 )) { logger.info( t.toString() ); }
312
313                logger.info( "10 to 21, 3 by 3" );
314                for( Location t: r.window( 3, 3 )) { logger.info( t.toString() ); }
315
316                logger.info( "10 to 21, 3 by 1" );
317                for( Location t: r.window( 3, 1 )) { logger.info( t.toString() ); }
318
319                logger.info( "10 to 21, 11 by 1" );
320                for( Location t: r.window( 11, 1 )) { logger.info( t.toString() ); }
321
322                logger.info( "10 to 21, 12 by 1" );
323                for( Location t: r.window( 12, 1 )) { logger.info( t.toString() ); }
324
325                logger.info( "10 to 21, 1 by -1" );
326                for( Location t: r.window( 1, -1 )) { logger.info( t.toString() ); }
327
328                logger.info( "10 to 21, 3 by -3" );
329                for( Location t: r.window( 3, -3 )) { logger.info( t.toString() ); }
330
331                logger.info( "10 to 21, 3 by -1" );
332                for( Location t: r.window( 3, -1 )) { logger.info( t.toString() ); }
333
334                logger.info( "10 to 21, 1 by 1" );
335                for( Location t: r.window( 1, 1 )) { logger.info( t.toString() ); }
336
337                logger.info( "10 to 21, 3 by 3" );
338                for( Location t: r.window( 3, 3 )) { logger.info( t.toString() ); }
339
340                logger.info( "10 to 21, 3 by 1" );
341                for( Location t: r.window( 3, 1 )) { logger.info( t.toString() ); }
342
343                logger.info( "10 to 21, 11 by 1" );
344                for( Location t: r.window( 11, 1 )) { logger.info( t.toString() ); }
345
346                logger.info( "10 to 21, 12 by 1" );
347                for( Location t: r.window( 12, 1 )) { logger.info( t.toString() ); }
348
349                logger.info( "10 to 21, 1 by -1" );
350                for( Location t: r.window( 1, -1 )) { logger.info( t.toString() ); }
351
352                logger.info( "10 to 21, 1 by 1 (+2)" );
353                LocIterator i= r.iterator();
354                int chunk= 1;
355                while( i.hasNext( 1, chunk ) )
356                {
357                        Location t= i.next( 1, chunk );
358                        logger.info( t.toString() );
359                        chunk+= 2;
360                }
361
362                //FIXME test remainder()
363
364                logger.info("JavaGene.LocIterator Passed.");
365        }
366
367}