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 */ 019package org.apache.directory.api.ldap.model.cursor; 020 021 022import java.util.Collections; 023import java.util.Comparator; 024import java.util.List; 025 026import org.apache.directory.api.i18n.I18n; 027import org.apache.directory.api.ldap.model.constants.Loggers; 028import org.apache.directory.api.ldap.model.exception.LdapException; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032 033/** 034 * A simple implementation of a Cursor on a {@link List}. Optionally, the 035 * Cursor may be limited to a specific range within the list. 036 * 037 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 038 * @param <E> The element on which this cursor will iterate 039 */ 040public class ListCursor<E> extends AbstractCursor<E> 041{ 042 /** A dedicated log for cursors */ 043 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 044 045 /** Speedup for logs */ 046 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 047 048 /** The inner List */ 049 private final List<E> list; 050 051 /** The associated comparator */ 052 private final Comparator<E> comparator; 053 054 /** The starting position for the cursor in the list. It can be > 0 */ 055 private final int start; 056 057 /** The ending position for the cursor in the list. It can be < List.size() */ 058 private final int end; 059 /** The current position in the list */ 060 061 private int index = -1; 062 063 064 /** 065 * Creates a new ListCursor with lower (inclusive) and upper (exclusive) 066 * bounds. 067 * 068 * As with all Cursors, this ListCursor requires a successful return from 069 * advance operations (next() or previous()) to properly return values 070 * using the get() operation. 071 * 072 * @param comparator an optional comparator to use for ordering 073 * @param start the lower bound index 074 * @param list the list this ListCursor operates on 075 * @param end the upper bound index 076 */ 077 public ListCursor( Comparator<E> comparator, int start, List<E> list, int end ) 078 { 079 if ( list == null ) 080 { 081 list = Collections.emptyList(); 082 } 083 084 if ( ( start < 0 ) || ( start > list.size() ) ) 085 { 086 throw new IllegalArgumentException( I18n.err( I18n.ERR_02005_START_INDEX_OUT_OF_RANGE, start ) ); 087 } 088 089 if ( ( end < 0 ) || ( end > list.size() ) ) 090 { 091 throw new IllegalArgumentException( I18n.err( I18n.ERR_02006_END_INDEX_OUT_OF_RANGE, end ) ); 092 } 093 094 // check list is not empty list since the empty list is the only situation 095 // where we allow for start to equal the end: in other cases it makes no sense 096 if ( ( list.size() > 0 ) && ( start >= end ) ) 097 { 098 throw new IllegalArgumentException( I18n.err( I18n.ERR_02007_START_INDEX_ABOVE_END_INDEX, start, end ) ); 099 } 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}