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 }