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