001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *  http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.directory.shared.ldap.cursor;
020    
021    
022    import java.io.IOException;
023    import java.util.Collections;
024    import java.util.List;
025    import java.util.Comparator;
026    
027    import org.apache.directory.shared.i18n.I18n;
028    
029    
030    /**
031     * A simple implementation of a Cursor on a {@link List}.  Optionally, the
032     * Cursor may be limited to a specific range within the list.
033     *
034     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
035     * @version $Rev$, $Date$
036     */
037    public class ListCursor<E> extends AbstractCursor<E>
038    {
039        /** The inner List */
040        private final List<E> list;
041        
042        /** The associated comparator */
043        private final Comparator<E> comparator;
044        
045        /** The starting position for the cursor in the list. It can be > 0 */
046        private final int start;
047        
048        /** The ending position for the cursor in the list. It can be < List.size() */
049        private final int end;
050        
051        /** The current position in the list */
052        private int index = -1;
053    
054    
055        /**
056         * Creates a new ListCursor with lower (inclusive) and upper (exclusive)
057         * bounds.
058         *
059         * As with all Cursors, this ListCursor requires a successful return from
060         * advance operations (next() or previous()) to properly return values
061         * using the get() operation.
062         *
063         * @param comparator an optional comparator to use for ordering
064         * @param start the lower bound index
065         * @param list the list this ListCursor operates on
066         * @param end the upper bound index
067         */
068        public ListCursor( Comparator<E> comparator, int start, List<E> list, int end )
069        {
070            if ( ( start < 0  )|| ( start > list.size() ) )
071            {
072                throw new IllegalArgumentException( I18n.err( I18n.ERR_02005, start ) );
073            }
074    
075            if ( ( end < 0 ) || ( end > list.size() ) )
076            {
077                throw new IllegalArgumentException( I18n.err( I18n.ERR_02006, end ) );
078            }
079    
080            // check list is not empty list since the empty list is the only situation
081            // where we allow for start to equal the end: in other cases it makes no sense
082            if ( ( list.size() > 0 ) && ( start >= end ) )
083            {
084                throw new IllegalArgumentException( I18n.err( I18n.ERR_02007, start, end ) );
085            }
086    
087            this.comparator = comparator;
088    
089            if ( list != null )
090            {
091                this.list = list;
092            }
093            else
094            {
095                this.list = Collections.emptyList();
096            }
097    
098            this.start = start;
099            this.end = end;
100        }
101    
102    
103        /**
104         * Creates a new ListCursor with lower (inclusive) and upper (exclusive)
105         * bounds.
106         *
107         * As with all Cursors, this ListCursor requires a successful return from
108         * advance operations (next() or previous()) to properly return values
109         * using the get() operation.
110         *
111         * @param start the lower bound index
112         * @param list the list this ListCursor operates on
113         * @param end the upper bound index
114         */
115        public ListCursor( int start, List<E> list, int end )
116        {
117            this( null, start, list, end );
118        }
119    
120    
121        /**
122         * Creates a new ListCursor with a specific upper (exclusive) bound: the
123         * lower (inclusive) bound defaults to 0.
124         *
125         * @param list the backing for this ListCursor
126         * @param end the upper bound index representing the position after the
127         * last element
128         */
129        public ListCursor( List<E> list, int end )
130        {
131            this( null, 0, list, end );
132        }
133    
134    
135        /**
136         * Creates a new ListCursor with a specific upper (exclusive) bound: the
137         * lower (inclusive) bound defaults to 0. We also provide a comparator.
138         *
139         * @param comparator The comparator to use for the <E> elements
140         * @param list the backing for this ListCursor
141         * @param end the upper bound index representing the position after the
142         * last element
143         */
144        public ListCursor( Comparator<E> comparator, List<E> list, int end )
145        {
146            this( comparator, 0, list, end );
147        }
148    
149    
150        /**
151         * Creates a new ListCursor with a lower (inclusive) bound: the upper
152         * (exclusive) bound is the size of the list.
153         *
154         * @param start the lower (inclusive) bound index: the position of the
155         * first entry
156         * @param list the backing for this ListCursor
157         */
158        public ListCursor( int start, List<E> list )
159        {
160            this( null, start, list, list.size() );
161        }
162    
163    
164        /**
165         * Creates a new ListCursor with a lower (inclusive) bound: the upper
166         * (exclusive) bound is the size of the list. We also provide a comparator.
167         *
168         * @param comparator The comparator to use for the <E> elements
169         * @param start the lower (inclusive) bound index: the position of the
170         * first entry
171         * @param list the backing for this ListCursor
172         */
173        public ListCursor( Comparator<E> comparator, int start, List<E> list )
174        {
175            this( comparator, start, list, list.size() );
176        }
177    
178    
179        /**
180         * Creates a new ListCursor without specific bounds: the bounds are
181         * acquired from the size of the list.
182         *
183         * @param list the backing for this ListCursor
184         */
185        public ListCursor( List<E> list )
186        {
187            this( null, 0, list, list.size() );
188        }
189    
190    
191        /**
192         * Creates a new ListCursor without specific bounds: the bounds are
193         * acquired from the size of the list. We also provide a comparator.
194         *
195         * @param comparator The comparator to use for the <E> elements
196         * @param list the backing for this ListCursor
197         */
198        public ListCursor( Comparator<E> comparator, List<E> list )
199        {
200            this( comparator, 0, list, list.size() );
201        }
202    
203    
204        /**
205         * Creates a new ListCursor without any elements.
206         */
207        @SuppressWarnings("unchecked")
208        public ListCursor()
209        {
210            this( null, 0, Collections.EMPTY_LIST, 0 );
211        }
212    
213    
214        /**
215         * Creates a new ListCursor without any elements. We also provide 
216         * a comparator.
217         * 
218         * @param comparator The comparator to use for the <E> elements
219         */
220        @SuppressWarnings("unchecked")
221        public ListCursor( Comparator<E> comparator )
222        {
223            this( comparator, 0, Collections.EMPTY_LIST, 0 );
224        }
225    
226    
227        /**
228         * {@inheritDoc}
229         */
230        public boolean available()
231        {
232            return index >= 0 && index < end;
233        }
234    
235    
236        /**
237         * @throws IllegalStateException if the underlying list is not sorted
238         * and/or a comparator is not provided.
239         */
240        public void before( E element ) throws Exception
241        {
242            checkNotClosed( "before()" );
243    
244            if ( comparator == null )
245            {
246                throw new IllegalStateException();
247            }
248    
249            // handle some special cases
250            if ( list.size() == 0 )
251            {
252                return;
253            }
254            else if ( list.size() == 1 )
255            {
256                if ( comparator.compare( element, list.get( 0 ) ) <= 0 )
257                {
258                    beforeFirst();
259                }
260                else
261                {
262                    afterLast();
263                }
264            }
265    
266            // TODO might want to add some code here to utilize the comparator
267            throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008 ) );
268        }
269    
270    
271        /**
272         * {@inheritDoc}
273         */
274        public void after( E element ) throws Exception
275        {
276            checkNotClosed( "after()" );
277    
278            if ( comparator == null )
279            {
280                throw new IllegalStateException();
281            }
282    
283            // handle some special cases
284            if ( list.size() == 0 )
285            {
286                return;
287            }
288            else if ( list.size() == 1 )
289            {
290                if ( comparator.compare( element, list.get( 0 ) ) >= 0 )
291                {
292                    afterLast();
293                }
294                else
295                {
296                    beforeFirst();
297                }
298            }
299    
300            // TODO might want to add some code here to utilize the comparator
301            throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008 ) );
302        }
303    
304    
305        /**
306         * {@inheritDoc}
307         */
308        public void beforeFirst() throws Exception
309        {
310            checkNotClosed( "beforeFirst()" );
311            this.index = -1;
312        }
313    
314    
315        /**
316         * {@inheritDoc}
317         */
318        public void afterLast() throws Exception
319        {
320            checkNotClosed( "afterLast()" );
321            this.index = end;
322        }
323    
324    
325        /**
326         * {@inheritDoc}
327         */
328        public boolean first() throws Exception
329        {
330            checkNotClosed( "first()" );
331    
332            if ( list.size() > 0 )
333            {
334                index = start;
335                return true;
336            }
337    
338            return false;
339        }
340    
341    
342        /**
343         * {@inheritDoc}
344         */
345        public boolean last() throws Exception
346        {
347            checkNotClosed( "last()" );
348    
349            if ( list.size() > 0 )
350            {
351                index = end - 1;
352                return true;
353            }
354            
355            return false;
356        }
357    
358    
359        /**
360         * {@inheritDoc}
361         */
362        public boolean isFirst() throws Exception
363        {
364            checkNotClosed( "isFirst()" );
365            return list.size() > 0 && index == start;
366        }
367    
368    
369        /**
370         * {@inheritDoc}
371         */
372        public boolean isLast() throws Exception
373        {
374            checkNotClosed( "isLast()" );
375            return list.size() > 0 && index == end - 1;
376        }
377    
378    
379        /**
380         * {@inheritDoc}
381         */
382        public boolean isAfterLast() throws Exception
383        {
384            checkNotClosed( "isAfterLast()" );
385            return index == end;
386        }
387    
388    
389        /**
390         * {@inheritDoc}
391         */
392        public boolean isBeforeFirst() throws Exception
393        {
394            checkNotClosed( "isBeforeFirst()" );
395            return index == -1;
396        }
397    
398    
399        /**
400         * {@inheritDoc}
401         */
402        public boolean previous() throws Exception
403        {
404            checkNotClosed( "previous()" );
405    
406            // if parked at -1 we cannot go backwards
407            if ( index == -1 )
408            {
409                return false;
410            }
411    
412            // if the index moved back is still greater than or eq to start then OK
413            if ( index - 1 >= start )
414            {
415                index--;
416                return true;
417            }
418    
419            // if the index currently less than or equal to start we need to park it at -1 and return false
420            if ( index <= start )
421            {
422                index = -1;
423                return false;
424            }
425    
426            if ( list.size() <= 0 )
427            {
428                index = -1;
429            }
430    
431            return false;
432        }
433    
434    
435        /**
436         * {@inheritDoc}
437         */
438        public boolean next() throws Exception
439        {
440            checkNotClosed( "next()" );
441    
442            // if parked at -1 we advance to the start index and return true
443            if ( list.size() > 0 && index == -1 )
444            {
445                index = start;
446                return true;
447            }
448    
449            // if the index plus one is less than the end then increment and return true
450            if ( list.size() > 0 && index + 1 < end )
451            {
452                index++;
453                return true;
454            }
455    
456            // if the index plus one is equal to the end then increment and return false
457            if ( list.size() > 0 && index + 1 == end )
458            {
459                index++;
460                return false;
461            }
462    
463            if ( list.size() <= 0 )
464            {
465                index = end;
466            }
467    
468            return false;
469        }
470    
471    
472        /**
473         * {@inheritDoc}
474         */
475        public E get() throws Exception
476        {
477            checkNotClosed( "get()" );
478            
479            if ( index < start || index >= end )
480            {
481                throw new IOException( I18n.err( I18n.ERR_02009 ) );
482            }
483    
484            return list.get( index );
485        }
486    
487    
488        /**
489         * {@inheritDoc}
490         */
491        public boolean isElementReused()
492        {
493            return true;
494        }
495    }