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.xdbm.impl.avl; 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.Cursor; 028import org.apache.directory.api.ldap.model.cursor.CursorException; 029import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException; 030import org.apache.directory.api.ldap.model.cursor.SingletonCursor; 031import org.apache.directory.api.ldap.model.cursor.Tuple; 032import org.apache.directory.api.ldap.model.exception.LdapException; 033import org.apache.directory.server.core.avltree.AvlSingletonOrOrderedSetCursor; 034import org.apache.directory.server.core.avltree.AvlTree; 035import org.apache.directory.server.core.avltree.AvlTreeCursor; 036import org.apache.directory.server.core.avltree.SingletonOrOrderedSet; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040 041/** 042 * A Cursor which walks and advance over AvlTables that may contain duplicate 043 * keys with values stored in an AvlTree. All duplicate keys are traversed 044 * returning the key and the value in a Tuple. 045 * 046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 047 */ 048public class AvlTableDupsCursor<K, V> extends AbstractCursor<Tuple<K, V>> 049{ 050 private static final Logger LOG = LoggerFactory.getLogger( AvlTableDupsCursor.class ); 051 052 /** A dedicated log for cursors */ 053 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 054 055 /** Speedup for logs */ 056 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 057 058 /** The AVL backed table this Cursor traverses over. */ 059 private final AvlTable<K, V> table; 060 061 /** 062 * The underlying wrapped cursor which returns Tuples whose values are 063 * either V objects or AvlTree objects. 064 */ 065 private final AvlSingletonOrOrderedSetCursor<K, V> wrappedCursor; 066 067 /** 068 * A Cursor over a set of value objects for the current key held in the 069 * containerTuple. A new Cursor will be set for each new key as we 070 * traverse. The Cursor traverses over either a AvlTree object full 071 * of values in a multi-valued key or it traverses over a BTree which 072 * contains the values in the key field of it's Tuples. 073 */ 074 private Cursor<V> dupsCursor; 075 076 /** The current Tuple returned from the wrapped cursor. */ 077 private final Tuple<K, SingletonOrOrderedSet<V>> wrappedTuple = new Tuple<>(); 078 079 /** 080 * The Tuple that is used to return values via the get() method. This 081 * same Tuple instance will be returned every time. At different 082 * positions it may return different values for the same key. 083 */ 084 private final Tuple<K, V> returnedTuple = new Tuple<>(); 085 086 /** Whether or not a value is available when get() is called. */ 087 private boolean valueAvailable; 088 089 090 /** 091 * Creates a new instance of AvlTableDupsCursor. 092 * 093 * @param table the AvlTable to build a Cursor on. 094 */ 095 public AvlTableDupsCursor( AvlTable<K, V> table ) 096 { 097 if ( IS_DEBUG ) 098 { 099 LOG_CURSOR.debug( "Creating AvlTableDupsCursor {}", this ); 100 } 101 102 this.table = table; 103 this.wrappedCursor = new AvlSingletonOrOrderedSetCursor<>( table.getAvlTreeMap() ); 104 LOG.debug( "Created on table {}", table.getName() ); 105 } 106 107 108 /** 109 * {@inheritDoc} 110 */ 111 public boolean available() 112 { 113 return valueAvailable; 114 } 115 116 117 /** 118 * {@inheritDoc} 119 */ 120 public void beforeKey( K key ) throws Exception 121 { 122 beforeValue( key, null ); 123 } 124 125 126 /** 127 * {@inheritDoc} 128 */ 129 public void beforeValue( K key, V value ) throws LdapException, CursorException 130 { 131 checkNotClosed(); 132 wrappedCursor.beforeKey( key ); 133 134 if ( dupsCursor != null ) 135 { 136 try 137 { 138 dupsCursor.close(); 139 } 140 catch ( IOException ioe ) 141 { 142 throw new LdapException( ioe.getMessage(), ioe ); 143 } 144 } 145 146 if ( wrappedCursor.next() ) 147 { 148 wrappedTuple.setBoth( wrappedCursor.get() ); 149 150 if ( wrappedTuple.getValue().isOrderedSet() ) 151 { 152 AvlTree<V> avlTree = wrappedTuple.getValue().getOrderedSet(); 153 dupsCursor = new AvlTreeCursor<>( avlTree ); 154 } 155 else 156 { 157 dupsCursor = new SingletonCursor<>( 158 wrappedTuple.getValue().getSingleton(), wrappedCursor.getValuComparator() ); 159 } 160 161 if ( value == null ) 162 { 163 clearValue(); 164 return; 165 } 166 167 /* 168 * The cursor over the values is only advanced if we're on the 169 * same key as the primary cursor. This is because we want this 170 * method to really position us within a set of duplicate key 171 * entries at the proper value. 172 */ 173 if ( table.getKeyComparator().compare( wrappedTuple.getKey(), key ) == 0 ) 174 { 175 dupsCursor.before( value ); 176 } 177 178 clearValue(); 179 return; 180 } 181 182 clearValue(); 183 wrappedTuple.setKey( null ); 184 wrappedTuple.setValue( null ); 185 } 186 187 188 /** 189 * {@inheritDoc} 190 */ 191 public void afterKey( K key ) throws Exception 192 { 193 afterValue( key, null ); 194 } 195 196 197 /** 198 * {@inheritDoc} 199 */ 200 public void afterValue( K key, V value ) throws LdapException, CursorException 201 { 202 checkNotClosed(); 203 204 if ( dupsCursor != null ) 205 { 206 try 207 { 208 dupsCursor.close(); 209 } 210 catch ( IOException ioe ) 211 { 212 throw new LdapException( ioe.getMessage(), ioe ); 213 } 214 } 215 216 /* 217 * There is a subtle difference between after and before handling 218 * with dupicate key values. Say we have the following tuples: 219 * 220 * (0, 0) 221 * (1, 1) 222 * (1, 2) 223 * (1, 3) 224 * (2, 2) 225 * 226 * If we request an after cursor on (1, 2). We must make sure that 227 * the container cursor does not advance after the entry with key 1 228 * since this would result in us skip returning (1. 3) on the call to 229 * next which will incorrectly return (2, 2) instead. 230 * 231 * So if the value is null in the element then we don't care about 232 * this obviously since we just want to advance past the duplicate key 233 * values all together. But when it is not null, then we want to 234 * go right before this key instead of after it. 235 */ 236 237 if ( value == null ) 238 { 239 wrappedCursor.afterKey( key ); 240 } 241 else 242 { 243 wrappedCursor.beforeKey( key ); 244 } 245 246 if ( wrappedCursor.next() ) 247 { 248 wrappedTuple.setBoth( wrappedCursor.get() ); 249 SingletonOrOrderedSet<V> values = wrappedTuple.getValue(); 250 251 if ( values.isOrderedSet() ) 252 { 253 AvlTree<V> set = values.getOrderedSet(); 254 dupsCursor = new AvlTreeCursor<>( set ); 255 } 256 else 257 { 258 dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() ); 259 } 260 261 if ( value == null ) 262 { 263 clearValue(); 264 265 return; 266 } 267 268 // only advance the dupsCursor if we're on same key 269 if ( table.getKeyComparator().compare( wrappedTuple.getKey(), key ) == 0 ) 270 { 271 dupsCursor.after( value ); 272 } 273 274 clearValue(); 275 return; 276 } 277 278 clearValue(); 279 wrappedTuple.setKey( null ); 280 wrappedTuple.setValue( null ); 281 } 282 283 284 /** 285 * {@inheritDoc} 286 */ 287 public void after( Tuple<K, V> element ) throws LdapException, CursorException 288 { 289 afterValue( element.getKey(), element.getValue() ); 290 } 291 292 293 /** 294 * {@inheritDoc} 295 */ 296 public void afterLast() throws LdapException, CursorException 297 { 298 checkNotClosed(); 299 clearValue(); 300 wrappedCursor.afterLast(); 301 wrappedTuple.setKey( null ); 302 wrappedTuple.setValue( null ); 303 304 if ( dupsCursor != null ) 305 { 306 try 307 { 308 dupsCursor.close(); 309 } 310 catch ( IOException ioe ) 311 { 312 throw new LdapException( ioe.getMessage(), ioe ); 313 } 314 } 315 316 dupsCursor = null; 317 } 318 319 320 /** 321 * {@inheritDoc} 322 */ 323 public void before( Tuple<K, V> element ) throws LdapException, CursorException 324 { 325 beforeValue( element.getKey(), element.getValue() ); 326 } 327 328 329 /** 330 * {@inheritDoc} 331 */ 332 public void beforeFirst() throws LdapException, CursorException 333 { 334 checkNotClosed(); 335 clearValue(); 336 wrappedCursor.beforeFirst(); 337 wrappedTuple.setKey( null ); 338 wrappedTuple.setValue( null ); 339 340 if ( dupsCursor != null ) 341 { 342 try 343 { 344 dupsCursor.close(); 345 } 346 catch ( IOException ioe ) 347 { 348 throw new LdapException( ioe.getMessage(), ioe ); 349 } 350 } 351 352 dupsCursor = null; 353 } 354 355 356 /** 357 * {@inheritDoc} 358 */ 359 public boolean first() throws LdapException, CursorException 360 { 361 checkNotClosed(); 362 clearValue(); 363 364 if ( dupsCursor != null ) 365 { 366 try 367 { 368 dupsCursor.close(); 369 } 370 catch ( IOException ioe ) 371 { 372 throw new LdapException( ioe.getMessage(), ioe ); 373 } 374 } 375 376 dupsCursor = null; 377 378 if ( wrappedCursor.first() ) 379 { 380 wrappedTuple.setBoth( wrappedCursor.get() ); 381 SingletonOrOrderedSet<V> values = wrappedTuple.getValue(); 382 383 if ( values.isOrderedSet() ) 384 { 385 dupsCursor = new AvlTreeCursor<>( values.getOrderedSet() ); 386 } 387 else 388 { 389 dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() ); 390 } 391 392 /* 393 * Since only tables with duplicate keys enabled use this 394 * cursor, entries must have at least one value, and therefore 395 * call to last() will always return true. 396 */ 397 dupsCursor.first(); 398 valueAvailable = true; 399 returnedTuple.setKey( wrappedTuple.getKey() ); 400 returnedTuple.setValue( dupsCursor.get() ); 401 402 return true; 403 } 404 405 return false; 406 } 407 408 409 /** 410 * {@inheritDoc} 411 */ 412 public Tuple<K, V> get() throws CursorException 413 { 414 checkNotClosed(); 415 416 if ( !valueAvailable ) 417 { 418 throw new InvalidCursorPositionException(); 419 } 420 421 return returnedTuple; 422 } 423 424 425 /** 426 * {@inheritDoc} 427 */ 428 public boolean last() throws LdapException, CursorException 429 { 430 checkNotClosed(); 431 clearValue(); 432 433 if ( dupsCursor != null ) 434 { 435 try 436 { 437 dupsCursor.close(); 438 } 439 catch ( IOException ioe ) 440 { 441 throw new LdapException( ioe.getMessage(), ioe ); 442 } 443 } 444 445 if ( wrappedCursor.last() ) 446 { 447 wrappedTuple.setBoth( wrappedCursor.get() ); 448 SingletonOrOrderedSet<V> values = wrappedTuple.getValue(); 449 450 if ( values.isOrderedSet() ) 451 { 452 dupsCursor = new AvlTreeCursor<>( values.getOrderedSet() ); 453 } 454 else 455 { 456 dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() ); 457 } 458 459 /* 460 * Since only tables with duplicate keys enabled use this 461 * cursor, entries must have at least one value, and therefore 462 * call to last() will always return true. 463 */ 464 dupsCursor.last(); 465 valueAvailable = true; 466 returnedTuple.setKey( wrappedTuple.getKey() ); 467 returnedTuple.setValue( dupsCursor.get() ); 468 469 return true; 470 } 471 472 return false; 473 } 474 475 476 /** 477 * {@inheritDoc} 478 */ 479 public boolean next() throws LdapException, CursorException 480 { 481 checkNotClosed(); 482 483 /* 484 * If the cursor over the values of the current key is null or is 485 * extinguished then we need to advance to the next key. 486 */ 487 if ( null == dupsCursor || !dupsCursor.next() ) 488 { 489 if ( dupsCursor != null ) 490 { 491 try 492 { 493 dupsCursor.close(); 494 } 495 catch ( IOException ioe ) 496 { 497 throw new LdapException( ioe.getMessage(), ioe ); 498 } 499 } 500 501 /* 502 * If the wrappedCursor cursor has more elements we get the next 503 * key/AvlTree Tuple to work with and get a cursor over it. 504 */ 505 if ( wrappedCursor.next() ) 506 { 507 wrappedTuple.setBoth( wrappedCursor.get() ); 508 SingletonOrOrderedSet<V> values = wrappedTuple.getValue(); 509 510 if ( values.isOrderedSet() ) 511 { 512 dupsCursor = new AvlTreeCursor<>( values.getOrderedSet() ); 513 } 514 else 515 { 516 dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() ); 517 } 518 519 /* 520 * Since only tables with duplicate keys enabled use this 521 * cursor, entries must have at least one value, and therefore 522 * call to next() after bringing the cursor to beforeFirst() 523 * will always return true. 524 */ 525 dupsCursor.beforeFirst(); 526 dupsCursor.next(); 527 } 528 else 529 { 530 dupsCursor = null; 531 532 return false; 533 } 534 } 535 536 /* 537 * If we get to this point then cursor has more elements and 538 * wrappedTuple holds the Tuple containing the key and the 539 * AvlTree of values for that key which the Cursor traverses. All we 540 * need to do is populate our tuple object with the key and the value 541 * in the cursor. 542 */ 543 returnedTuple.setKey( wrappedTuple.getKey() ); 544 returnedTuple.setValue( dupsCursor.get() ); 545 546 valueAvailable = true; 547 return true; 548 } 549 550 551 /** 552 * {@inheritDoc} 553 */ 554 public boolean previous() throws LdapException, CursorException 555 { 556 checkNotClosed(); 557 /* 558 * If the cursor over the values of the current key is null or is 559 * extinguished then we need to advance to the previous key. 560 */ 561 if ( ( null == dupsCursor ) || !dupsCursor.previous() ) 562 { 563 if ( dupsCursor != null ) 564 { 565 try 566 { 567 dupsCursor.close(); 568 } 569 catch ( IOException ioe ) 570 { 571 throw new LdapException( ioe.getMessage(), ioe ); 572 } 573 } 574 575 /* 576 * If the wrappedCursor cursor has more elements we get the previous 577 * key/AvlTree Tuple to work with and get a cursor over it's 578 * values. 579 */ 580 if ( wrappedCursor.previous() ) 581 { 582 wrappedTuple.setBoth( wrappedCursor.get() ); 583 SingletonOrOrderedSet<V> values = wrappedTuple.getValue(); 584 585 if ( values.isOrderedSet() ) 586 { 587 dupsCursor = new AvlTreeCursor<>( values.getOrderedSet() ); 588 } 589 else 590 { 591 dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() ); 592 } 593 594 /* 595 * Since only tables with duplicate keys enabled use this 596 * cursor, entries must have at least one value, and therefore 597 * call to previous() after bringing the cursor to afterLast() 598 * will always return true. 599 */ 600 dupsCursor.afterLast(); 601 dupsCursor.previous(); 602 } 603 else 604 { 605 dupsCursor = null; 606 607 return false; 608 } 609 } 610 611 returnedTuple.setKey( wrappedTuple.getKey() ); 612 returnedTuple.setValue( dupsCursor.get() ); 613 614 valueAvailable = true; 615 return true; 616 } 617 618 619 @Override 620 public void close() throws IOException 621 { 622 if ( IS_DEBUG ) 623 { 624 LOG_CURSOR.debug( "Closing AvlTableDupsCursor {}", this ); 625 } 626 627 if ( dupsCursor != null ) 628 { 629 dupsCursor.close(); 630 } 631 632 wrappedCursor.close(); 633 } 634 635 636 @Override 637 public void close( Exception reason ) throws IOException 638 { 639 if ( IS_DEBUG ) 640 { 641 LOG_CURSOR.debug( "Closing AvlTableDupsCursor {}", this ); 642 } 643 644 if ( dupsCursor != null ) 645 { 646 dupsCursor.close(); 647 } 648 649 wrappedCursor.close(); 650 } 651 652 653 /** 654 * Clears the returned Tuple and makes sure valueAvailable returns false. 655 */ 656 private void clearValue() 657 { 658 returnedTuple.setKey( null ); 659 returnedTuple.setValue( null ); 660 valueAvailable = false; 661 } 662}