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  
24  import jdbm.helper.TupleBrowser;
25  
26  import org.apache.directory.api.ldap.model.constants.Loggers;
27  import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
28  import org.apache.directory.api.ldap.model.cursor.CursorException;
29  import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
30  import org.apache.directory.api.ldap.model.cursor.Tuple;
31  import org.apache.directory.api.ldap.model.exception.LdapException;
32  import org.apache.directory.server.i18n.I18n;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  
37  /**
38   * Cursor over the Tuples of a JDBM BTree.  Duplicate keys are not supported
39   * by JDBM natively so you will not see duplicate keys.  For this reason as
40   * well before() and after() positioning only considers the key of the Tuple
41   * arguments provided.
42   *
43   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
44   */
45  class NoDupsCursor<K, V> extends AbstractCursor<Tuple<K, V>>
46  {
47      /** A dedicated log for cursors */
48      private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
49  
50      /** Speedup for logs */
51      private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
52  
53      private final JdbmTable<K, V> table;
54  
55      private jdbm.helper.Tuple jdbmTuple = new jdbm.helper.Tuple();
56      private Tuple<K, V> returnedTuple = new Tuple<>();
57      private TupleBrowser browser;
58      private boolean valueAvailable;
59  
60  
61      /**
62       * Creates a Cursor over the tuples of a JDBM table.
63       *
64       * @param table the JDBM Table to build a Cursor over
65       */
66      NoDupsCursor( JdbmTable<K, V> table )
67      {
68          if ( IS_DEBUG )
69          {
70              LOG_CURSOR.debug( "Creating NoDupsCursor {}", this );
71          }
72  
73          this.table = table;
74      }
75  
76  
77      private void clearValue()
78      {
79          returnedTuple.setKey( null );
80          returnedTuple.setValue( null );
81          jdbmTuple.setKey( null );
82          jdbmTuple.setValue( null );
83          valueAvailable = false;
84      }
85  
86  
87      public boolean available()
88      {
89          return valueAvailable;
90      }
91  
92  
93      public void beforeKey( K key ) throws CursorException
94      {
95          checkNotClosed();
96          try
97          {
98              browser = table.getBTree().browse( key );
99              clearValue();
100         }
101         catch ( IOException e )
102         {
103             throw new CursorException( e );
104         }
105     }
106 
107 
108     @SuppressWarnings("unchecked")
109     public void afterKey( K key ) throws CursorException
110     {
111         try
112         {
113             browser = table.getBTree().browse( key );
114 
115             /*
116              * While the next value is less than or equal to the element keep
117              * advancing forward to the next item.  If we cannot advance any
118              * further then stop and return.  If we find a value greater than
119              * the element then we stop, backup, and return so subsequent calls
120              * to getNext() will return a value greater than the element.
121              */
122             while ( browser.getNext( jdbmTuple ) )
123             {
124                 checkNotClosed();
125                 K next = ( K ) jdbmTuple.getKey();
126 
127                 int nextCompared = table.getKeyComparator().compare( next, key );
128 
129                 if ( nextCompared > 0 )
130                 {
131                     browser.getPrevious( jdbmTuple );
132                     clearValue();
133                     return;
134                 }
135             }
136 
137             clearValue();
138         }
139         catch ( IOException e )
140         {
141             throw new CursorException( e );
142         }
143     }
144 
145 
146     public void beforeValue( K key, V value ) throws Exception
147     {
148         throw new UnsupportedOperationException( I18n.err( I18n.ERR_596 ) );
149     }
150 
151 
152     public void afterValue( K key, V value )
153     {
154         throw new UnsupportedOperationException( I18n.err( I18n.ERR_596 ) );
155     }
156 
157 
158     /**
159      * Positions this Cursor before the key of the supplied tuple.
160      *
161      * @param element the tuple who's key is used to position this Cursor
162      * @throws IOException if there are failures to position the Cursor
163      */
164     public void before( Tuple<K, V> element ) throws CursorException
165     {
166         beforeKey( element.getKey() );
167     }
168 
169 
170     /**
171      * {@inheritDoc}
172      */
173     public void after( Tuple<K, V> element ) throws CursorException
174     {
175         afterKey( element.getKey() );
176     }
177 
178 
179     /**
180      * {@inheritDoc}
181      */
182     public void beforeFirst() throws LdapException, CursorException
183     {
184         checkNotClosed();
185         try
186         {
187             browser = table.getBTree().browse();
188             clearValue();
189         }
190         catch ( IOException e )
191         {
192             throw new CursorException( e );
193         }
194     }
195 
196 
197     /**
198      * {@inheritDoc}
199      */
200     public void afterLast() throws LdapException, CursorException
201     {
202         checkNotClosed();
203         try
204         {
205             browser = table.getBTree().browse( null );
206             clearValue();
207         }
208         catch ( IOException e )
209         {
210             throw new CursorException( e );
211         }
212     }
213 
214 
215     /**
216      * {@inheritDoc}
217      */
218     public boolean first() throws LdapException, CursorException
219     {
220         beforeFirst();
221         return next();
222     }
223 
224 
225     /**
226      * {@inheritDoc}
227      */
228     public boolean last() throws LdapException, CursorException
229     {
230         afterLast();
231         return previous();
232     }
233 
234 
235     /**
236      * {@inheritDoc}
237      */
238     @SuppressWarnings("unchecked")
239     public boolean previous() throws LdapException, CursorException
240     {
241         checkNotClosed();
242 
243         if ( browser == null )
244         {
245             afterLast();
246         }
247 
248         try
249         {
250             if ( browser.getPrevious( jdbmTuple ) )
251             {
252                 if ( returnedTuple.getKey() != null
253                     && table.getKeyComparator().compare( ( K ) jdbmTuple.getKey(), returnedTuple.getKey() ) == 0 )
254                 {
255                     browser.getPrevious( jdbmTuple );
256                 }
257 
258                 returnedTuple.setKey( ( K ) jdbmTuple.getKey() );
259                 returnedTuple.setValue( ( V ) jdbmTuple.getValue() );
260                 valueAvailable = true;
261                 return true;
262             }
263             else
264             {
265                 clearValue();
266                 return false;
267             }
268         }
269         catch ( IOException e )
270         {
271             throw new CursorException( e );
272         }
273     }
274 
275 
276     /**
277      * {@inheritDoc}
278      */
279     @SuppressWarnings("unchecked")
280     public boolean next() throws LdapException, CursorException
281     {
282         checkNotClosed();
283 
284         if ( browser == null )
285         {
286             beforeFirst();
287         }
288 
289         try
290         {
291             if ( browser.getNext( jdbmTuple ) )
292             {
293                 if ( returnedTuple.getKey() != null
294                     && table.getKeyComparator().compare( ( K ) jdbmTuple.getKey(), returnedTuple.getKey() ) == 0 )
295                 {
296                     browser.getNext( jdbmTuple );
297                 }
298 
299                 returnedTuple.setKey( ( K ) jdbmTuple.getKey() );
300                 returnedTuple.setValue( ( V ) jdbmTuple.getValue() );
301                 valueAvailable = true;
302                 return true;
303             }
304             else
305             {
306                 clearValue();
307                 return false;
308             }
309         }
310         catch ( IOException e )
311         {
312             throw new CursorException( e );
313         }
314     }
315 
316 
317     /**
318      * {@inheritDoc}
319      */
320     public Tuple<K, V> get() throws CursorException
321     {
322         checkNotClosed();
323         if ( valueAvailable )
324         {
325             return returnedTuple;
326         }
327 
328         throw new InvalidCursorPositionException();
329     }
330 
331 
332     /**
333      * {@inheritDoc}
334      */
335     @Override
336     public void close() throws IOException
337     {
338         if ( IS_DEBUG )
339         {
340             LOG_CURSOR.debug( "Closing NoDupsCursor {}", this );
341         }
342 
343         super.close();
344     }
345 
346 
347     /**
348      * {@inheritDoc}
349      */
350     @Override
351     public void close( Exception cause ) throws IOException
352     {
353         if ( IS_DEBUG )
354         {
355             LOG_CURSOR.debug( "Closing NoDupsCursor {}", this );
356         }
357 
358         super.close( cause );
359     }
360 }