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   */
20  package org.apache.directory.server.core.avltree;
21  
22  
23  import java.io.IOException;
24  import java.util.Comparator;
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.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  
36  /**
37   * A Cursor for AvlTreeMap without duplicates.
38   *
39   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
40   */
41  public class AvlSingletonOrOrderedSetCursor<K, V> extends AbstractCursor<Tuple<K, SingletonOrOrderedSet<V>>>
42  {
43      /** A dedicated log for cursors */
44      private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
45  
46      /** Speedup for logs */
47      private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
48  
49      /** The underlying AVL tree map */
50      private AvlTreeMap<K, V> tree;
51  
52      /** The current node */
53      private LinkedAvlMapNode<K, V> node;
54  
55      /** The current position of this cursor, relative to the node */
56      private Position position = Position.BEFORE_FIRST;
57  
58      private Tuple<K, SingletonOrOrderedSet<V>> returnedTuple = new Tuple<>();
59  
60  
61      public AvlSingletonOrOrderedSetCursor( AvlTreeMap<K, V> tree )
62      {
63          if ( IS_DEBUG )
64          {
65              LOG_CURSOR.debug( "Creating AvlSingletonOrOrderedSetCursor {}", this );
66          }
67  
68          this.tree = tree;
69      }
70  
71  
72      public Comparator<K> getKeyComparator()
73      {
74          return tree.getKeyComparator();
75      }
76  
77  
78      public Comparator<V> getValuComparator()
79      {
80          return tree.getValueComparator();
81      }
82  
83  
84      /**
85       * {@inheritDoc}
86       */
87      public void after( Tuple<K, SingletonOrOrderedSet<V>> element ) throws LdapException, CursorException
88      {
89          afterKey( element.getKey() );
90      }
91  
92  
93      /**
94       * {@inheritDoc}
95       */
96      public void afterLast() throws LdapException, CursorException
97      {
98          checkNotClosed();
99          node = null;
100         position = Position.AFTER_LAST;
101     }
102 
103 
104     public boolean available()
105     {
106         return position == Position.ON_NODE;
107     }
108 
109 
110     public void before( Tuple<K, SingletonOrOrderedSet<V>> element ) throws LdapException, CursorException
111     {
112         beforeKey( element.getKey() );
113     }
114 
115 
116     /**
117      * {@inheritDoc}
118      */
119     public void beforeFirst() throws LdapException, CursorException
120     {
121         checkNotClosed();
122         node = null;
123         position = Position.BEFORE_FIRST;
124     }
125 
126 
127     /**
128      * {@inheritDoc}
129      */
130     public boolean first() throws LdapException, CursorException
131     {
132         checkNotClosed();
133 
134         node = tree.getFirst();
135 
136         if ( node == null )
137         {
138             position = Position.BEFORE_FIRST;
139             return false;
140         }
141         else
142         {
143             position = Position.ON_NODE;
144             return true;
145         }
146     }
147 
148 
149     /**
150      * {@inheritDoc}
151      */
152     public Tuple<K, SingletonOrOrderedSet<V>> get() throws CursorException
153     {
154         checkNotClosed();
155 
156         if ( position == Position.ON_NODE )
157         {
158             returnedTuple.setKey( node.key );
159             returnedTuple.setValue( node.value );
160             return returnedTuple;
161         }
162 
163         throw new InvalidCursorPositionException();
164     }
165 
166 
167     /**
168      * {@inheritDoc}
169      */
170     public boolean last() throws LdapException, CursorException
171     {
172         checkNotClosed();
173 
174         node = tree.getLast();
175 
176         if ( node == null )
177         {
178             position = Position.AFTER_LAST;
179             return false;
180         }
181         else
182         {
183             position = Position.ON_NODE;
184             return true;
185         }
186     }
187 
188 
189     /**
190      * {@inheritDoc}
191      */
192     public boolean next() throws LdapException, CursorException
193     {
194         checkNotClosed();
195 
196         switch ( position )
197         {
198             case BEFORE_FIRST:
199                 return first();
200 
201             case BEFORE_NODE:
202                 position = Position.ON_NODE;
203                 return true;
204 
205             case ON_NODE:
206             case AFTER_NODE:
207                 node = node.next;
208 
209                 if ( node == null )
210                 {
211                     afterLast();
212 
213                     return false;
214                 }
215                 else
216                 {
217                     position = Position.ON_NODE;
218                     return true;
219                 }
220 
221             case AFTER_LAST:
222                 return false;
223 
224             default:
225                 throw new IllegalStateException( "Unexpected position " + position );
226         }
227     }
228 
229 
230     /**
231      * {@inheritDoc}
232      */
233     public boolean previous() throws LdapException, CursorException
234     {
235         checkNotClosed();
236 
237         switch ( position )
238         {
239             case BEFORE_FIRST:
240                 return false;
241 
242             case BEFORE_NODE:
243             case ON_NODE:
244                 node = node.previous;
245                 if ( node == null )
246                 {
247                     beforeFirst();
248                     return false;
249                 }
250                 else
251                 {
252                     position = Position.ON_NODE;
253                     return true;
254                 }
255 
256             case AFTER_NODE:
257                 position = Position.ON_NODE;
258                 return true;
259 
260             case AFTER_LAST:
261                 return last();
262 
263             default:
264                 throw new IllegalStateException( "Unexpected position " + position );
265         }
266     }
267 
268 
269     /**
270      * {@inheritDoc}
271      */
272     public void afterKey( K key ) throws LdapException, CursorException
273     {
274         checkNotClosed();
275 
276         if ( key == null )
277         {
278             afterLast();
279             return;
280         }
281 
282         node = tree.findGreater( key );
283 
284         if ( node == null )
285         {
286             position = Position.AFTER_LAST;
287         }
288         else
289         {
290             // the cursor should be positioned after the given element
291             // we just fetched the next greater element so the cursor
292             // is positioned before the fetched element
293             position = Position.BEFORE_NODE;
294         }
295     }
296 
297 
298     public void afterValue( K key, SingletonOrOrderedSet<V> value ) throws Exception
299     {
300         throw new UnsupportedOperationException( "This Cursor does not support duplicate keys." );
301     }
302 
303 
304     public void beforeKey( K key ) throws LdapException, CursorException
305     {
306         checkNotClosed();
307 
308         if ( key == null )
309         {
310             beforeFirst();
311             return;
312         }
313 
314         node = tree.findLess( key );
315 
316         if ( node == null )
317         {
318             position = Position.BEFORE_FIRST;
319         }
320         else
321         {
322             // the cursor should be positioned before the given element
323             // we just fetched the next less element so the cursor
324             // is positioned after the fetched element
325             position = Position.AFTER_NODE;
326         }
327     }
328 
329 
330     public void beforeValue( K key, SingletonOrOrderedSet<V> value ) throws Exception
331     {
332         throw new UnsupportedOperationException( "This Cursor does not support duplicate keys." );
333     }
334 
335 
336     /**
337      * {@inheritDoc}
338      */
339     @Override
340     public void close() throws IOException
341     {
342         if ( IS_DEBUG )
343         {
344             LOG_CURSOR.debug( "Closing AvlSingletonOrOrderedSetCursor {}", this );
345         }
346 
347         super.close();
348     }
349 
350 
351     /**
352      * {@inheritDoc}
353      */
354     @Override
355     public void close( Exception reason ) throws IOException
356     {
357         if ( IS_DEBUG )
358         {
359             LOG_CURSOR.debug( "Closing AvlSingletonOrOrderedSetCursor {}", this );
360         }
361 
362         super.close( reason );
363     }
364 }