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.Tuple; 027import jdbm.helper.TupleBrowser; 028 029import org.apache.directory.api.ldap.model.constants.Loggers; 030import org.apache.directory.api.ldap.model.cursor.AbstractCursor; 031import org.apache.directory.api.ldap.model.cursor.CursorException; 032import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException; 033import org.apache.directory.api.ldap.model.exception.LdapException; 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036 037 038/** 039 * Cursor over the keys of a JDBM BTree. Obviously does not return duplicate 040 * keys since JDBM does not natively support multiple values for the same key. 041 * 042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 043 */ 044public class KeyBTreeCursor<E> extends AbstractCursor<E> 045{ 046 /** A dedicated log for cursors */ 047 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 048 049 /** Speedup for logs */ 050 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 051 052 private final Tuple tuple = new Tuple(); 053 054 private final BTree btree; 055 private final Comparator<E> comparator; 056 private boolean valueAvailable; 057 private TupleBrowser browser; 058 059 060 /** 061 * Creates a Cursor over the keys of a JDBM BTree. 062 * 063 * @param btree the JDBM BTree to build a Cursor over 064 * @param comparator the Comparator used to determine key ordering 065 */ 066 public KeyBTreeCursor( BTree btree, Comparator<E> comparator ) 067 { 068 if ( IS_DEBUG ) 069 { 070 LOG_CURSOR.debug( "Creating KeyBTreeCursor {}", this ); 071 } 072 073 this.btree = btree; 074 this.comparator = comparator; 075 } 076 077 078 private void clearValue() 079 { 080 tuple.setKey( null ); 081 tuple.setValue( null ); 082 valueAvailable = false; 083 } 084 085 086 public boolean available() 087 { 088 return valueAvailable; 089 } 090 091 092 /** 093 * {@inheritDoc} 094 */ 095 public void before( E element ) throws LdapException, CursorException 096 { 097 checkNotClosed(); 098 try 099 { 100 browser = btree.browse( element ); 101 } 102 catch ( IOException e ) 103 { 104 throw new CursorException( e ); 105 } 106 clearValue(); 107 } 108 109 110 /** 111 * {@inheritDoc} 112 */ 113 @SuppressWarnings("unchecked") 114 public void after( E element ) throws LdapException, CursorException 115 { 116 try 117 { 118 browser = btree.browse( element ); 119 120 /* 121 * While the next value is less than or equal to the element keep 122 * advancing forward to the next item. If we cannot advance any 123 * further then stop and return. If we find a value greater than 124 * the element then we stop, backup, and return so subsequent calls 125 * to getNext() will return a value greater than the element. 126 */ 127 while ( browser.getNext( tuple ) ) 128 { 129 checkNotClosed(); 130 E next = ( E ) tuple.getKey(); 131 int nextCompared = comparator.compare( next, element ); 132 133 if ( nextCompared > 0 ) 134 { 135 /* 136 * If we just have values greater than the element argument 137 * then we are before the first element and must backup to 138 * before the first element state for the JDBM browser which 139 * apparently the browser supports. 140 */ 141 browser.getPrevious( tuple ); 142 clearValue(); 143 return; 144 } 145 } 146 147 clearValue(); 148 // just return 149 } 150 catch ( IOException e ) 151 { 152 throw new CursorException( e ); 153 } 154 } 155 156 157 /** 158 * {@inheritDoc} 159 */ 160 public void beforeFirst() throws LdapException, CursorException 161 { 162 checkNotClosed(); 163 try 164 { 165 browser = btree.browse(); 166 clearValue(); 167 } 168 catch ( IOException e ) 169 { 170 throw new CursorException( e ); 171 } 172 } 173 174 175 /** 176 * {@inheritDoc} 177 */ 178 public void afterLast() throws LdapException, CursorException 179 { 180 checkNotClosed(); 181 try 182 { 183 browser = btree.browse( null ); 184 } 185 catch ( IOException e ) 186 { 187 throw new CursorException( e ); 188 } 189 } 190 191 192 /** 193 * {@inheritDoc} 194 */ 195 public boolean first() throws LdapException, CursorException 196 { 197 beforeFirst(); 198 return next(); 199 } 200 201 202 /** 203 * {@inheritDoc} 204 */ 205 public boolean last() throws LdapException, CursorException 206 { 207 afterLast(); 208 return previous(); 209 } 210 211 212 /** 213 * {@inheritDoc} 214 */ 215 public boolean previous() throws LdapException, CursorException 216 { 217 checkNotClosed(); 218 219 try 220 { 221 if ( browser == null ) 222 { 223 browser = btree.browse( null ); 224 } 225 226 if ( browser.getPrevious( tuple ) ) 227 { 228 valueAvailable = true; 229 return true; 230 } 231 else 232 { 233 clearValue(); 234 return false; 235 } 236 } 237 catch ( IOException e ) 238 { 239 throw new CursorException( e ); 240 } 241 } 242 243 244 /** 245 * {@inheritDoc} 246 */ 247 public boolean next() throws LdapException, CursorException 248 { 249 checkNotClosed(); 250 251 try 252 { 253 if ( browser == null ) 254 { 255 browser = btree.browse(); 256 } 257 258 if ( browser.getNext( tuple ) ) 259 { 260 valueAvailable = true; 261 return true; 262 } 263 else 264 { 265 clearValue(); 266 267 return false; 268 } 269 } 270 catch ( IOException e ) 271 { 272 throw new CursorException( e ); 273 } 274 } 275 276 277 /** 278 * {@inheritDoc} 279 */ 280 public E get() throws CursorException 281 { 282 checkNotClosed(); 283 284 if ( valueAvailable ) 285 { 286 return ( E ) tuple.getKey(); 287 } 288 289 throw new InvalidCursorPositionException(); 290 } 291 292 293 /** 294 * {@inheritDoc} 295 */ 296 @Override 297 public void close() throws IOException 298 { 299 if ( IS_DEBUG ) 300 { 301 LOG_CURSOR.debug( "Closing KeyBTreeCursor {}", this ); 302 } 303 304 super.close(); 305 } 306 307 308 /** 309 * {@inheritDoc} 310 */ 311 @Override 312 public void close( Exception cause ) throws IOException 313 { 314 if ( IS_DEBUG ) 315 { 316 LOG_CURSOR.debug( "Closing KeyBTreeCursor {}", this ); 317 } 318 319 super.close( cause ); 320 } 321}