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.io.IOException; 023import java.util.Collections; 024import java.util.Comparator; 025import java.util.Set; 026 027import org.apache.directory.api.i18n.I18n; 028import org.apache.directory.api.ldap.model.constants.Loggers; 029import org.apache.directory.api.ldap.model.exception.LdapException; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033 034/** 035 * A simple implementation of a Cursor on a {@link Set}. Optionally, the 036 * Cursor may be limited to a specific range within the list. 037 * 038 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 039 * @param <E> The element on which this cursor will iterate 040 */ 041public class SetCursor<E> extends AbstractCursor<E> 042{ 043 /** A dedicated log for cursors */ 044 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 045 046 /** Speedup for logs */ 047 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 048 049 /** The inner Set */ 050 private final E[] set; 051 052 /** The associated comparator */ 053 private final Comparator<E> comparator; 054 055 /** The current position in the list */ 056 private int index = -1; 057 058 /** A limit to what we can print */ 059 private static final int MAX_PRINTED_ELEMENT = 100; 060 061 062 /** 063 * Creates a new SetCursor. 064 * 065 * As with all Cursors, this SetCursor requires a successful return from 066 * advance operations (next() or previous()) to properly return values 067 * using the get() operation. 068 * 069 * @param comparator an optional comparator to use for ordering 070 * @param set the Set this StCursor operates on 071 */ 072 @SuppressWarnings("unchecked") 073 public SetCursor( Comparator<E> comparator, Set<E> set ) 074 { 075 if ( set == null ) 076 { 077 set = Collections.EMPTY_SET; 078 } 079 080 if ( IS_DEBUG ) 081 { 082 LOG_CURSOR.debug( "Creating SetCursor {}", this ); 083 } 084 085 this.comparator = comparator; 086 this.set = ( E[] ) set.toArray(); 087 } 088 089 090 /** 091 * Creates a new SetCursor 092 * 093 * As with all Cursors, this SetCursor requires a successful return from 094 * advance operations (next() or previous()) to properly return values 095 * using the get() operation. 096 * 097 * @param set the Set this SetCursor operates on 098 */ 099 public SetCursor( Set<E> set ) 100 { 101 this( null, set ); 102 } 103 104 105 /** 106 * Creates a new SetCursor without any elements. 107 */ 108 @SuppressWarnings("unchecked") 109 public SetCursor() 110 { 111 this( null, Collections.EMPTY_SET ); 112 } 113 114 115 /** 116 * Creates a new SetCursor without any elements. We also provide 117 * a comparator. 118 * 119 * @param comparator The comparator to use for the <E> elements 120 */ 121 @SuppressWarnings("unchecked") 122 public SetCursor( Comparator<E> comparator ) 123 { 124 this( comparator, Collections.EMPTY_SET ); 125 } 126 127 128 /** 129 * {@inheritDoc} 130 */ 131 @Override 132 public boolean available() 133 { 134 return ( index >= 0 ) && ( index < set.length ); 135 } 136 137 138 /** 139 * {@inheritDoc} 140 */ 141 @Override 142 public void before( E element ) throws LdapException, CursorException 143 { 144 checkNotClosed( "before()" ); 145 146 if ( comparator == null ) 147 { 148 throw new IllegalStateException(); 149 } 150 151 // handle some special cases 152 if ( set.length == 0 ) 153 { 154 return; 155 } 156 else if ( set.length == 1 ) 157 { 158 if ( comparator.compare( element, set[0] ) <= 0 ) 159 { 160 beforeFirst(); 161 } 162 else 163 { 164 afterLast(); 165 } 166 } 167 168 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008_LIST_MAY_BE_SORTED ) ); 169 } 170 171 172 /** 173 * {@inheritDoc} 174 */ 175 @Override 176 public void after( E element ) throws LdapException, CursorException 177 { 178 checkNotClosed( "after()" ); 179 180 if ( comparator == null ) 181 { 182 throw new IllegalStateException(); 183 } 184 185 // handle some special cases 186 if ( set.length == 0 ) 187 { 188 return; 189 } 190 else if ( set.length == 1 ) 191 { 192 if ( comparator.compare( element, set[0] ) >= 0 ) 193 { 194 afterLast(); 195 } 196 else 197 { 198 beforeFirst(); 199 } 200 } 201 202 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008_LIST_MAY_BE_SORTED ) ); 203 } 204 205 206 /** 207 * {@inheritDoc} 208 */ 209 @Override 210 public void beforeFirst() throws LdapException, CursorException 211 { 212 checkNotClosed( "beforeFirst()" ); 213 this.index = -1; 214 } 215 216 217 /** 218 * {@inheritDoc} 219 */ 220 @Override 221 public void afterLast() throws LdapException, CursorException 222 { 223 checkNotClosed( "afterLast()" ); 224 this.index = set.length; 225 } 226 227 228 /** 229 * {@inheritDoc} 230 */ 231 @Override 232 public boolean first() throws LdapException, CursorException 233 { 234 checkNotClosed( "first()" ); 235 236 if ( set.length > 0 ) 237 { 238 index = 0; 239 240 return true; 241 } 242 243 return false; 244 } 245 246 247 /** 248 * {@inheritDoc} 249 */ 250 @Override 251 public boolean last() throws LdapException, CursorException 252 { 253 checkNotClosed( "last()" ); 254 255 if ( set.length > 0 ) 256 { 257 index = set.length - 1; 258 259 return true; 260 } 261 262 return false; 263 } 264 265 266 /** 267 * {@inheritDoc} 268 */ 269 @Override 270 public boolean isFirst() 271 { 272 return ( set.length > 0 ) && ( index == 0 ); 273 } 274 275 276 /** 277 * {@inheritDoc} 278 */ 279 @Override 280 public boolean isLast() 281 { 282 return ( set.length > 0 ) && ( index == set.length - 1 ); 283 } 284 285 286 /** 287 * {@inheritDoc} 288 */ 289 @Override 290 public boolean isAfterLast() 291 { 292 return index == set.length; 293 } 294 295 296 /** 297 * {@inheritDoc} 298 */ 299 @Override 300 public boolean isBeforeFirst() 301 { 302 return index == -1; 303 } 304 305 306 /** 307 * {@inheritDoc} 308 */ 309 @Override 310 public boolean previous() throws LdapException, CursorException 311 { 312 checkNotClosed( "previous()" ); 313 314 // if parked at -1 we cannot go backwards 315 if ( index == -1 ) 316 { 317 return false; 318 } 319 320 // if the index moved back is still greater than or eq to start then OK 321 if ( index - 1 >= 0 ) 322 { 323 index--; 324 325 return true; 326 } 327 328 // if the index currently less than or equal to start we need to park it at -1 and return false 329 if ( index <= 0 ) 330 { 331 index = -1; 332 333 return false; 334 } 335 336 if ( set.length <= 0 ) 337 { 338 index = -1; 339 } 340 341 return false; 342 } 343 344 345 /** 346 * {@inheritDoc} 347 */ 348 @Override 349 public boolean next() throws LdapException, CursorException 350 { 351 checkNotClosed( "next()" ); 352 353 // if parked at -1 we advance to the start index and return true 354 if ( ( set.length > 0 ) && ( index == -1 ) ) 355 { 356 index = 0; 357 358 return true; 359 } 360 361 // if the index plus one is less than the end then increment and return true 362 if ( ( set.length > 0 ) && ( index + 1 < set.length ) ) 363 { 364 index++; 365 366 return true; 367 } 368 369 // if the index plus one is equal to the end then increment and return false 370 if ( ( set.length > 0 ) && ( index + 1 == set.length ) ) 371 { 372 index++; 373 374 return false; 375 } 376 377 if ( set.length <= 0 ) 378 { 379 index = set.length; 380 } 381 382 return false; 383 } 384 385 386 /** 387 * {@inheritDoc} 388 */ 389 @Override 390 public E get() throws CursorException 391 { 392 checkNotClosed( "get()" ); 393 394 if ( ( index < 0 ) || ( index >= set.length ) ) 395 { 396 throw new CursorException( I18n.err( I18n.ERR_02009_CURSOR_NOT_POSITIONED ) ); 397 } 398 399 return set[index]; 400 } 401 402 403 /** 404 * {@inheritDoc} 405 */ 406 @Override 407 public void close() throws IOException 408 { 409 if ( IS_DEBUG ) 410 { 411 LOG_CURSOR.debug( "Closing ListCursor {}", this ); 412 } 413 414 super.close(); 415 } 416 417 418 /** 419 * {@inheritDoc} 420 */ 421 @Override 422 public void close( Exception cause ) throws IOException 423 { 424 if ( IS_DEBUG ) 425 { 426 LOG_CURSOR.debug( "Closing ListCursor {}", this ); 427 } 428 429 super.close( cause ); 430 } 431 432 433 /** 434 * @see Object#toString() 435 */ 436 @Override 437 public String toString( String tabs ) 438 { 439 StringBuilder sb = new StringBuilder(); 440 441 sb.append( tabs ).append( "SetCursor :\n" ); 442 sb.append( tabs ).append( " Index : " ).append( index ).append( "\n" ); 443 444 if ( ( set != null ) && ( set.length > 0 ) ) 445 { 446 sb.append( tabs ).append( " Size : " ).append( set.length ).append( "\n" ); 447 448 // Don't print more than 100 elements... 449 int counter = 0; 450 451 for ( E e : set ) 452 { 453 sb.append( tabs ).append( " " ).append( e ).append( "\n" ); 454 counter++; 455 456 if ( counter == MAX_PRINTED_ELEMENT ) 457 { 458 break; 459 } 460 } 461 } 462 463 return sb.toString(); 464 } 465 466 467 /** 468 * @see Object#toString() 469 */ 470 @Override 471 public String toString() 472 { 473 return toString( "" ); 474 } 475}