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.server.core.cursor;
20  
21  
22  import java.io.IOException;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.Comparator;
26  
27  
28  /**
29   * A simple implementation of a Cursor on a {@link List}.  Optionally, the
30   * Cursor may be limited to a specific range within the list.
31   *
32   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
33   * @version $Rev$, $Date$
34   */
35  public class ListCursor<E> extends AbstractCursor<E>
36  {
37      private final List<E> list;
38      private final Comparator<E> comparator;
39      private final int start;
40      private final int end;
41      private int index = -1;
42  
43  
44      /**
45       * Creates a new ListCursor with lower (inclusive) and upper (exclusive)
46       * bounds.
47       *
48       * As with all Cursors, this ListCursor requires a successful return from
49       * advance operations (next() or previous()) to properly return values
50       * using the get() operation.
51       *
52       * @param comparator an optional comparator to use for ordering
53       * @param start the lower bound index
54       * @param list the list this ListCursor operates on
55       * @param end the upper bound index
56       */
57      public ListCursor( Comparator<E> comparator, int start, List<E> list, int end )
58      {
59          if ( start < 0 || start > list.size() )
60          {
61              throw new IllegalArgumentException( "start index '" + start + "' out of range" );
62          }
63  
64          if ( end < 0 || end > list.size() )
65          {
66              throw new IllegalArgumentException( "end index '" + end + "' out of range" );
67          }
68  
69          // check list is not empty list since the empty list is the only situation
70          // where we allow for start to equal the end: in other cases it makes no sense
71          if ( list.size() > 0 && start >= end )
72          {
73              throw new IllegalArgumentException( "start index '" + start + "' greater than or equal to end index '"
74                      + end + "' just does not make sense" );
75          }
76  
77          this.comparator = comparator;
78  
79          //noinspection ConstantConditions
80          if ( list != null )
81          {
82              this.list = list;
83          }
84          else
85          {
86              //noinspection unchecked
87              this.list = Collections.emptyList();
88          }
89  
90          this.start = start;
91          this.end = end;
92      }
93  
94  
95      /**
96       * Creates a new ListCursor with lower (inclusive) and upper (exclusive)
97       * bounds.
98       *
99       * As with all Cursors, this ListCursor requires a successful return from
100      * advance operations (next() or previous()) to properly return values
101      * using the get() operation.
102      *
103      * @param start the lower bound index
104      * @param list the list this ListCursor operates on
105      * @param end the upper bound index
106      */
107     public ListCursor( int start, List<E> list, int end )
108     {
109         this( null, start, list, end );
110     }
111 
112 
113     /**
114      * Creates a new ListCursor with a specific upper (exclusive) bound: the
115      * lower (inclusive) bound defaults to 0.
116      *
117      * @param list the backing for this ListCursor
118      * @param end the upper bound index representing the position after the
119      * last element
120      */
121     public ListCursor( List<E> list, int end )
122     {
123         this( null, 0, list, end );
124     }
125 
126 
127     public ListCursor( Comparator<E> comparator, List<E> list, int end )
128     {
129         this( comparator, 0, list, end );
130     }
131 
132 
133     /**
134      * Creates a new ListCursor with a lower (inclusive) bound: the upper
135      * (exclusive) bound is the size of the list.
136      *
137      * @param start the lower (inclusive) bound index: the position of the
138      * first entry
139      * @param list the backing for this ListCursor
140      */
141     public ListCursor( int start, List<E> list )
142     {
143         this( null, start, list, list.size() );
144     }
145 
146 
147     public ListCursor( Comparator<E> comparator, int start, List<E> list )
148     {
149         this( comparator, start, list, list.size() );
150     }
151 
152 
153     /**
154      * Creates a new ListCursor without specific bounds: the bounds are
155      * acquired from the size of the list.
156      *
157      * @param list the backing for this ListCursor
158      */
159     public ListCursor( List<E> list )
160     {
161         this( null, 0, list, list.size() );
162     }
163 
164 
165     public ListCursor( Comparator<E> comparator, List<E> list )
166     {
167         this( comparator, 0, list, list.size() );
168     }
169 
170 
171     /**
172      * Creates a new ListCursor without any elements.
173      */
174     @SuppressWarnings("unchecked")
175     public ListCursor()
176     {
177         this( null, 0, Collections.EMPTY_LIST, 0 );
178     }
179 
180 
181     @SuppressWarnings("unchecked")
182     public ListCursor( Comparator<E> comparator )
183     {
184         this( comparator, 0, Collections.EMPTY_LIST, 0 );
185     }
186 
187 
188     public boolean available()
189     {
190         return index >= 0 && index < end;
191     }
192 
193 
194     /**
195      * @throws IllegalStateException if the underlying list is not sorted
196      * and/or a comparator is not provided.
197      */
198     public void before( E element ) throws Exception
199     {
200         checkNotClosed( "before()" );
201 
202         if ( comparator == null )
203         {
204             throw new IllegalStateException();
205         }
206 
207         // handle some special cases
208         if ( list.size() == 0 )
209         {
210             return;
211         }
212         else if ( list.size() == 1 )
213         {
214             if ( comparator.compare( element, list.get( 0 ) ) <= 0 )
215             {
216                 beforeFirst();
217             }
218             else
219             {
220                 afterLast();
221             }
222         }
223 
224         // TODO might want to add some code here to utilize the comparator
225         throw new UnsupportedOperationException( "don't know if list is sorted and checking that is not worth it" );
226     }
227 
228 
229     public void after( E element ) throws Exception
230     {
231         checkNotClosed( "after()" );
232 
233         if ( comparator == null )
234         {
235             throw new IllegalStateException();
236         }
237 
238         // handle some special cases
239         if ( list.size() == 0 )
240         {
241             return;
242         }
243         else if ( list.size() == 1 )
244         {
245             if ( comparator.compare( element, list.get( 0 ) ) >= 0 )
246             {
247                 afterLast();
248             }
249             else
250             {
251                 beforeFirst();
252             }
253         }
254 
255         // TODO might want to add some code here to utilize the comparator
256         throw new UnsupportedOperationException( "don't know if list is sorted and checking that is not worth it" );
257     }
258 
259 
260     public void beforeFirst() throws Exception
261     {
262         checkNotClosed( "beforeFirst()" );
263         this.index = -1;
264     }
265 
266 
267     public void afterLast() throws Exception
268     {
269         checkNotClosed( "afterLast()" );
270         this.index = end;
271     }
272 
273 
274     public boolean first() throws Exception
275     {
276         checkNotClosed( "first()" );
277 
278         if ( list.size() > 0 )
279         {
280             index = start;
281             return true;
282         }
283 
284         return false;
285     }
286 
287 
288     public boolean last() throws Exception
289     {
290         checkNotClosed( "last()" );
291 
292         if ( list.size() > 0 )
293         {
294             index = end - 1;
295             return true;
296         }
297         
298         return false;
299     }
300 
301 
302     public boolean isFirst() throws Exception
303     {
304         checkNotClosed( "isFirst()" );
305         return list.size() > 0 && index == start;
306     }
307 
308 
309     public boolean isLast() throws Exception
310     {
311         checkNotClosed( "isLast()" );
312         return list.size() > 0 && index == end - 1;
313     }
314 
315 
316     public boolean isAfterLast() throws Exception
317     {
318         checkNotClosed( "isAfterLast()" );
319         return index == end;
320     }
321 
322 
323     public boolean isBeforeFirst() throws Exception
324     {
325         checkNotClosed( "isBeforeFirst()" );
326         return index == -1;
327     }
328 
329 
330     public boolean previous() throws Exception
331     {
332         checkNotClosed( "previous()" );
333 
334         // if parked at -1 we cannot go backwards
335         if ( index == -1 )
336         {
337             return false;
338         }
339 
340         // if the index moved back is still greater than or eq to start then OK
341         if ( index - 1 >= start )
342         {
343             index--;
344             return true;
345         }
346 
347         // if the index currently less than or equal to start we need to park it at -1 and return false
348         if ( index <= start )
349         {
350             index = -1;
351             return false;
352         }
353 
354         if ( list.size() <= 0 )
355         {
356             index = -1;
357         }
358 
359         return false;
360     }
361 
362 
363     public boolean next() throws Exception
364     {
365         checkNotClosed( "next()" );
366 
367         // if parked at -1 we advance to the start index and return true
368         if ( list.size() > 0 && index == -1 )
369         {
370             index = start;
371             return true;
372         }
373 
374         // if the index plus one is less than the end then increment and return true
375         if ( list.size() > 0 && index + 1 < end )
376         {
377             index++;
378             return true;
379         }
380 
381         // if the index plus one is equal to the end then increment and return false
382         if ( list.size() > 0 && index + 1 == end )
383         {
384             index++;
385             return false;
386         }
387 
388         if ( list.size() <= 0 )
389         {
390             index = end;
391         }
392 
393         return false;
394     }
395 
396 
397     public E get() throws Exception
398     {
399         checkNotClosed( "get()" );
400         if ( index < start || index >= end )
401         {
402             throw new IOException( "Cursor not positioned at an element" );
403         }
404 
405         return list.get( index );
406     }
407 
408 
409     public boolean isElementReused()
410     {
411         return true;
412     }
413 }