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 }