View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.directory.server.core.partition.impl.btree.jdbm;
20  
21  
22  import java.io.IOException;
23  import java.util.Comparator;
24  
25  import jdbm.btree.BTree;
26  import jdbm.helper.TupleBrowser;
27  
28  import org.apache.directory.api.ldap.model.constants.Loggers;
29  import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
30  import org.apache.directory.api.ldap.model.cursor.CursorException;
31  import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
32  import org.apache.directory.api.ldap.model.cursor.Tuple;
33  import org.apache.directory.api.ldap.model.exception.LdapException;
34  import org.apache.directory.server.i18n.I18n;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  
39  /**
40   * Cursor over a set of values for the same key which are store in another
41   * BTree.  This Cursor is limited to the same key and it's tuples will always
42   * return the same key.
43   *
44   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
45   */
46  public class KeyTupleBTreeCursor<K, V> extends AbstractCursor<Tuple<K, V>>
47  {
48      /** A dedicated log for cursors */
49      private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
50  
51      /** Speedup for logs */
52      private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
53  
54      private final Comparator<V> comparator;
55      private final BTree btree;
56      private final K key;
57  
58      private jdbm.helper.Tuple<K, V> valueTuple = new jdbm.helper.Tuple<>();
59      private Tuple<K, V> returnedTuple = new Tuple<>();
60      private TupleBrowser<K, V> browser;
61      private boolean valueAvailable;
62  
63  
64      /**
65       * Creates a Cursor over the tuples of a JDBM BTree.
66       *
67       * @param btree the JDBM BTree to build a Cursor over
68       * @param key the constant key for which values are returned
69       * @param comparator the Comparator used to determine <b>key</b> ordering
70       * @throws IOException of there are problems accessing the BTree
71       */
72      public KeyTupleBTreeCursor( BTree btree, K key, Comparator<V> comparator ) throws IOException
73      {
74          if ( IS_DEBUG )
75          {
76              LOG_CURSOR.debug( "Creating KeyTupleBTreeCursor {}", this );
77          }
78  
79          this.key = key;
80          this.btree = btree;
81          this.comparator = comparator;
82          this.browser = btree.browse();
83      }
84  
85  
86      private void clearValue()
87      {
88          returnedTuple.setKey( key );
89          returnedTuple.setValue( null );
90          valueAvailable = false;
91      }
92  
93  
94      /**
95       * {@inheritDoc}
96       */
97      public boolean available()
98      {
99          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 }