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.GreaterEqEvaluator;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  
43  /**
44   * A Cursor over entry candidates matching a GreaterEq 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 GreaterEqCursor<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 = "GreaterEqCursors only support positioning by element when a user index exists on the asserted attribute.";
61  
62      /** An greater eq evaluator for candidates */
63      private final GreaterEqEvaluator<V> greaterEqEvaluator;
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 uuidCandidate so it can be saved after
73       * call to evaluate() which changes the value so it's not referring to
74       * the NDN 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 GreaterEqCursor
81       * 
82       * @param partitionTxn The transaction to use
83       * @param store The store
84       * @param greaterEqEvaluator The GreaterEqEvaluator
85       * @throws LdapException If the creation failed
86       * @throws IndexNotFoundException If the index was not found
87       */
88      @SuppressWarnings("unchecked")
89      public GreaterEqCursor( PartitionTxn partitionTxn, Store store, GreaterEqEvaluator<V> greaterEqEvaluator ) 
90          throws LdapException, IndexNotFoundException
91      {
92          if ( IS_DEBUG )
93          {
94              LOG_CURSOR.debug( "Creating GreaterEqCursor {}", this );
95          }
96  
97          this.greaterEqEvaluator = greaterEqEvaluator;
98          this.partitionTxn = partitionTxn;
99  
100         AttributeType attributeType = greaterEqEvaluator.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 node.  If it is smaller or
138              * equal to this lower bound then we simply position the
139              * userIdxCursor before the first element.  Otherwise we let the
140              * underlying userIdx Cursor position the element.
141              */
142             if ( greaterEqEvaluator.getComparator().compare( element.getKey(),
143                 greaterEqEvaluator.getExpression().getValue().getString() ) <= 0 )
144             {
145                 beforeFirst();
146                 return;
147             }
148 
149             userIdxCursor.before( element );
150             setAvailable( false );
151         }
152         else
153         {
154             super.before( element );
155         }
156     }
157 
158 
159     /**
160      * {@inheritDoc}
161      */
162     @Override
163     public void after( IndexEntry<V, String> element ) throws LdapException, CursorException
164     {
165         checkNotClosed();
166 
167         if ( userIdxCursor != null )
168         {
169             int comparedValue = greaterEqEvaluator.getComparator().compare( element.getKey(),
170                 greaterEqEvaluator.getExpression().getValue().getString() );
171 
172             /*
173              * First we need to check and make sure this element is within
174              * bounds as mandated by the assertion node.  To do so we compare
175              * it's value with the value of the node.  If it is equal to this
176              * lower bound then we simply position the userIdxCursor after
177              * this first node.  If it is less than this value then we
178              * position the Cursor before the first entry.
179              */
180             if ( comparedValue == 0 )
181             {
182                 userIdxCursor.after( element );
183                 setAvailable( false );
184 
185                 return;
186             }
187 
188             if ( comparedValue < 0 )
189             {
190                 beforeFirst();
191 
192                 return;
193             }
194 
195             // Element is in the valid range as specified by assertion
196             userIdxCursor.after( element );
197             setAvailable( false );
198         }
199         else
200         {
201             super.after( element );
202         }
203     }
204 
205 
206     /**
207      * {@inheritDoc}
208      */
209     @SuppressWarnings("unchecked")
210     public void beforeFirst() throws LdapException, CursorException
211     {
212         checkNotClosed();
213 
214         if ( userIdxCursor != null )
215         {
216             IndexEntry<V, String> advanceTo = new IndexEntry<>();
217             String normalizedNode = greaterEqEvaluator.getNormalizer().normalize( greaterEqEvaluator.getExpression().getValue().getString() );
218             advanceTo.setKey( ( V ) normalizedNode );
219             userIdxCursor.before( advanceTo );
220         }
221         else
222         {
223             uuidIdxCursor.beforeFirst();
224             uuidCandidate = null;
225         }
226 
227         setAvailable( false );
228     }
229 
230 
231     /**
232      * {@inheritDoc}
233      */
234     public void afterLast() throws LdapException, CursorException
235     {
236         checkNotClosed();
237 
238         if ( userIdxCursor != null )
239         {
240             userIdxCursor.afterLast();
241         }
242         else
243         {
244             uuidIdxCursor.afterLast();
245             uuidCandidate = null;
246         }
247 
248         setAvailable( false );
249     }
250 
251 
252     /**
253      * {@inheritDoc}
254      */
255     public boolean first() throws LdapException, CursorException
256     {
257         beforeFirst();
258 
259         return next();
260     }
261 
262 
263     /**
264      * {@inheritDoc}
265      */
266     public boolean last() throws LdapException, CursorException
267     {
268         afterLast();
269 
270         return previous();
271     }
272 
273 
274     /**
275      * {@inheritDoc}
276      */
277     @Override
278     public boolean previous() throws LdapException, CursorException
279     {
280         checkNotClosed();
281 
282         if ( userIdxCursor != null )
283         {
284             /*
285              * We have to check and make sure the previous value complies by
286              * being greater than or eq to the expression node's value
287              */
288             while ( userIdxCursor.previous() )
289             {
290                 checkNotClosed();
291                 IndexEntry<?, String> candidate = userIdxCursor.get();
292 
293                 if ( greaterEqEvaluator.getComparator().compare( candidate.getKey(),
294                     greaterEqEvaluator.getExpression().getValue().getString() ) >= 0 )
295                 {
296                     return setAvailable( true );
297                 }
298             }
299 
300             return setAvailable( false );
301         }
302 
303         while ( uuidIdxCursor.previous() )
304         {
305             checkNotClosed();
306             uuidCandidate = uuidIdxCursor.get();
307 
308             if ( greaterEqEvaluator.evaluate( partitionTxn, uuidCandidate ) )
309             {
310                 return setAvailable( true );
311             }
312         }
313 
314         return setAvailable( false );
315     }
316 
317 
318     /**
319      * {@inheritDoc}
320      */
321     @Override
322     public boolean next() throws LdapException, CursorException
323     {
324         checkNotClosed();
325 
326         if ( userIdxCursor != null )
327         {
328             /*
329              * No need to do the same check that is done in previous() since
330              * values are increasing with calls to next().
331              */
332             return setAvailable( userIdxCursor.next() );
333         }
334 
335         while ( uuidIdxCursor.next() )
336         {
337             checkNotClosed();
338             uuidCandidate = uuidIdxCursor.get();
339 
340             if ( greaterEqEvaluator.evaluate( partitionTxn, uuidCandidate ) )
341             {
342                 return setAvailable( true );
343             }
344         }
345 
346         return setAvailable( false );
347     }
348 
349 
350     /**
351      * {@inheritDoc}
352      */
353     public IndexEntry<V, String> get() throws CursorException
354     {
355         checkNotClosed();
356 
357         if ( userIdxCursor != null )
358         {
359             if ( available() )
360             {
361                 return userIdxCursor.get();
362             }
363 
364             throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
365         }
366 
367         if ( available() )
368         {
369             return ( IndexEntry<V, String> ) uuidCandidate;
370         }
371 
372         throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
373     }
374 
375 
376     /**
377      * {@inheritDoc}
378      */
379     @Override
380     public void close() throws IOException
381     {
382         if ( IS_DEBUG )
383         {
384             LOG_CURSOR.debug( "Closing GreaterEqCursor {}", this );
385         }
386 
387         super.close();
388 
389         if ( userIdxCursor != null )
390         {
391             userIdxCursor.close();
392         }
393         else
394         {
395             uuidIdxCursor.close();
396             uuidCandidate = null;
397         }
398     }
399 
400 
401     /**
402      * {@inheritDoc}
403      */
404     @Override
405     public void close( Exception cause ) throws IOException
406     {
407         if ( IS_DEBUG )
408         {
409             LOG_CURSOR.debug( "Closing GreaterEqCursor {}", this );
410         }
411 
412         super.close( cause );
413 
414         if ( userIdxCursor != null )
415         {
416             userIdxCursor.close( cause );
417         }
418         else
419         {
420             uuidIdxCursor.close( cause );
421             uuidCandidate = null;
422         }
423     }
424 
425 
426     /**
427      * @see Object#toString()
428      */
429     @Override
430     public String toString( String tabs )
431     {
432         StringBuilder sb = new StringBuilder();
433 
434         sb.append( tabs ).append( "GreaterEqCursor (" );
435 
436         if ( available() )
437         {
438             sb.append( "available)" );
439         }
440         else
441         {
442             sb.append( "absent)" );
443         }
444 
445         sb.append( "#candidate<" ).append( uuidCandidate ).append( ">:\n" );
446 
447         sb.append( tabs + "  >>" ).append( greaterEqEvaluator ).append( '\n' );
448 
449         if ( userIdxCursor != null )
450         {
451             sb.append( tabs + "  <user>\n" );
452             sb.append( userIdxCursor.toString( tabs + "    " ) );
453         }
454 
455         if ( uuidIdxCursor != null )
456         {
457             sb.append( tabs + "  <uuid>\n" );
458             sb.append( uuidIdxCursor.toString( tabs + "  " ) );
459         }
460 
461         return sb.toString();
462     }
463 
464 
465     /**
466      * @see Object#toString()
467      */
468     public String toString()
469     {
470         return toString( "" );
471     }
472 }