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 */ 020package org.apache.directory.server.core.avltree; 021 022 023import java.io.IOException; 024 025import org.apache.directory.api.ldap.model.constants.Loggers; 026import org.apache.directory.api.ldap.model.cursor.AbstractCursor; 027import org.apache.directory.api.ldap.model.cursor.CursorException; 028import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException; 029import org.apache.directory.api.ldap.model.exception.LdapException; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033 034/** 035 * A Cursor for an ArrayTree. 036 * 037 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 038 */ 039public class ArrayTreeCursor<E> extends AbstractCursor<E> 040{ 041 /** A dedicated log for cursors */ 042 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 043 044 /** Speedup for logs */ 045 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 046 047 /** The underlying ArrayTree */ 048 private ArrayTree<E> array; 049 050 /** The current position/index in the array */ 051 private int current; 052 053 /** The current position of this cursor, relative to the node */ 054 private Position position; 055 056 057 /** 058 * Create a cursor on an ArrayTree 059 * @param array The array we want a cursor for 060 */ 061 public ArrayTreeCursor( ArrayTree<E> array ) 062 { 063 if ( IS_DEBUG ) 064 { 065 LOG_CURSOR.debug( "Creating ArrayTreeCursor {}", this ); 066 } 067 068 this.array = array; 069 position = Position.BEFORE_FIRST; 070 } 071 072 073 /** 074 * {@inheritDoc} 075 */ 076 public void after( E element ) throws LdapException, CursorException 077 { 078 checkNotClosed(); 079 080 if ( element == null ) 081 { 082 afterLast(); 083 return; 084 } 085 086 current = array.getAfterPosition( element ); 087 088 if ( current == -1 ) 089 { 090 // As the element has not been found, we move after the last position 091 position = Position.AFTER_LAST; 092 } 093 else 094 { 095 // the cursor should be positioned after the given element 096 // we just fetched the next greater element so the cursor 097 // is positioned before the fetched element 098 position = Position.BEFORE_NODE; 099 } 100 } 101 102 103 /** 104 * {@inheritDoc} 105 */ 106 public void afterLast() throws LdapException, CursorException 107 { 108 checkNotClosed(); 109 110 current = -1; 111 position = Position.AFTER_LAST; 112 } 113 114 115 /** 116 * {@inheritDoc} 117 */ 118 public boolean available() 119 { 120 return position == Position.ON_NODE; 121 } 122 123 124 /** 125 * {@inheritDoc} 126 */ 127 public void before( E element ) throws LdapException, CursorException 128 { 129 checkNotClosed(); 130 131 if ( element == null ) 132 { 133 beforeFirst(); 134 return; 135 } 136 137 current = array.getBeforePosition( element ); 138 139 if ( current == -1 ) 140 { 141 // If the element has not been found, move to thea first position 142 position = Position.BEFORE_FIRST; 143 } 144 else 145 { 146 // the cursor should be positioned before the given element 147 // we just fetched the next less element so the cursor 148 // is positioned after the fetched element 149 position = Position.AFTER_NODE; 150 } 151 } 152 153 154 /** 155 * {@inheritDoc} 156 */ 157 public void beforeFirst() throws LdapException, CursorException 158 { 159 checkNotClosed(); 160 161 current = -1; 162 position = Position.BEFORE_FIRST; 163 } 164 165 166 /** 167 * {@inheritDoc} 168 */ 169 public boolean first() throws LdapException, CursorException 170 { 171 checkNotClosed(); 172 173 if ( array.isEmpty() ) 174 { 175 current = -1; 176 position = Position.BEFORE_FIRST; 177 return false; 178 } 179 else 180 { 181 current = 0; 182 position = Position.ON_NODE; 183 return true; 184 } 185 } 186 187 188 /** 189 * {@inheritDoc} 190 */ 191 public E get() throws CursorException 192 { 193 checkNotClosed(); 194 195 if ( position == Position.ON_NODE ) 196 { 197 return array.get( current ); 198 } 199 200 throw new InvalidCursorPositionException(); 201 } 202 203 204 /** 205 * {@inheritDoc} 206 */ 207 public boolean last() throws LdapException, CursorException 208 { 209 checkNotClosed(); 210 211 if ( array.isEmpty() ) 212 { 213 current = -1; 214 position = Position.AFTER_LAST; 215 return false; 216 } 217 else 218 { 219 current = array.size() - 1; 220 position = Position.ON_NODE; 221 return true; 222 } 223 } 224 225 226 /** 227 * {@inheritDoc} 228 */ 229 public boolean next() throws LdapException, CursorException 230 { 231 checkNotClosed(); 232 233 // If the array is empty, return false 234 if ( array.size() == 0 ) 235 { 236 return false; 237 } 238 239 switch ( position ) 240 { 241 case BEFORE_FIRST: 242 return first(); 243 244 case BEFORE_NODE: 245 position = Position.ON_NODE; 246 return true; 247 248 case ON_NODE: 249 case AFTER_NODE: 250 current++; 251 if ( current > array.size() - 1 ) 252 { 253 afterLast(); 254 return false; 255 } 256 else 257 { 258 position = Position.ON_NODE; 259 return true; 260 } 261 262 case AFTER_LAST: 263 return false; 264 265 default: 266 throw new IllegalStateException( "Unexpected position " + position ); 267 } 268 } 269 270 271 /** 272 * {@inheritDoc} 273 */ 274 public boolean previous() throws LdapException, CursorException 275 { 276 checkNotClosed(); 277 278 if ( array.size() == 0 ) 279 { 280 return false; 281 } 282 283 switch ( position ) 284 { 285 case BEFORE_FIRST: 286 return false; 287 288 case BEFORE_NODE: 289 case ON_NODE: 290 current--; 291 if ( current < 0 ) 292 { 293 beforeFirst(); 294 return false; 295 } 296 else 297 { 298 position = Position.ON_NODE; 299 return true; 300 } 301 302 case AFTER_NODE: 303 position = Position.ON_NODE; 304 return true; 305 306 case AFTER_LAST: 307 return last(); 308 309 default: 310 throw new IllegalStateException( "Unexpected position " + position ); 311 } 312 } 313 314 315 /** 316 * {@inheritDoc} 317 */ 318 @Override 319 public void close() throws IOException 320 { 321 if ( IS_DEBUG ) 322 { 323 LOG_CURSOR.debug( "Closing ArrayTreeCursor {}", this ); 324 } 325 326 super.close(); 327 } 328 329 330 /** 331 * {@inheritDoc} 332 */ 333 @Override 334 public void close( Exception reason ) throws IOException 335 { 336 if ( IS_DEBUG ) 337 { 338 LOG_CURSOR.debug( "Closing ArrayTreeCursor {}", this ); 339 } 340 341 super.close( reason ); 342 } 343 344 345 /** 346 * @see Object#toString() 347 */ 348 @Override 349 public String toString( String tabs ) 350 { 351 StringBuilder sb = new StringBuilder(); 352 353 sb.append( tabs ).append( "ArrayTreeCursor (" ); 354 355 if ( available() ) 356 { 357 sb.append( "available)" ); 358 sb.append( "#<" ).append( current ).append( ":" ).append( array.get( current ) ).append( ">" ); 359 } 360 else 361 { 362 sb.append( "absent)" ); 363 } 364 365 return sb.toString(); 366 } 367 368 369 /** 370 * @see Object#toString() 371 */ 372 public String toString() 373 { 374 return toString( "" ); 375 } 376}