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 GreaterEq 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 GreaterEqCursor<V> extends AbstractIndexCursor<V, ServerEntry>
39  {
40      private static final String UNSUPPORTED_MSG =
41          "GreaterEqCursors only support positioning by element when a user index exists on the asserted attribute.";
42  
43      /** An greater eq evaluator for candidates */
44      private final GreaterEqEvaluator greaterEqEvaluator;
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<String,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<String, ServerEntry> ndnCandidate;
58  
59      /** used in both modes */
60      private boolean available = false;
61  
62  
63      @SuppressWarnings("unchecked")
64      public GreaterEqCursor( Store<ServerEntry> db, GreaterEqEvaluator greaterEqEvaluator ) throws Exception
65      {
66          this.greaterEqEvaluator = greaterEqEvaluator;
67  
68          String attribute = greaterEqEvaluator.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 = db.getNdnIndex().forwardCursor();
77              userIdxCursor = null;
78          }
79      }
80  
81  
82      public boolean available()
83      {
84          return available;
85      }
86  
87  
88      @SuppressWarnings("unchecked")
89      public void beforeValue( Long id, V value ) throws Exception
90      {
91          checkNotClosed( "beforeValue()" );
92          if ( userIdxCursor != null )
93          {
94              /*
95               * First we need to check and make sure this element is within
96               * bounds as mandated by the assertion node.  To do so we compare
97               * it's value with the value of the node.  If it is smaller or
98               * equal to this lower bound then we simply position the
99               * userIdxCursor before the first element.  Otherwise we let the
100              * underlying userIdx Cursor position the element.
101              */
102             if ( greaterEqEvaluator.getComparator().compare( value,
103                  greaterEqEvaluator.getExpression().getValue().get() ) <= 0 )
104             {
105                 beforeFirst();
106                 return;
107             }
108 
109             userIdxCursor.beforeValue( id, value );
110             available = false;
111         }
112         else
113         {
114             throw new UnsupportedOperationException( UNSUPPORTED_MSG );
115         }
116     }
117 
118 
119     @SuppressWarnings("unchecked")
120     public void afterValue( Long id, V value ) throws Exception
121     {
122         checkNotClosed( "afterValue()" );
123         if ( userIdxCursor != null )
124         {
125             int comparedValue = greaterEqEvaluator.getComparator().compare( value,
126                  greaterEqEvaluator.getExpression().getValue().get() );
127 
128             /*
129              * First we need to check and make sure this element is within
130              * bounds as mandated by the assertion node.  To do so we compare
131              * it's value with the value of the node.  If it is equal to this
132              * lower bound then we simply position the userIdxCursor after
133              * this first node.  If it is less than this value then we
134              * position the Cursor before the first entry.
135              */
136             if ( comparedValue == 0 )
137             {
138                 userIdxCursor.afterValue( id, value );
139                 available = false;
140                 return;
141             }
142             else if ( comparedValue < 0 )
143             {
144                 beforeFirst();
145                 return;
146             }
147 
148             // Element is in the valid range as specified by assertion
149             userIdxCursor.afterValue( id, value );
150             available = false;
151         }
152         else
153         {
154             throw new UnsupportedOperationException( UNSUPPORTED_MSG );
155         }
156     }
157 
158 
159     @SuppressWarnings("unchecked")
160     public void before( IndexEntry<V, ServerEntry> element ) throws Exception
161     {
162         checkNotClosed( "before()" );
163         if ( userIdxCursor != null )
164         {
165             /*
166              * First we need to check and make sure this element is within
167              * bounds as mandated by the assertion node.  To do so we compare
168              * it's value with the value of the node.  If it is smaller or
169              * equal to this lower bound then we simply position the
170              * userIdxCursor before the first element.  Otherwise we let the
171              * underlying userIdx Cursor position the element.
172              */
173             if ( greaterEqEvaluator.getComparator().compare( element.getValue(),
174                  greaterEqEvaluator.getExpression().getValue().get() ) <= 0 )
175             {
176                 beforeFirst();
177                 return;
178             }
179 
180             userIdxCursor.before( element );
181             available = false;
182         }
183         else
184         {
185             throw new UnsupportedOperationException( UNSUPPORTED_MSG );
186         }
187     }
188 
189 
190     @SuppressWarnings("unchecked")
191     public void after( IndexEntry<V, ServerEntry> element ) throws Exception
192     {
193         checkNotClosed( "after()" );
194         if ( userIdxCursor != null )
195         {
196             int comparedValue = greaterEqEvaluator.getComparator().compare( element.getValue(),
197                  greaterEqEvaluator.getExpression().getValue().get() );
198 
199             /*
200              * First we need to check and make sure this element is within
201              * bounds as mandated by the assertion node.  To do so we compare
202              * it's value with the value of the node.  If it is equal to this
203              * lower bound then we simply position the userIdxCursor after
204              * this first node.  If it is less than this value then we
205              * position the Cursor before the first entry.
206              */
207             if ( comparedValue == 0 )
208             {
209                 userIdxCursor.after( element );
210                 available = false;
211                 return;
212             }
213             else if ( comparedValue < 0 )
214             {
215                 beforeFirst();
216                 return;
217             }
218 
219             // Element is in the valid range as specified by assertion
220             userIdxCursor.after( element );
221             available = false;
222         }
223         else
224         {
225             throw new UnsupportedOperationException( UNSUPPORTED_MSG );
226         }
227     }
228 
229 
230     @SuppressWarnings("unchecked")
231     public void beforeFirst() throws Exception
232     {
233         checkNotClosed( "beforeFirst()" );
234         if ( userIdxCursor != null )
235         {
236             IndexEntry<V,ServerEntry> advanceTo = new ForwardIndexEntry<V,ServerEntry>();
237             advanceTo.setValue( ( V ) greaterEqEvaluator.getExpression().getValue().get() );
238             userIdxCursor.before( advanceTo );
239         }
240         else
241         {
242             ndnIdxCursor.beforeFirst();
243             ndnCandidate = null;
244         }
245 
246         available = false;
247     }
248 
249 
250     public void afterLast() throws Exception
251     {
252         checkNotClosed( "afterLast()" );
253         if ( userIdxCursor != null )
254         {
255             userIdxCursor.afterLast();
256         }
257         else
258         {
259             ndnIdxCursor.afterLast();
260             ndnCandidate = null;
261         }
262 
263         available = false;
264     }
265 
266 
267     public boolean first() throws Exception
268     {
269         beforeFirst();
270         return next();
271     }
272 
273 
274     public boolean last() throws Exception
275     {
276         afterLast();
277         return previous();
278     }
279 
280 
281     @SuppressWarnings("unchecked")
282     public boolean previous() throws Exception
283     {
284         checkNotClosed( "previous()" );
285         if ( userIdxCursor != null )
286         {
287             /*
288              * We have to check and make sure the previous value complies by
289              * being greater than or eq to the expression node's value
290              */
291             while ( userIdxCursor.previous() )
292             {
293                 checkNotClosed( "previous()" );
294                 IndexEntry<?,ServerEntry> candidate = userIdxCursor.get();
295                 if ( greaterEqEvaluator.getComparator().compare( candidate.getValue(), greaterEqEvaluator.getExpression().getValue().get() ) >= 0 )
296                 {
297                     return available = true;
298                 }
299             }
300 
301             return available = false;
302         }
303 
304         while( ndnIdxCursor.previous() )
305         {
306             checkNotClosed( "previous()" );
307             ndnCandidate = ndnIdxCursor.get();
308             if ( greaterEqEvaluator.evaluate( ndnCandidate ) )
309             {
310                  return available = true;
311             }
312         }
313 
314         return available = false;
315     }
316 
317 
318     public boolean next() throws Exception
319     {
320         checkNotClosed( "next()" );
321         if ( userIdxCursor != null )
322         {
323             /*
324              * No need to do the same check that is done in previous() since
325              * values are increasing with calls to next().
326              */
327             return available = userIdxCursor.next();
328         }
329 
330         while( ndnIdxCursor.next() )
331         {
332             checkNotClosed( "next()" );
333             ndnCandidate = ndnIdxCursor.get();
334             if ( greaterEqEvaluator.evaluate( ndnCandidate ) )
335             {
336                  return available = true;
337             }
338         }
339 
340         return available = false;
341     }
342 
343 
344     @SuppressWarnings("unchecked")
345     public IndexEntry<V, ServerEntry> get() throws Exception
346     {
347         checkNotClosed( "get()" );
348         if ( userIdxCursor != null )
349         {
350             if ( available )
351             {
352                 return userIdxCursor.get();
353             }
354 
355             throw new InvalidCursorPositionException( "Cursor has not been positioned yet." );
356         }
357 
358         if ( available )
359         {
360             return ( IndexEntry<V, ServerEntry> ) ndnCandidate;
361         }
362 
363         throw new InvalidCursorPositionException( "Cursor has not been positioned yet." );
364     }
365 
366 
367     public boolean isElementReused()
368     {
369         if ( userIdxCursor != null )
370         {
371             return userIdxCursor.isElementReused();
372         }
373 
374         return ndnIdxCursor.isElementReused();
375     }
376 
377 
378     public void close() throws Exception
379     {
380         super.close();
381 
382         if ( userIdxCursor != null )
383         {
384             userIdxCursor.close();
385         }
386         else
387         {
388             ndnIdxCursor.close();
389         }
390     }
391 }