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.impl;
21  
22  
23  import org.apache.directory.server.xdbm.*;
24  import org.apache.directory.server.core.cursor.InvalidCursorPositionException;
25  import org.apache.directory.server.core.entry.ServerEntry;
26  
27  
28  /**
29   * A Cursor over entry candidates matching a LessEq assertion filter.  This
30   * Cursor operates in two modes.  The first is when an index exists for the
31   * attribute the assertion is built on.  The second is when the user index for
32   * the assertion attribute does not exist.  Different Cursors are used in each
33   * of these cases where the other remains null.
34   *
35   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
36   * @version $$Rev$$
37   */
38  public class LessEqCursor<V> extends AbstractIndexCursor<V, ServerEntry>
39  {
40      private static final String UNSUPPORTED_MSG =
41          "LessEqCursors only support positioning by element when a user index exists on the asserted attribute.";
42  
43      /** An less eq evaluator for candidates */
44      private final LessEqEvaluator lessEqEvaluator;
45  
46      /** Cursor over attribute entry matching filter: set when index present */
47      private final IndexCursor<V,ServerEntry> userIdxCursor;
48  
49      /** NDN Cursor on all entries in  (set when no index on user attribute) */
50      private final IndexCursor<V,ServerEntry> ndnIdxCursor;
51  
52      /**
53       * Used to store indexEntry from ndnCandidate so it can be saved after
54       * call to evaluate() which changes the value so it's not referring to
55       * the NDN but to the value of the attribute instead.
56       */
57      IndexEntry<V, ServerEntry> ndnCandidate;
58  
59      /** used in both modes */
60      private boolean available = false;
61  
62  
63      @SuppressWarnings("unchecked")
64      public LessEqCursor( Store<ServerEntry> db, LessEqEvaluator lessEqEvaluator ) throws Exception
65      {
66          this.lessEqEvaluator = lessEqEvaluator;
67  
68          String attribute = lessEqEvaluator.getExpression().getAttribute();
69          if ( db.hasUserIndexOn( attribute ) )
70          {
71              userIdxCursor = ( ( Index<V,ServerEntry> ) db.getUserIndex( attribute ) ).forwardCursor();
72              ndnIdxCursor = null;
73          }
74          else
75          {
76              ndnIdxCursor = ( IndexCursor<V,ServerEntry> ) db.getNdnIndex().forwardCursor();
77              userIdxCursor = null;
78          }
79      }
80  
81  
82      public boolean available()
83      {
84          return available;
85      }
86  
87  
88      public void beforeValue( Long id, V value ) throws Exception
89      {
90          checkNotClosed( "beforeValue()" );
91          if ( userIdxCursor != null )
92          {
93              /*
94               * First we need to check and make sure this element is within
95               * bounds as mandated by the assertion node.  To do so we compare
96               * it's value with the value of the expression node.  If the
97               * element's value is greater than this upper bound then we
98               * position the userIdxCursor after the last node.
99               *
100              * If the element's value is equal to this upper bound then we
101              * position the userIdxCursor right before the last node.
102              *
103              * If the element's value is smaller, then we delegate to the
104              * before() method of the userIdxCursor.
105              */
106             //noinspection unchecked
107             int compareValue = lessEqEvaluator.getComparator().compare( value,
108                  lessEqEvaluator.getExpression().getValue().get() );
109 
110             if ( compareValue > 0 )
111             {
112                 afterLast();
113                 return;
114             }
115             else if ( compareValue == 0 )
116             {
117                 last();
118                 previous();
119                 available = false;
120                 return;
121             }
122 
123             userIdxCursor.beforeValue( id, value );
124             available = false;
125         }
126         else
127         {
128             throw new UnsupportedOperationException( UNSUPPORTED_MSG );
129         }
130     }
131 
132 
133     @SuppressWarnings("unchecked")
134     public void before( IndexEntry<V, ServerEntry> element ) throws Exception
135     {
136         checkNotClosed( "before()" );
137         if ( userIdxCursor != null )
138         {
139             /*
140              * First we need to check and make sure this element is within
141              * bounds as mandated by the assertion node.  To do so we compare
142              * it's value with the value of the expression node.  If the
143              * element's value is greater than this upper bound then we
144              * position the userIdxCursor after the last node.
145              *
146              * If the element's value is equal to this upper bound then we
147              * position the userIdxCursor right before the last node.
148              *
149              * If the element's value is smaller, then we delegate to the
150              * before() method of the userIdxCursor.
151              */
152             int compareValue = lessEqEvaluator.getComparator().compare( element.getValue(),
153                  lessEqEvaluator.getExpression().getValue().get() );
154 
155             if ( compareValue > 0 )
156             {
157                 afterLast();
158                 return;
159             }
160             else if ( compareValue == 0 )
161             {
162                 last();
163                 previous();
164                 available = false;
165                 return;
166             }
167 
168             userIdxCursor.before( element );
169             available = false;
170         }
171         else
172         {
173             throw new UnsupportedOperationException( UNSUPPORTED_MSG );
174         }
175     }
176 
177 
178     @SuppressWarnings("unchecked")
179     public void afterValue( Long id, V value ) throws Exception
180     {
181         checkNotClosed( "afterValue()" );
182         if ( userIdxCursor != null )
183         {
184             int comparedValue = lessEqEvaluator.getComparator().compare( value,
185                  lessEqEvaluator.getExpression().getValue().get() );
186 
187             /*
188              * First we need to check and make sure this element is within
189              * bounds as mandated by the assertion node.  To do so we compare
190              * it's value with the value of the expression node.
191              *
192              * If the element's value is equal to or greater than this upper
193              * bound then we position the userIdxCursor after the last node.
194              *
195              * If the element's value is smaller, then we delegate to the
196              * after() method of the userIdxCursor.
197              */
198             if ( comparedValue >= 0 )
199             {
200                 afterLast();
201                 return;
202             }
203 
204             // Element is in the valid range as specified by assertion
205             userIdxCursor.afterValue( id, value );
206             available = false;
207         }
208         else
209         {
210             throw new UnsupportedOperationException( UNSUPPORTED_MSG );
211         }
212     }
213 
214 
215     @SuppressWarnings("unchecked")
216     public void after( IndexEntry<V, ServerEntry> element ) throws Exception
217     {
218         checkNotClosed( "after()" );
219         if ( userIdxCursor != null )
220         {
221             int comparedValue = lessEqEvaluator.getComparator().compare( element.getValue(),
222                  lessEqEvaluator.getExpression().getValue().get() );
223 
224             /*
225              * First we need to check and make sure this element is within
226              * bounds as mandated by the assertion node.  To do so we compare
227              * it's value with the value of the expression node.
228              *
229              * If the element's value is equal to or greater than this upper
230              * bound then we position the userIdxCursor after the last node.
231              *
232              * If the element's value is smaller, then we delegate to the
233              * after() method of the userIdxCursor.
234              */
235             if ( comparedValue >= 0 )
236             {
237                 afterLast();
238                 return;
239             }
240 
241             // Element is in the valid range as specified by assertion
242             userIdxCursor.after( element );
243             available = false;
244         }
245         else
246         {
247             throw new UnsupportedOperationException( UNSUPPORTED_MSG );
248         }
249     }
250 
251 
252     public void beforeFirst() throws Exception
253     {
254         checkNotClosed( "beforeFirst()" );
255         if ( userIdxCursor != null )
256         {
257             userIdxCursor.beforeFirst();
258         }
259         else
260         {
261             ndnIdxCursor.beforeFirst();
262             ndnCandidate = null;
263         }
264 
265         available = false;
266     }
267 
268 
269     public void afterLast() throws Exception
270     {
271         checkNotClosed( "afterLast()" );
272         if ( userIdxCursor != null )
273         {
274             IndexEntry<V,ServerEntry> advanceTo = new ForwardIndexEntry<V,ServerEntry>();
275             //noinspection unchecked
276             advanceTo.setValue( ( V ) lessEqEvaluator.getExpression().getValue().get() );
277             userIdxCursor.after( advanceTo );
278         }
279         else
280         {
281             ndnIdxCursor.afterLast();
282             ndnCandidate = null;
283         }
284 
285         available = false;
286     }
287 
288 
289     public boolean first() throws Exception
290     {
291         beforeFirst();
292         return next();
293     }
294 
295 
296     public boolean last() throws Exception
297     {
298         afterLast();
299         return previous();
300     }
301 
302 
303     public boolean previous() throws Exception
304     {
305         checkNotClosed( "previous()" );
306 
307         if ( userIdxCursor != null )
308         {
309             /*
310              * No need to do the same check that is done in next() since
311              * values are decreasing with calls to previous().  We will
312              * always have lesser values.
313              */
314             return available = userIdxCursor.previous();
315         }
316 
317         while( ndnIdxCursor.previous() )
318         {
319             checkNotClosed( "previous()" );
320             ndnCandidate = ndnIdxCursor.get();
321             if ( lessEqEvaluator.evaluate( ndnCandidate ) )
322             {
323                  return available = true;
324             }
325             else
326             {
327                 ndnCandidate = null;
328             }
329         }
330 
331         return available = false;
332     }
333 
334 
335     @SuppressWarnings("unchecked")
336     public boolean next() throws Exception
337     {
338         checkNotClosed( "next()" );
339         if ( userIdxCursor != null )
340         {
341             /*
342              * We have to check and make sure the next value complies by
343              * being less than or eq to the expression node's value.  We need
344              * to do this since values are increasing and we must limit to our
345              * upper bound.
346              */
347             while ( userIdxCursor.next() )
348             {
349                 checkNotClosed( "next()" );
350                 IndexEntry<?,ServerEntry> candidate = userIdxCursor.get();
351                 if ( lessEqEvaluator.getComparator().compare( candidate.getValue(),
352                      lessEqEvaluator.getExpression().getValue().get() ) <= 0 )
353                 {
354                     return available = true;
355                 }
356             }
357 
358             return available = false;
359         }
360 
361         while( ndnIdxCursor.next() )
362         {
363             checkNotClosed( "next()" );
364             ndnCandidate = ndnIdxCursor.get();
365             if ( lessEqEvaluator.evaluate( ndnCandidate ) )
366             {
367                  return available = true;
368             }
369             else
370             {
371                 ndnCandidate = null;
372             }
373         }
374 
375         return available = false;
376     }
377 
378 
379     public IndexEntry<V, ServerEntry> get() throws Exception
380     {
381         checkNotClosed( "get()" );
382         if ( userIdxCursor != null )
383         {
384             if ( available )
385             {
386                 return userIdxCursor.get();
387             }
388 
389             throw new InvalidCursorPositionException( "Cursor has not been positioned yet." );
390         }
391 
392         if ( available )
393         {
394             return ndnCandidate;
395         }
396 
397         throw new InvalidCursorPositionException( "Cursor has not been positioned yet." );
398     }
399 
400 
401     public boolean isElementReused()
402     {
403         if ( userIdxCursor != null )
404         {
405             return userIdxCursor.isElementReused();
406         }
407 
408         return ndnIdxCursor.isElementReused();
409     }
410 
411 
412     public void close() throws Exception
413     {
414         super.close();
415 
416         if ( userIdxCursor != null )
417         {
418             userIdxCursor.close();
419         }
420         else
421         {
422             ndnIdxCursor.close();
423             ndnCandidate = null;
424          }
425     }
426 }