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 }