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.shared.ldap.model.cursor; 020 021 022import java.io.IOException; 023import java.util.Collections; 024import java.util.Comparator; 025import java.util.List; 026 027import 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 * @param <E> The element on which this cursor will iterate 036 */ 037public 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 ( list == null ) 071 { 072 list = Collections.emptyList(); 073 } 074 075 if ( ( start < 0 ) || ( start > list.size() ) ) 076 { 077 throw new IllegalArgumentException( I18n.err( I18n.ERR_02005_START_INDEX_OUT_OF_RANGE, start ) ); 078 } 079 080 if ( ( end < 0 ) || ( end > list.size() ) ) 081 { 082 throw new IllegalArgumentException( I18n.err( I18n.ERR_02006_END_INDEX_OUT_OF_RANGE, end ) ); 083 } 084 085 // check list is not empty list since the empty list is the only situation 086 // where we allow for start to equal the end: in other cases it makes no sense 087 if ( ( list.size() > 0 ) && ( start >= end ) ) 088 { 089 throw new IllegalArgumentException( I18n.err( I18n.ERR_02007_START_INDEX_ABOVE_END_INDEX, start, end ) ); 090 } 091 092 this.comparator = comparator; 093 this.list = list; 094 this.start = start; 095 this.end = end; 096 } 097 098 099 /** 100 * Creates a new ListCursor with lower (inclusive) and upper (exclusive) 101 * bounds. 102 * 103 * As with all Cursors, this ListCursor requires a successful return from 104 * advance operations (next() or previous()) to properly return values 105 * using the get() operation. 106 * 107 * @param start the lower bound index 108 * @param list the list this ListCursor operates on 109 * @param end the upper bound index 110 */ 111 public ListCursor( int start, List<E> list, int end ) 112 { 113 this( null, start, list, end ); 114 } 115 116 117 /** 118 * Creates a new ListCursor with a specific upper (exclusive) bound: the 119 * lower (inclusive) bound defaults to 0. 120 * 121 * @param list the backing for this ListCursor 122 * @param end the upper bound index representing the position after the 123 * last element 124 */ 125 public ListCursor( List<E> list, int end ) 126 { 127 this( null, 0, 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. We also provide a comparator. 134 * 135 * @param comparator The comparator to use for the <E> elements 136 * @param list the backing for this ListCursor 137 * @param end the upper bound index representing the position after the 138 * last element 139 */ 140 public ListCursor( Comparator<E> comparator, List<E> list, int end ) 141 { 142 this( comparator, 0, list, end ); 143 } 144 145 146 /** 147 * Creates a new ListCursor with a lower (inclusive) bound: the upper 148 * (exclusive) bound is the size of the list. 149 * 150 * @param start the lower (inclusive) bound index: the position of the 151 * first entry 152 * @param list the backing for this ListCursor 153 */ 154 public ListCursor( int start, List<E> list ) 155 { 156 this( null, start, list, list.size() ); 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. We also provide a comparator. 163 * 164 * @param comparator The comparator to use for the <E> elements 165 * @param start the lower (inclusive) bound index: the position of the 166 * first entry 167 * @param list the backing for this ListCursor 168 */ 169 public ListCursor( Comparator<E> comparator, int start, List<E> list ) 170 { 171 this( comparator, start, list, list.size() ); 172 } 173 174 175 /** 176 * Creates a new ListCursor without specific bounds: the bounds are 177 * acquired from the size of the list. 178 * 179 * @param list the backing for this ListCursor 180 */ 181 public ListCursor( List<E> list ) 182 { 183 this( null, 0, list, list.size() ); 184 } 185 186 187 /** 188 * Creates a new ListCursor without specific bounds: the bounds are 189 * acquired from the size of the list. We also provide a comparator. 190 * 191 * @param comparator The comparator to use for the <E> elements 192 * @param list the backing for this ListCursor 193 */ 194 public ListCursor( Comparator<E> comparator, List<E> list ) 195 { 196 this( comparator, 0, list, list.size() ); 197 } 198 199 200 /** 201 * Creates a new ListCursor without any elements. 202 */ 203 @SuppressWarnings("unchecked") 204 public ListCursor() 205 { 206 this( null, 0, Collections.EMPTY_LIST, 0 ); 207 } 208 209 210 /** 211 * Creates a new ListCursor without any elements. We also provide 212 * a comparator. 213 * 214 * @param comparator The comparator to use for the <E> elements 215 */ 216 @SuppressWarnings("unchecked") 217 public ListCursor( Comparator<E> comparator ) 218 { 219 this( comparator, 0, Collections.EMPTY_LIST, 0 ); 220 } 221 222 223 /** 224 * {@inheritDoc} 225 */ 226 public boolean available() 227 { 228 return index >= 0 && index < end; 229 } 230 231 232 /** 233 * {@inheritDoc} 234 */ 235 public void before( E element ) throws Exception 236 { 237 checkNotClosed( "before()" ); 238 239 if ( comparator == null ) 240 { 241 throw new IllegalStateException(); 242 } 243 244 // handle some special cases 245 if ( list.size() == 0 ) 246 { 247 return; 248 } 249 else if ( list.size() == 1 ) 250 { 251 if ( comparator.compare( element, list.get( 0 ) ) <= 0 ) 252 { 253 beforeFirst(); 254 } 255 else 256 { 257 afterLast(); 258 } 259 } 260 261 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008_LIST_MAY_BE_SORTED ) ); 262 } 263 264 265 /** 266 * {@inheritDoc} 267 */ 268 public void after( E element ) throws Exception 269 { 270 checkNotClosed( "after()" ); 271 272 if ( comparator == null ) 273 { 274 throw new IllegalStateException(); 275 } 276 277 // handle some special cases 278 if ( list.size() == 0 ) 279 { 280 return; 281 } 282 else if ( list.size() == 1 ) 283 { 284 if ( comparator.compare( element, list.get( 0 ) ) >= 0 ) 285 { 286 afterLast(); 287 } 288 else 289 { 290 beforeFirst(); 291 } 292 } 293 294 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008_LIST_MAY_BE_SORTED ) ); 295 } 296 297 298 /** 299 * {@inheritDoc} 300 */ 301 public void beforeFirst() throws Exception 302 { 303 checkNotClosed( "beforeFirst()" ); 304 this.index = -1; 305 } 306 307 308 /** 309 * {@inheritDoc} 310 */ 311 public void afterLast() throws Exception 312 { 313 checkNotClosed( "afterLast()" ); 314 this.index = end; 315 } 316 317 318 /** 319 * {@inheritDoc} 320 */ 321 public boolean first() throws Exception 322 { 323 checkNotClosed( "first()" ); 324 325 if ( list.size() > 0 ) 326 { 327 index = start; 328 329 return true; 330 } 331 332 return false; 333 } 334 335 336 /** 337 * {@inheritDoc} 338 */ 339 public boolean last() throws Exception 340 { 341 checkNotClosed( "last()" ); 342 343 if ( list.size() > 0 ) 344 { 345 index = end - 1; 346 347 return true; 348 } 349 350 return false; 351 } 352 353 354 /** 355 * {@inheritDoc} 356 */ 357 @Override 358 public boolean isFirst() throws Exception 359 { 360 checkNotClosed( "isFirst()" ); 361 362 return list.size() > 0 && index == start; 363 } 364 365 366 /** 367 * {@inheritDoc} 368 */ 369 @Override 370 public boolean isLast() throws Exception 371 { 372 checkNotClosed( "isLast()" ); 373 374 return list.size() > 0 && index == end - 1; 375 } 376 377 378 /** 379 * {@inheritDoc} 380 */ 381 @Override 382 public boolean isAfterLast() throws Exception 383 { 384 checkNotClosed( "isAfterLast()" ); 385 return index == end; 386 } 387 388 389 /** 390 * {@inheritDoc} 391 */ 392 @Override 393 public boolean isBeforeFirst() throws Exception 394 { 395 checkNotClosed( "isBeforeFirst()" ); 396 return index == -1; 397 } 398 399 400 /** 401 * {@inheritDoc} 402 */ 403 public boolean previous() throws Exception 404 { 405 checkNotClosed( "previous()" ); 406 407 // if parked at -1 we cannot go backwards 408 if ( index == -1 ) 409 { 410 return false; 411 } 412 413 // if the index moved back is still greater than or eq to start then OK 414 if ( index - 1 >= start ) 415 { 416 index--; 417 418 return true; 419 } 420 421 // if the index currently less than or equal to start we need to park it at -1 and return false 422 if ( index <= start ) 423 { 424 index = -1; 425 426 return false; 427 } 428 429 if ( list.size() <= 0 ) 430 { 431 index = -1; 432 } 433 434 return false; 435 } 436 437 438 /** 439 * {@inheritDoc} 440 */ 441 public boolean next() throws Exception 442 { 443 checkNotClosed( "next()" ); 444 445 // if parked at -1 we advance to the start index and return true 446 if ( ( list.size() > 0 ) && ( index == -1 ) ) 447 { 448 index = start; 449 450 return true; 451 } 452 453 // if the index plus one is less than the end then increment and return true 454 if ( ( list.size() > 0 ) && ( index + 1 < end ) ) 455 { 456 index++; 457 458 return true; 459 } 460 461 // if the index plus one is equal to the end then increment and return false 462 if ( ( list.size() > 0 ) && ( index + 1 == end ) ) 463 { 464 index++; 465 466 return false; 467 } 468 469 if ( list.size() <= 0 ) 470 { 471 index = end; 472 } 473 474 return false; 475 } 476 477 478 /** 479 * {@inheritDoc} 480 */ 481 public E get() throws Exception 482 { 483 checkNotClosed( "get()" ); 484 485 if ( ( index < start ) || ( index >= end ) ) 486 { 487 throw new IOException( I18n.err( I18n.ERR_02009_CURSOR_NOT_POSITIONED ) ); 488 } 489 490 return list.get( index ); 491 } 492}