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.Tuple;
27  import jdbm.helper.TupleBrowser;
28  
29  import org.apache.directory.api.ldap.model.constants.Loggers;
30  import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
31  import org.apache.directory.api.ldap.model.cursor.CursorException;
32  import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
33  import org.apache.directory.api.ldap.model.exception.LdapException;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  
38  /**
39   * Cursor over the keys of a JDBM BTree.  Obviously does not return duplicate
40   * keys since JDBM does not natively support multiple values for the same key.
41   *
42   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
43   */
44  public class KeyBTreeCursor<E> extends AbstractCursor<E>
45  {
46      /** A dedicated log for cursors */
47      private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
48  
49      /** Speedup for logs */
50      private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
51  
52      private final Tuple tuple = new Tuple();
53  
54      private final BTree btree;
55      private final Comparator<E> comparator;
56      private boolean valueAvailable;
57      private TupleBrowser browser;
58  
59  
60      /**
61       * Creates a Cursor over the keys of a JDBM BTree.
62       *
63       * @param btree the JDBM BTree to build a Cursor over
64       * @param comparator the Comparator used to determine key ordering
65       */
66      public KeyBTreeCursor( BTree btree, Comparator<E> comparator )
67      {
68          if ( IS_DEBUG )
69          {
70              LOG_CURSOR.debug( "Creating KeyBTreeCursor {}", this );
71          }
72  
73          this.btree = btree;
74          this.comparator = comparator;
75      }
76  
77  
78      private void clearValue()
79      {
80          tuple.setKey( null );
81          tuple.setValue( null );
82          valueAvailable = false;
83      }
84  
85  
86      public boolean available()
87      {
88          return valueAvailable;
89      }
90  
91  
92      /**
93       * {@inheritDoc}
94       */
95      public void before( E element ) throws LdapException, CursorException
96      {
97          checkNotClosed();
98          try
99          {
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 }