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