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.server.core.partition.impl.btree.jdbm; 020 021 022import java.io.IOException; 023import java.util.Comparator; 024 025import jdbm.btree.BTree; 026import jdbm.helper.TupleBrowser; 027 028import org.apache.directory.api.ldap.model.constants.Loggers; 029import org.apache.directory.api.ldap.model.cursor.AbstractCursor; 030import org.apache.directory.api.ldap.model.cursor.CursorException; 031import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException; 032import org.apache.directory.api.ldap.model.cursor.Tuple; 033import org.apache.directory.api.ldap.model.exception.LdapException; 034import org.apache.directory.server.i18n.I18n; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038 039/** 040 * Cursor over a set of values for the same key which are store in another 041 * BTree. This Cursor is limited to the same key and it's tuples will always 042 * return the same key. 043 * 044 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 045 */ 046public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>> 047{ 048 /** A dedicated log for cursors */ 049 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 050 051 /** Speedup for logs */ 052 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 053 054 private final Comparator<V> comparator; 055 private final BTree btree; 056 private final K key; 057 058 private jdbm.helper.Tuple<K, V> valueTuple = new jdbm.helper.Tuple<>(); 059 private Tuple<K, V> returnedTuple = new Tuple<>(); 060 private TupleBrowser<K, V> browser; 061 private boolean valueAvailable; 062 063 064 /** 065 * Creates a Cursor over the tuples of a JDBM BTree. 066 * 067 * @param btree the JDBM BTree to build a Cursor over 068 * @param key the constant key for which values are returned 069 * @param comparator the Comparator used to determine <b>key</b> ordering 070 * @throws IOException of there are problems accessing the BTree 071 */ 072 public KeyTupleBTreeCursor( BTree btree, K key, Comparator<V> comparator ) throws IOException 073 { 074 if ( IS_DEBUG ) 075 { 076 LOG_CURSOR.debug( "Creating KeyTupleBTreeCursor {}", this ); 077 } 078 079 this.key = key; 080 this.btree = btree; 081 this.comparator = comparator; 082 this.browser = btree.browse(); 083 } 084 085 086 private void clearValue() 087 { 088 returnedTuple.setKey( key ); 089 returnedTuple.setValue( null ); 090 valueAvailable = false; 091 } 092 093 094 /** 095 * {@inheritDoc} 096 */ 097 public boolean available() 098 { 099 return valueAvailable; 100 } 101 102 103 public void beforeKey( K key ) throws Exception 104 { 105 throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) ); 106 } 107 108 109 /** 110 * {@inheritDoc} 111 */ 112 public void afterKey( K key ) throws Exception 113 { 114 throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) ); 115 } 116 117 118 /** 119 * {@inheritDoc} 120 */ 121 public void beforeValue( K key, V value ) throws Exception 122 { 123 checkNotClosed(); 124 if ( key != null && !key.equals( this.key ) ) 125 { 126 throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) ); 127 } 128 129 browser = btree.browse( value ); 130 clearValue(); 131 } 132 133 134 /** 135 * {@inheritDoc} 136 */ 137 @SuppressWarnings("unchecked") 138 public void afterValue( K key, V value ) throws CursorException 139 { 140 if ( key != null && !key.equals( this.key ) ) 141 { 142 throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) ); 143 } 144 145 try 146 { 147 browser = btree.browse( value ); 148 149 /* 150 * While the next value is less than or equal to the element keep 151 * advancing forward to the next item. If we cannot advance any 152 * further then stop and return. If we find a value greater than 153 * the element then we stop, backup, and return so subsequent calls 154 * to getNext() will return a value greater than the element. 155 */ 156 while ( browser.getNext( valueTuple ) ) 157 { 158 checkNotClosed(); 159 160 V next = ( V ) valueTuple.getKey(); 161 162 int nextCompared = comparator.compare( next, value ); 163 164 if ( nextCompared > 0 ) 165 { 166 /* 167 * If we just have values greater than the element argument 168 * then we are before the first element and cannot backup, and 169 * the call below to getPrevious() will fail. In this special 170 * case we just reset the Cursor's browser and return. 171 */ 172 if ( !browser.getPrevious( valueTuple ) ) 173 { 174 browser = btree.browse( this.key ); 175 } 176 177 clearValue(); 178 179 return; 180 } 181 } 182 183 clearValue(); 184 } 185 catch ( IOException e ) 186 { 187 throw new CursorException( e ); 188 } 189 } 190 191 192 /** 193 * Positions this Cursor over the same keys before the value of the 194 * supplied valueTuple. The supplied element Tuple's key is not considered at 195 * all. 196 * 197 * @param element the valueTuple who's value is used to position this Cursor 198 * @throws LdapException if there are failures to position the Cursor 199 * @throws CursorException if there are failures to position the Cursor 200 */ 201 public void before( Tuple<K, V> element ) throws LdapException, CursorException 202 { 203 checkNotClosed(); 204 try 205 { 206 browser = btree.browse( element.getValue() ); 207 clearValue(); 208 } 209 catch ( IOException e ) 210 { 211 throw new CursorException( e ); 212 } 213 } 214 215 216 /** 217 * {@inheritDoc} 218 */ 219 public void after( Tuple<K, V> element ) throws CursorException 220 { 221 afterValue( key, element.getValue() ); 222 } 223 224 225 /** 226 * {@inheritDoc} 227 */ 228 public void beforeFirst() throws LdapException, CursorException 229 { 230 checkNotClosed(); 231 try 232 { 233 browser = btree.browse(); 234 clearValue(); 235 } 236 catch ( IOException e ) 237 { 238 throw new CursorException( e ); 239 } 240 } 241 242 243 /** 244 * {@inheritDoc} 245 */ 246 public void afterLast() throws LdapException, CursorException 247 { 248 checkNotClosed(); 249 try 250 { 251 browser = btree.browse( null ); 252 } 253 catch ( IOException e ) 254 { 255 throw new CursorException( e ); 256 } 257 } 258 259 260 /** 261 * {@inheritDoc} 262 */ 263 public boolean first() throws LdapException, CursorException 264 { 265 beforeFirst(); 266 267 return next(); 268 } 269 270 271 /** 272 * {@inheritDoc} 273 */ 274 public boolean last() throws LdapException, CursorException 275 { 276 afterLast(); 277 278 return previous(); 279 } 280 281 282 /** 283 * {@inheritDoc} 284 */ 285 @SuppressWarnings("unchecked") 286 public boolean previous() throws LdapException, CursorException 287 { 288 checkNotClosed(); 289 290 try 291 { 292 if ( browser.getPrevious( valueTuple ) ) 293 { 294 // work around to fix direction change problem with jdbm browser 295 if ( ( returnedTuple.getValue() != null ) 296 && ( comparator.compare( ( V ) valueTuple.getKey(), returnedTuple.getValue() ) == 0 ) ) 297 { 298 browser.getPrevious( valueTuple ); 299 } 300 returnedTuple.setKey( key ); 301 returnedTuple.setValue( ( V ) valueTuple.getKey() ); 302 303 valueAvailable = true; 304 return true; 305 } 306 else 307 { 308 clearValue(); 309 310 return false; 311 } 312 } 313 catch ( IOException e ) 314 { 315 throw new CursorException( e ); 316 } 317 } 318 319 320 /** 321 * {@inheritDoc} 322 */ 323 @SuppressWarnings("unchecked") 324 public boolean next() throws LdapException, CursorException 325 { 326 checkNotClosed(); 327 328 try 329 { 330 if ( browser.getNext( valueTuple ) ) 331 { 332 // work around to fix direction change problem with jdbm browser 333 if ( returnedTuple.getValue() != null 334 && comparator.compare( ( V ) valueTuple.getKey(), returnedTuple.getValue() ) == 0 ) 335 { 336 browser.getNext( valueTuple ); 337 } 338 339 returnedTuple.setKey( key ); 340 returnedTuple.setValue( ( V ) valueTuple.getKey() ); 341 342 valueAvailable = true; 343 return true; 344 } 345 else 346 { 347 clearValue(); 348 349 return false; 350 } 351 } 352 catch ( IOException e ) 353 { 354 throw new CursorException( e ); 355 } 356 } 357 358 359 /** 360 * {@inheritDoc} 361 */ 362 public Tuple<K, V> get() throws CursorException 363 { 364 checkNotClosed(); 365 366 if ( valueAvailable ) 367 { 368 return returnedTuple; 369 } 370 371 throw new InvalidCursorPositionException(); 372 } 373 374 375 /** 376 * {@inheritDoc} 377 */ 378 @Override 379 public void close() throws IOException 380 { 381 if ( IS_DEBUG ) 382 { 383 LOG_CURSOR.debug( "Closing KeyTupleBTreeCursor {}", this ); 384 } 385 386 super.close(); 387 } 388 389 390 /** 391 * {@inheritDoc} 392 */ 393 @Override 394 public void close( Exception cause ) throws IOException 395 { 396 if ( IS_DEBUG ) 397 { 398 LOG_CURSOR.debug( "Closing KeyTupleBTreeCursor {}", this ); 399 } 400 401 super.close( cause ); 402 } 403}