View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.directory.api.ldap.model.cursor;
20  
21  
22  import java.util.Collections;
23  import java.util.Comparator;
24  import java.util.List;
25  
26  import org.apache.directory.api.i18n.I18n;
27  import org.apache.directory.api.ldap.model.constants.Loggers;
28  import org.apache.directory.api.ldap.model.exception.LdapException;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  
33  /**
34   * A simple implementation of a Cursor on a {@link List}.  Optionally, the
35   * Cursor may be limited to a specific range within the list.
36   *
37   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
38   * @param <E> The element on which this cursor will iterate
39   */
40  public class ListCursor<E> extends AbstractCursor<E>
41  {
42      /** A dedicated log for cursors */
43      private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
44  
45      /** Speedup for logs */
46      private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
47  
48      /** The inner List */
49      private final List<E> list;
50  
51      /** The associated comparator */
52      private final Comparator<E> comparator;
53  
54      /** The starting position for the cursor in the list. It can be > 0 */
55      private final int start;
56  
57      /** The ending position for the cursor in the list. It can be < List.size() */
58      private final int end;
59      /** The current position in the list */
60  
61      private int index = -1;
62  
63  
64      /**
65       * Creates a new ListCursor with lower (inclusive) and upper (exclusive)
66       * bounds.
67       *
68       * As with all Cursors, this ListCursor requires a successful return from
69       * advance operations (next() or previous()) to properly return values
70       * using the get() operation.
71       *
72       * @param comparator an optional comparator to use for ordering
73       * @param start the lower bound index
74       * @param list the list this ListCursor operates on
75       * @param end the upper bound index
76       */
77      public ListCursor( Comparator<E> comparator, int start, List<E> list, int end )
78      {
79          if ( list == null )
80          {
81              list = Collections.emptyList();
82          }
83  
84          if ( ( start < 0 ) || ( start > list.size() ) )
85          {
86              throw new IllegalArgumentException( I18n.err( I18n.ERR_02005_START_INDEX_OUT_OF_RANGE, start ) );
87          }
88  
89          if ( ( end < 0 ) || ( end > list.size() ) )
90          {
91              throw new IllegalArgumentException( I18n.err( I18n.ERR_02006_END_INDEX_OUT_OF_RANGE, end ) );
92          }
93  
94          // check list is not empty list since the empty list is the only situation
95          // where we allow for start to equal the end: in other cases it makes no sense
96          if ( ( list.size() > 0 ) && ( start >= end ) )
97          {
98              throw new IllegalArgumentException( I18n.err( I18n.ERR_02007_START_INDEX_ABOVE_END_INDEX, start, end ) );
99          }
100 
101         if ( IS_DEBUG )
102         {
103             LOG_CURSOR.debug( "Creating ListCursor {}", this );
104         }
105 
106         this.comparator = comparator;
107         this.list = list;
108         this.start = start;
109         this.end = end;
110     }
111 
112 
113     /**
114      * Creates a new ListCursor with lower (inclusive) and upper (exclusive)
115      * bounds.
116      *
117      * As with all Cursors, this ListCursor requires a successful return from
118      * advance operations (next() or previous()) to properly return values
119      * using the get() operation.
120      *
121      * @param start the lower bound index
122      * @param list the list this ListCursor operates on
123      * @param end the upper bound index
124      */
125     public ListCursor( int start, List<E> list, int end )
126     {
127         this( null, start, list, end );
128     }
129 
130 
131     /**
132      * Creates a new ListCursor with a specific upper (exclusive) bound: the
133      * lower (inclusive) bound defaults to 0.
134      *
135      * @param list the backing for this ListCursor
136      * @param end the upper bound index representing the position after the
137      * last element
138      */
139     public ListCursor( List<E> list, int end )
140     {
141         this( null, 0, list, end );
142     }
143 
144 
145     /**
146      * Creates a new ListCursor with a specific upper (exclusive) bound: the
147      * lower (inclusive) bound defaults to 0. We also provide a comparator.
148      *
149      * @param comparator The comparator to use for the <E> elements
150      * @param list the backing for this ListCursor
151      * @param end the upper bound index representing the position after the
152      * last element
153      */
154     public ListCursor( Comparator<E> comparator, List<E> list, int end )
155     {
156         this( comparator, 0, list, end );
157     }
158 
159 
160     /**
161      * Creates a new ListCursor with a lower (inclusive) bound: the upper
162      * (exclusive) bound is the size of the list.
163      *
164      * @param start the lower (inclusive) bound index: the position of the
165      * first entry
166      * @param list the backing for this ListCursor
167      */
168     public ListCursor( int start, List<E> list )
169     {
170         this( null, start, list, list.size() );
171     }
172 
173 
174     /**
175      * Creates a new ListCursor with a lower (inclusive) bound: the upper
176      * (exclusive) bound is the size of the list. We also provide a comparator.
177      *
178      * @param comparator The comparator to use for the <E> elements
179      * @param start the lower (inclusive) bound index: the position of the
180      * first entry
181      * @param list the backing for this ListCursor
182      */
183     public ListCursor( Comparator<E> comparator, int start, List<E> list )
184     {
185         this( comparator, start, list, list.size() );
186     }
187 
188 
189     /**
190      * Creates a new ListCursor without specific bounds: the bounds are
191      * acquired from the size of the list.
192      *
193      * @param list the backing for this ListCursor
194      */
195     public ListCursor( List<E> list )
196     {
197         this( null, 0, list, list.size() );
198     }
199 
200 
201     /**
202      * Creates a new ListCursor without specific bounds: the bounds are
203      * acquired from the size of the list. We also provide a comparator.
204      *
205      * @param comparator The comparator to use for the <E> elements
206      * @param list the backing for this ListCursor
207      */
208     public ListCursor( Comparator<E> comparator, List<E> list )
209     {
210         this( comparator, 0, list, list.size() );
211     }
212 
213 
214     /**
215      * Creates a new ListCursor without any elements.
216      */
217     @SuppressWarnings("unchecked")
218     public ListCursor()
219     {
220         this( null, 0, Collections.EMPTY_LIST, 0 );
221     }
222 
223 
224     /**
225      * Creates a new ListCursor without any elements. We also provide 
226      * a comparator.
227      * 
228      * @param comparator The comparator to use for the <E> elements
229      */
230     @SuppressWarnings("unchecked")
231     public ListCursor( Comparator<E> comparator )
232     {
233         this( comparator, 0, Collections.EMPTY_LIST, 0 );
234     }
235 
236 
237     /**
238      * {@inheritDoc}
239      */
240     public boolean available()
241     {
242         return index >= 0 && index < end;
243     }
244 
245 
246     /**
247      * {@inheritDoc}
248      */
249     public void before( E element ) throws LdapException, CursorException
250     {
251         checkNotClosed( "before()" );
252 
253         if ( comparator == null )
254         {
255             throw new IllegalStateException();
256         }
257 
258         // handle some special cases
259         if ( list.size() == 0 )
260         {
261             return;
262         }
263         else if ( list.size() == 1 )
264         {
265             if ( comparator.compare( element, list.get( 0 ) ) <= 0 )
266             {
267                 beforeFirst();
268             }
269             else
270             {
271                 afterLast();
272             }
273         }
274 
275         throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008_LIST_MAY_BE_SORTED ) );
276     }
277 
278 
279     /**
280      * {@inheritDoc}
281      */
282     public void after( E element ) throws LdapException, CursorException
283     {
284         checkNotClosed( "after()" );
285 
286         if ( comparator == null )
287         {
288             throw new IllegalStateException();
289         }
290 
291         // handle some special cases
292         if ( list.size() == 0 )
293         {
294             return;
295         }
296         else if ( list.size() == 1 )
297         {
298             if ( comparator.compare( element, list.get( 0 ) ) >= 0 )
299             {
300                 afterLast();
301             }
302             else
303             {
304                 beforeFirst();
305             }
306         }
307 
308         throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008_LIST_MAY_BE_SORTED ) );
309     }
310 
311 
312     /**
313      * {@inheritDoc}
314      */
315     public void beforeFirst() throws LdapException, CursorException
316     {
317         checkNotClosed( "beforeFirst()" );
318         this.index = -1;
319     }
320 
321 
322     /**
323      * {@inheritDoc}
324      */
325     public void afterLast() throws LdapException, CursorException
326     {
327         checkNotClosed( "afterLast()" );
328         this.index = end;
329     }
330 
331 
332     /**
333      * {@inheritDoc}
334      */
335     public boolean first() throws LdapException, CursorException
336     {
337         checkNotClosed( "first()" );
338 
339         if ( list.size() > 0 )
340         {
341             index = start;
342 
343             return true;
344         }
345 
346         return false;
347     }
348 
349 
350     /**
351      * {@inheritDoc}
352      */
353     public boolean last() throws LdapException, CursorException
354     {
355         checkNotClosed( "last()" );
356 
357         if ( list.size() > 0 )
358         {
359             index = end - 1;
360 
361             return true;
362         }
363 
364         return false;
365     }
366 
367 
368     /**
369      * {@inheritDoc}
370      */
371     @Override
372     public boolean isFirst()
373     {
374         return list.size() > 0 && index == start;
375     }
376 
377 
378     /**
379      * {@inheritDoc}
380      */
381     @Override
382     public boolean isLast()
383     {
384         return list.size() > 0 && index == end - 1;
385     }
386 
387 
388     /**
389      * {@inheritDoc}
390      */
391     @Override
392     public boolean isAfterLast()
393     {
394         return index == end;
395     }
396 
397 
398     /**
399      * {@inheritDoc}
400      */
401     @Override
402     public boolean isBeforeFirst()
403     {
404         return index == -1;
405     }
406 
407 
408     /**
409      * {@inheritDoc}
410      */
411     public boolean previous() throws LdapException, CursorException
412     {
413         checkNotClosed( "previous()" );
414 
415         // if parked at -1 we cannot go backwards
416         if ( index == -1 )
417         {
418             return false;
419         }
420 
421         // if the index moved back is still greater than or eq to start then OK
422         if ( index - 1 >= start )
423         {
424             index--;
425 
426             return true;
427         }
428 
429         // if the index currently less than or equal to start we need to park it at -1 and return false
430         if ( index <= start )
431         {
432             index = -1;
433 
434             return false;
435         }
436 
437         if ( list.size() <= 0 )
438         {
439             index = -1;
440         }
441 
442         return false;
443     }
444 
445 
446     /**
447      * {@inheritDoc}
448      */
449     public boolean next() throws LdapException, CursorException
450     {
451         checkNotClosed( "next()" );
452 
453         // if parked at -1 we advance to the start index and return true
454         if ( ( list.size() > 0 ) && ( index == -1 ) )
455         {
456             index = start;
457 
458             return true;
459         }
460 
461         // if the index plus one is less than the end then increment and return true
462         if ( ( list.size() > 0 ) && ( index + 1 < end ) )
463         {
464             index++;
465 
466             return true;
467         }
468 
469         // if the index plus one is equal to the end then increment and return false
470         if ( ( list.size() > 0 ) && ( index + 1 == end ) )
471         {
472             index++;
473 
474             return false;
475         }
476 
477         if ( list.size() <= 0 )
478         {
479             index = end;
480         }
481 
482         return false;
483     }
484 
485 
486     /**
487      * {@inheritDoc}
488      */
489     public E get() throws CursorException
490     {
491         checkNotClosed( "get()" );
492 
493         if ( ( index < start ) || ( index >= end ) )
494         {
495             throw new CursorException( I18n.err( I18n.ERR_02009_CURSOR_NOT_POSITIONED ) );
496         }
497 
498         return list.get( index );
499     }
500 
501 
502     /**
503      * {@inheritDoc}
504      */
505     @Override
506     public void close()
507     {
508         if ( IS_DEBUG )
509         {
510             LOG_CURSOR.debug( "Closing ListCursor {}", this );
511         }
512 
513         super.close();
514     }
515 
516 
517     /**
518      * {@inheritDoc}
519      */
520     @Override
521     public void close( Exception cause )
522     {
523         if ( IS_DEBUG )
524         {
525             LOG_CURSOR.debug( "Closing ListCursor {}", this );
526         }
527 
528         super.close( cause );
529     }
530 }