001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.server.xdbm.search.impl;
021
022
023 import org.apache.directory.server.i18n.I18n;
024 import org.apache.directory.server.xdbm.AbstractIndexCursor;
025 import org.apache.directory.server.xdbm.ForwardIndexEntry;
026 import org.apache.directory.server.xdbm.Index;
027 import org.apache.directory.server.xdbm.IndexCursor;
028 import org.apache.directory.server.xdbm.IndexEntry;
029 import org.apache.directory.server.xdbm.Store;
030 import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException;
031 import org.apache.directory.shared.ldap.entry.ServerEntry;
032
033
034 /**
035 * A Cursor over entry candidates matching a LessEq assertion filter. This
036 * Cursor operates in two modes. The first is when an index exists for the
037 * attribute the assertion is built on. The second is when the user index for
038 * the assertion attribute does not exist. Different Cursors are used in each
039 * of these cases where the other remains null.
040 *
041 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
042 * @version $$Rev$$
043 */
044 public class LessEqCursor<V, ID> extends AbstractIndexCursor<V, ServerEntry, ID>
045 {
046 private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_716 );
047
048 /** An less eq evaluator for candidates */
049 private final LessEqEvaluator<V, ID> lessEqEvaluator;
050
051 /** Cursor over attribute entry matching filter: set when index present */
052 private final IndexCursor<V, ServerEntry, ID> userIdxCursor;
053
054 /** NDN Cursor on all entries in (set when no index on user attribute) */
055 private final IndexCursor<V, ServerEntry, ID> ndnIdxCursor;
056
057 /**
058 * Used to store indexEntry from ndnCandidate so it can be saved after
059 * call to evaluate() which changes the value so it's not referring to
060 * the NDN but to the value of the attribute instead.
061 */
062 IndexEntry<V, ServerEntry, ID> ndnCandidate;
063
064 /** used in both modes */
065 private boolean available = false;
066
067
068 @SuppressWarnings("unchecked")
069 public LessEqCursor( Store<ServerEntry, ID> db, LessEqEvaluator<V, ID> lessEqEvaluator ) throws Exception
070 {
071 this.lessEqEvaluator = lessEqEvaluator;
072
073 String attribute = lessEqEvaluator.getExpression().getAttribute();
074 if ( db.hasIndexOn( attribute ) )
075 {
076 userIdxCursor = ( ( Index<V, ServerEntry, ID> ) db.getIndex( attribute ) ).forwardCursor();
077 ndnIdxCursor = null;
078 }
079 else
080 {
081 ndnIdxCursor = ( IndexCursor<V, ServerEntry, ID> ) db.getNdnIndex().forwardCursor();
082 userIdxCursor = null;
083 }
084 }
085
086
087 public boolean available()
088 {
089 return available;
090 }
091
092
093 public void beforeValue( ID id, V value ) throws Exception
094 {
095 checkNotClosed( "beforeValue()" );
096 if ( userIdxCursor != null )
097 {
098 /*
099 * First we need to check and make sure this element is within
100 * bounds as mandated by the assertion node. To do so we compare
101 * it's value with the value of the expression node. If the
102 * element's value is greater than this upper bound then we
103 * position the userIdxCursor after the last node.
104 *
105 * If the element's value is equal to this upper bound then we
106 * position the userIdxCursor right before the last node.
107 *
108 * If the element's value is smaller, then we delegate to the
109 * before() method of the userIdxCursor.
110 */
111 //noinspection unchecked
112 int compareValue = lessEqEvaluator.getLdapComparator().compare( value,
113 lessEqEvaluator.getExpression().getValue().get() );
114
115 if ( compareValue > 0 )
116 {
117 afterLast();
118 return;
119 }
120 else if ( compareValue == 0 )
121 {
122 last();
123 previous();
124 available = false;
125 return;
126 }
127
128 userIdxCursor.beforeValue( id, value );
129 available = false;
130 }
131 else
132 {
133 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
134 }
135 }
136
137
138 public void before( IndexEntry<V, ServerEntry, ID> element ) throws Exception
139 {
140 checkNotClosed( "before()" );
141 if ( userIdxCursor != null )
142 {
143 /*
144 * First we need to check and make sure this element is within
145 * bounds as mandated by the assertion node. To do so we compare
146 * it's value with the value of the expression node. If the
147 * element's value is greater than this upper bound then we
148 * position the userIdxCursor after the last node.
149 *
150 * If the element's value is equal to this upper bound then we
151 * position the userIdxCursor right before the last node.
152 *
153 * If the element's value is smaller, then we delegate to the
154 * before() method of the userIdxCursor.
155 */
156 int compareValue = lessEqEvaluator.getLdapComparator().compare( element.getValue(),
157 lessEqEvaluator.getExpression().getValue().get() );
158
159 if ( compareValue > 0 )
160 {
161 afterLast();
162 return;
163 }
164 else if ( compareValue == 0 )
165 {
166 last();
167 previous();
168 available = false;
169 return;
170 }
171
172 userIdxCursor.before( element );
173 available = false;
174 }
175 else
176 {
177 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
178 }
179 }
180
181
182 public void afterValue( ID id, V value ) throws Exception
183 {
184 checkNotClosed( "afterValue()" );
185 if ( userIdxCursor != null )
186 {
187 int comparedValue = lessEqEvaluator.getLdapComparator().compare( value,
188 lessEqEvaluator.getExpression().getValue().get() );
189
190 /*
191 * First we need to check and make sure this element is within
192 * bounds as mandated by the assertion node. To do so we compare
193 * it's value with the value of the expression node.
194 *
195 * If the element's value is equal to or greater than this upper
196 * bound then we position the userIdxCursor after the last node.
197 *
198 * If the element's value is smaller, then we delegate to the
199 * after() method of the userIdxCursor.
200 */
201 if ( comparedValue >= 0 )
202 {
203 afterLast();
204 return;
205 }
206
207 // Element is in the valid range as specified by assertion
208 userIdxCursor.afterValue( id, value );
209 available = false;
210 }
211 else
212 {
213 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
214 }
215 }
216
217
218 public void after( IndexEntry<V, ServerEntry, ID> element ) throws Exception
219 {
220 checkNotClosed( "after()" );
221 if ( userIdxCursor != null )
222 {
223 int comparedValue = lessEqEvaluator.getLdapComparator().compare( element.getValue(),
224 lessEqEvaluator.getExpression().getValue().get() );
225
226 /*
227 * First we need to check and make sure this element is within
228 * bounds as mandated by the assertion node. To do so we compare
229 * it's value with the value of the expression node.
230 *
231 * If the element's value is equal to or greater than this upper
232 * bound then we position the userIdxCursor after the last node.
233 *
234 * If the element's value is smaller, then we delegate to the
235 * after() method of the userIdxCursor.
236 */
237 if ( comparedValue >= 0 )
238 {
239 afterLast();
240 return;
241 }
242
243 // Element is in the valid range as specified by assertion
244 userIdxCursor.after( element );
245 available = false;
246 }
247 else
248 {
249 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
250 }
251 }
252
253
254 public void beforeFirst() throws Exception
255 {
256 checkNotClosed( "beforeFirst()" );
257 if ( userIdxCursor != null )
258 {
259 userIdxCursor.beforeFirst();
260 }
261 else
262 {
263 ndnIdxCursor.beforeFirst();
264 ndnCandidate = null;
265 }
266
267 available = false;
268 }
269
270
271 public void afterLast() throws Exception
272 {
273 checkNotClosed( "afterLast()" );
274 if ( userIdxCursor != null )
275 {
276 IndexEntry<V, ServerEntry, ID> advanceTo = new ForwardIndexEntry<V, ServerEntry, ID>();
277 //noinspection unchecked
278 advanceTo.setValue( ( V ) lessEqEvaluator.getExpression().getValue().get() );
279 userIdxCursor.after( advanceTo );
280 }
281 else
282 {
283 ndnIdxCursor.afterLast();
284 ndnCandidate = null;
285 }
286
287 available = false;
288 }
289
290
291 public boolean first() throws Exception
292 {
293 beforeFirst();
294 return next();
295 }
296
297
298 public boolean last() throws Exception
299 {
300 afterLast();
301 return previous();
302 }
303
304
305 public boolean previous() throws Exception
306 {
307 checkNotClosed( "previous()" );
308
309 if ( userIdxCursor != null )
310 {
311 /*
312 * No need to do the same check that is done in next() since
313 * values are decreasing with calls to previous(). We will
314 * always have lesser values.
315 */
316 return available = userIdxCursor.previous();
317 }
318
319 while ( ndnIdxCursor.previous() )
320 {
321 checkNotClosed( "previous()" );
322 ndnCandidate = ndnIdxCursor.get();
323 if ( lessEqEvaluator.evaluate( ndnCandidate ) )
324 {
325 return available = true;
326 }
327 else
328 {
329 ndnCandidate = null;
330 }
331 }
332
333 return available = false;
334 }
335
336
337 public boolean next() throws Exception
338 {
339 checkNotClosed( "next()" );
340 if ( userIdxCursor != null )
341 {
342 /*
343 * We have to check and make sure the next value complies by
344 * being less than or eq to the expression node's value. We need
345 * to do this since values are increasing and we must limit to our
346 * upper bound.
347 */
348 while ( userIdxCursor.next() )
349 {
350 checkNotClosed( "next()" );
351 IndexEntry<?, ServerEntry, ID> candidate = userIdxCursor.get();
352 if ( lessEqEvaluator.getLdapComparator().compare( candidate.getValue(),
353 lessEqEvaluator.getExpression().getValue().get() ) <= 0 )
354 {
355 return available = true;
356 }
357 }
358
359 return available = false;
360 }
361
362 while ( ndnIdxCursor.next() )
363 {
364 checkNotClosed( "next()" );
365 ndnCandidate = ndnIdxCursor.get();
366 if ( lessEqEvaluator.evaluate( ndnCandidate ) )
367 {
368 return available = true;
369 }
370 else
371 {
372 ndnCandidate = null;
373 }
374 }
375
376 return available = false;
377 }
378
379
380 public IndexEntry<V, ServerEntry, ID> get() throws Exception
381 {
382 checkNotClosed( "get()" );
383 if ( userIdxCursor != null )
384 {
385 if ( available )
386 {
387 return userIdxCursor.get();
388 }
389
390 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
391 }
392
393 if ( available )
394 {
395 return ndnCandidate;
396 }
397
398 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
399 }
400
401
402 public boolean isElementReused()
403 {
404 if ( userIdxCursor != null )
405 {
406 return userIdxCursor.isElementReused();
407 }
408
409 return ndnIdxCursor.isElementReused();
410 }
411
412
413 public void close() throws Exception
414 {
415 super.close();
416
417 if ( userIdxCursor != null )
418 {
419 userIdxCursor.close();
420 }
421 else
422 {
423 ndnIdxCursor.close();
424 ndnCandidate = null;
425 }
426 }
427 }