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.xdbm.search.cursor;
21  
22  
23  import java.io.IOException;
24  
25  import org.apache.directory.api.ldap.model.constants.Loggers;
26  import org.apache.directory.api.ldap.model.cursor.Cursor;
27  import org.apache.directory.api.ldap.model.cursor.CursorException;
28  import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
29  import org.apache.directory.api.ldap.model.exception.LdapException;
30  import org.apache.directory.api.ldap.model.schema.AttributeType;
31  import org.apache.directory.server.core.api.partition.PartitionTxn;
32  import org.apache.directory.server.i18n.I18n;
33  import org.apache.directory.server.xdbm.AbstractIndexCursor;
34  import org.apache.directory.server.xdbm.Index;
35  import org.apache.directory.server.xdbm.IndexEntry;
36  import org.apache.directory.server.xdbm.IndexNotFoundException;
37  import org.apache.directory.server.xdbm.Store;
38  import org.apache.directory.server.xdbm.search.evaluator.LessEqEvaluator;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  
43  /**
44   * A Cursor over entry candidates matching a LessEq assertion filter.  This
45   * Cursor operates in two modes.  The first is when an index exists for the
46   * attribute the assertion is built on.  The second is when the user index for
47   * the assertion attribute does not exist.  Different Cursors are used in each
48   * of these cases where the other remains null.
49   *
50   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
51   */
52  public class LessEqCursor<V> extends AbstractIndexCursor<V>
53  {
54      /** A dedicated log for cursors */
55      private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
56  
57      /** Speedup for logs */
58      private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
59  
60      private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_716 );
61  
62      /** An less eq evaluator for candidates */
63      private final LessEqEvaluator<V> lessEqEvaluator;
64  
65      /** Cursor over attribute entry matching filter: set when index present */
66      private final Cursor<IndexEntry<V, String>> userIdxCursor;
67  
68      /** NDN Cursor on all entries in  (set when no index on user attribute) */
69      private final Cursor<IndexEntry<String, String>> uuidIdxCursor;
70  
71      /**
72       * Used to store indexEntry from uudCandidate so it can be saved after
73       * call to evaluate() which changes the value so it's not referring to
74       * the String but to the value of the attribute instead.
75       */
76      private IndexEntry<String, String> uuidCandidate;
77  
78  
79      /**
80       * Creates a new instance of an LessEqCursor
81       * 
82       * @param partitionTxn The transaction to use
83       * @param store The store
84       * @param lessEqEvaluator The LessEqEvaluator
85       * @throws LdapException If the creation failed
86       * @throws IndexNotFoundException If the index was not found
87       */
88      @SuppressWarnings("unchecked")
89      public LessEqCursor( PartitionTxn partitionTxn, Store store, LessEqEvaluator<V> lessEqEvaluator ) 
90          throws LdapException, IndexNotFoundException
91      {
92          if ( IS_DEBUG )
93          {
94              LOG_CURSOR.debug( "Creating LessEqCursor {}", this );
95          }
96  
97          this.lessEqEvaluator = lessEqEvaluator;
98          this.partitionTxn = partitionTxn;
99  
100         AttributeType attributeType = lessEqEvaluator.getExpression().getAttributeType();
101 
102         if ( store.hasIndexOn( attributeType ) )
103         {
104             userIdxCursor = ( ( Index<V, String> ) store.getIndex( attributeType ) ).forwardCursor( partitionTxn );
105             uuidIdxCursor = null;
106         }
107         else
108         {
109             uuidIdxCursor = new AllEntriesCursor( partitionTxn, store );
110             userIdxCursor = null;
111         }
112     }
113 
114 
115     /**
116      * {@inheritDoc}
117      */
118     protected String getUnsupportedMessage()
119     {
120         return UNSUPPORTED_MSG;
121     }
122 
123 
124     /**
125      * {@inheritDoc}
126      */
127     @Override
128     public void before( IndexEntry<V, String> element ) throws LdapException, CursorException
129     {
130         checkNotClosed();
131 
132         if ( userIdxCursor != null )
133         {
134             /*
135              * First we need to check and make sure this element is within
136              * bounds as mandated by the assertion node.  To do so we compare
137              * it's value with the value of the expression node.  If the
138              * element's value is greater than this upper bound then we
139              * position the userIdxCursor after the last node.
140              *
141              * If the element's value is equal to this upper bound then we
142              * position the userIdxCursor right before the last node.
143              *
144              * If the element's value is smaller, then we delegate to the
145              * before() method of the userIdxCursor.
146              */
147             int compareValue = lessEqEvaluator.getComparator().compare( element.getKey(),
148                 lessEqEvaluator.getExpression().getValue().getString() );
149 
150             if ( compareValue > 0 )
151             {
152                 afterLast();
153                 return;
154             }
155             else if ( compareValue == 0 )
156             {
157                 last();
158                 previous();
159                 setAvailable( false );
160                 return;
161             }
162 
163             userIdxCursor.before( element );
164             setAvailable( false );
165         }
166         else
167         {
168             super.before( element );
169         }
170     }
171 
172 
173     /**
174      * {@inheritDoc}
175      */
176     @Override
177     public void after( IndexEntry<V, String> element ) throws LdapException, CursorException
178     {
179         checkNotClosed();
180 
181         if ( userIdxCursor != null )
182         {
183             int comparedValue = lessEqEvaluator.getComparator().compare( element.getKey(),
184                 lessEqEvaluator.getExpression().getValue().getString() );
185 
186             /*
187              * First we need to check and make sure this element is within
188              * bounds as mandated by the assertion node.  To do so we compare
189              * it's value with the value of the expression node.
190              *
191              * If the element's value is equal to or greater than this upper
192              * bound then we position the userIdxCursor after the last node.
193              *
194              * If the element's value is smaller, then we delegate to the
195              * after() method of the userIdxCursor.
196              */
197             if ( comparedValue >= 0 )
198             {
199                 afterLast();
200                 return;
201             }
202 
203             // Element is in the valid range as specified by assertion
204             userIdxCursor.after( element );
205             setAvailable( false );
206         }
207         else
208         {
209             super.after( element );
210         }
211     }
212 
213 
214     /**
215      * {@inheritDoc}
216      */
217     public void beforeFirst() throws LdapException, CursorException
218     {
219         checkNotClosed();
220         
221         if ( userIdxCursor != null )
222         {
223             userIdxCursor.beforeFirst();
224         }
225         else
226         {
227             uuidIdxCursor.beforeFirst();
228             uuidCandidate = null;
229         }
230 
231         setAvailable( false );
232     }
233 
234 
235     /**
236      * {@inheritDoc}
237      */
238     public void afterLast() throws LdapException, CursorException
239     {
240         checkNotClosed();
241         
242         if ( userIdxCursor != null )
243         {
244             IndexEntry<V, String> advanceTo = new IndexEntry<>();
245             //noinspection unchecked
246             String normalizedKey = lessEqEvaluator.getAttributeType().getEquality().getNormalizer().normalize( 
247                 lessEqEvaluator.getExpression().getValue().getString() );
248             
249             advanceTo.setKey( ( V ) normalizedKey );
250             userIdxCursor.after( advanceTo );
251         }
252         else
253         {
254             uuidIdxCursor.afterLast();
255             uuidCandidate = null;
256         }
257 
258         setAvailable( false );
259     }
260 
261 
262     /**
263      * {@inheritDoc}
264      */
265     public boolean first() throws LdapException, CursorException
266     {
267         beforeFirst();
268         return next();
269     }
270 
271 
272     /**
273      * {@inheritDoc}
274      */
275     public boolean last() throws LdapException, CursorException
276     {
277         afterLast();
278 
279         return previous();
280     }
281 
282 
283     /**
284      * {@inheritDoc}
285      */
286     @Override
287     public boolean previous() throws LdapException, CursorException
288     {
289         checkNotClosed();
290 
291         if ( userIdxCursor != null )
292         {
293             /*
294              * No need to do the same check that is done in next() since
295              * values are decreasing with calls to previous().  We will
296              * always have lesser values.
297              */
298             return setAvailable( userIdxCursor.previous() );
299         }
300 
301         while ( uuidIdxCursor.previous() )
302         {
303             checkNotClosed();
304             uuidCandidate = uuidIdxCursor.get();
305 
306             if ( lessEqEvaluator.evaluate( partitionTxn, uuidCandidate ) )
307             {
308                 return setAvailable( true );
309             }
310             else
311             {
312                 uuidCandidate = null;
313             }
314         }
315 
316         return setAvailable( false );
317     }
318 
319 
320     /**
321      * {@inheritDoc}
322      */
323     @Override
324     public boolean next() throws LdapException, CursorException
325     {
326         checkNotClosed();
327 
328         if ( userIdxCursor != null )
329         {
330             /*
331              * We have to check and make sure the next value complies by
332              * being less than or eq to the expression node's value.  We need
333              * to do this since values are increasing and we must limit to our
334              * upper bound.
335              */
336             while ( userIdxCursor.next() )
337             {
338                 checkNotClosed();
339                 IndexEntry<?, String> candidate = userIdxCursor.get();
340 
341                 if ( lessEqEvaluator.getComparator().compare( candidate.getKey(),
342                     lessEqEvaluator.getExpression().getValue().getString() ) <= 0 )
343                 {
344                     return setAvailable( true );
345                 }
346             }
347 
348             return setAvailable( false );
349         }
350 
351         while ( uuidIdxCursor.next() )
352         {
353             checkNotClosed();
354             uuidCandidate = uuidIdxCursor.get();
355 
356             if ( lessEqEvaluator.evaluate( partitionTxn, uuidCandidate ) )
357             {
358                 return setAvailable( true );
359             }
360             else
361             {
362                 uuidCandidate = null;
363             }
364         }
365 
366         return setAvailable( false );
367     }
368 
369 
370     /**
371      * {@inheritDoc}
372      */
373     public IndexEntry<V, String> get() throws CursorException
374     {
375         checkNotClosed();
376 
377         if ( userIdxCursor != null )
378         {
379             if ( available() )
380             {
381                 return userIdxCursor.get();
382             }
383 
384             throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
385         }
386 
387         if ( available() )
388         {
389             return ( IndexEntry<V, String> ) uuidCandidate;
390         }
391 
392         throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
393     }
394 
395 
396     /**
397      * {@inheritDoc}
398      */
399     @Override
400     public void close() throws IOException
401     {
402         if ( IS_DEBUG )
403         {
404             LOG_CURSOR.debug( "Closing LessEqCursor {}", this );
405         }
406 
407         super.close();
408 
409         if ( userIdxCursor != null )
410         {
411             userIdxCursor.close();
412         }
413         else
414         {
415             uuidIdxCursor.close();
416             uuidCandidate = null;
417         }
418     }
419 
420 
421     /**
422      * {@inheritDoc}
423      */
424     @Override
425     public void close( Exception cause ) throws IOException
426     {
427         LOG_CURSOR.debug( "Closing LessEqCursor {}", this );
428         super.close( cause );
429 
430         if ( userIdxCursor != null )
431         {
432             userIdxCursor.close( cause );
433         }
434         else
435         {
436             uuidIdxCursor.close( cause );
437             uuidCandidate = null;
438         }
439     }
440 
441 
442     /**
443      * @see Object#toString()
444      */
445     @Override
446     public String toString( String tabs )
447     {
448         StringBuilder sb = new StringBuilder();
449 
450         sb.append( tabs ).append( "LessEqCursor (" );
451 
452         if ( available() )
453         {
454             sb.append( "available)" );
455         }
456         else
457         {
458             sb.append( "absent)" );
459         }
460 
461         sb.append( "#candidate<" ).append( uuidCandidate ).append( ">:\n" );
462 
463         sb.append( tabs + "  >>" ).append( lessEqEvaluator ).append( '\n' );
464 
465         if ( userIdxCursor != null )
466         {
467             sb.append( tabs + "  <user>\n" );
468             sb.append( userIdxCursor.toString( tabs + "    " ) );
469         }
470 
471         if ( uuidIdxCursor != null )
472         {
473             sb.append( tabs + "  <uuid>\n" );
474             sb.append( uuidIdxCursor.toString( tabs + "  " ) );
475         }
476 
477         return sb.toString();
478     }
479 
480 
481     /**
482      * @see Object#toString()
483      */
484     public String toString()
485     {
486         return toString( "" );
487     }
488 }