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 */
020package org.apache.directory.server.xdbm.search.cursor;
021
022
023import java.io.IOException;
024
025import org.apache.directory.api.ldap.model.constants.Loggers;
026import org.apache.directory.api.ldap.model.cursor.Cursor;
027import org.apache.directory.api.ldap.model.cursor.CursorException;
028import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
029import org.apache.directory.api.ldap.model.exception.LdapException;
030import org.apache.directory.api.ldap.model.schema.AttributeType;
031import org.apache.directory.server.core.api.partition.PartitionTxn;
032import org.apache.directory.server.i18n.I18n;
033import org.apache.directory.server.xdbm.AbstractIndexCursor;
034import org.apache.directory.server.xdbm.Index;
035import org.apache.directory.server.xdbm.IndexEntry;
036import org.apache.directory.server.xdbm.IndexNotFoundException;
037import org.apache.directory.server.xdbm.Store;
038import org.apache.directory.server.xdbm.search.evaluator.GreaterEqEvaluator;
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042
043/**
044 * A Cursor over entry candidates matching a GreaterEq assertion filter.  This
045 * Cursor operates in two modes.  The first is when an index exists for the
046 * attribute the assertion is built on.  The second is when the user index for
047 * the assertion attribute does not exist.  Different Cursors are used in each
048 * of these cases where the other remains null.
049 *
050 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
051 */
052public class GreaterEqCursor<V> extends AbstractIndexCursor<V>
053{
054    /** A dedicated log for cursors */
055    private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
056
057    /** Speedup for logs */
058    private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
059
060    private static final String UNSUPPORTED_MSG = "GreaterEqCursors only support positioning by element when a user index exists on the asserted attribute.";
061
062    /** An greater eq evaluator for candidates */
063    private final GreaterEqEvaluator<V> greaterEqEvaluator;
064
065    /** Cursor over attribute entry matching filter: set when index present */
066    private final Cursor<IndexEntry<V, String>> userIdxCursor;
067
068    /** NDN Cursor on all entries in  (set when no index on user attribute) */
069    private final Cursor<IndexEntry<String, String>> uuidIdxCursor;
070
071    /**
072     * Used to store indexEntry from uuidCandidate so it can be saved after
073     * call to evaluate() which changes the value so it's not referring to
074     * the NDN but to the value of the attribute instead.
075     */
076    private IndexEntry<String, String> uuidCandidate;
077
078
079    /**
080     * Creates a new instance of an GreaterEqCursor
081     * 
082     * @param partitionTxn The transaction to use
083     * @param store The store
084     * @param greaterEqEvaluator The GreaterEqEvaluator
085     * @throws LdapException If the creation failed
086     * @throws IndexNotFoundException If the index was not found
087     */
088    @SuppressWarnings("unchecked")
089    public GreaterEqCursor( PartitionTxn partitionTxn, Store store, GreaterEqEvaluator<V> greaterEqEvaluator ) 
090        throws LdapException, IndexNotFoundException
091    {
092        if ( IS_DEBUG )
093        {
094            LOG_CURSOR.debug( "Creating GreaterEqCursor {}", this );
095        }
096
097        this.greaterEqEvaluator = greaterEqEvaluator;
098        this.partitionTxn = partitionTxn;
099
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().getValue() ) <= 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().getValue() );
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().getValue() );
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    public boolean previous() throws LdapException, CursorException
278    {
279        checkNotClosed();
280
281        if ( userIdxCursor != null )
282        {
283            /*
284             * We have to check and make sure the previous value complies by
285             * being greater than or eq to the expression node's value
286             */
287            while ( userIdxCursor.previous() )
288            {
289                checkNotClosed();
290                IndexEntry<?, String> candidate = userIdxCursor.get();
291
292                if ( greaterEqEvaluator.getComparator().compare( candidate.getKey(),
293                    greaterEqEvaluator.getExpression().getValue().getValue() ) >= 0 )
294                {
295                    return setAvailable( true );
296                }
297            }
298
299            return setAvailable( false );
300        }
301
302        while ( uuidIdxCursor.previous() )
303        {
304            checkNotClosed();
305            uuidCandidate = uuidIdxCursor.get();
306
307            if ( greaterEqEvaluator.evaluate( partitionTxn, uuidCandidate ) )
308            {
309                return setAvailable( true );
310            }
311        }
312
313        return setAvailable( false );
314    }
315
316
317    /**
318     * {@inheritDoc}
319     */
320    public boolean next() throws LdapException, CursorException
321    {
322        checkNotClosed();
323
324        if ( userIdxCursor != null )
325        {
326            /*
327             * No need to do the same check that is done in previous() since
328             * values are increasing with calls to next().
329             */
330            return setAvailable( userIdxCursor.next() );
331        }
332
333        while ( uuidIdxCursor.next() )
334        {
335            checkNotClosed();
336            uuidCandidate = uuidIdxCursor.get();
337
338            if ( greaterEqEvaluator.evaluate( partitionTxn, uuidCandidate ) )
339            {
340                return setAvailable( true );
341            }
342        }
343
344        return setAvailable( false );
345    }
346
347
348    /**
349     * {@inheritDoc}
350     */
351    public IndexEntry<V, String> get() throws CursorException
352    {
353        checkNotClosed();
354
355        if ( userIdxCursor != null )
356        {
357            if ( available() )
358            {
359                return userIdxCursor.get();
360            }
361
362            throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
363        }
364
365        if ( available() )
366        {
367            return ( IndexEntry<V, String> ) uuidCandidate;
368        }
369
370        throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
371    }
372
373
374    /**
375     * {@inheritDoc}
376     */
377    @Override
378    public void close() throws IOException
379    {
380        if ( IS_DEBUG )
381        {
382            LOG_CURSOR.debug( "Closing GreaterEqCursor {}", this );
383        }
384
385        super.close();
386
387        if ( userIdxCursor != null )
388        {
389            userIdxCursor.close();
390        }
391        else
392        {
393            uuidIdxCursor.close();
394            uuidCandidate = null;
395        }
396    }
397
398
399    /**
400     * {@inheritDoc}
401     */
402    @Override
403    public void close( Exception cause ) throws IOException
404    {
405        if ( IS_DEBUG )
406        {
407            LOG_CURSOR.debug( "Closing GreaterEqCursor {}", this );
408        }
409
410        super.close( cause );
411
412        if ( userIdxCursor != null )
413        {
414            userIdxCursor.close( cause );
415        }
416        else
417        {
418            uuidIdxCursor.close( cause );
419            uuidCandidate = null;
420        }
421    }
422
423
424    /**
425     * @see Object#toString()
426     */
427    @Override
428    public String toString( String tabs )
429    {
430        StringBuilder sb = new StringBuilder();
431
432        sb.append( tabs ).append( "GreaterEqCursor (" );
433
434        if ( available() )
435        {
436            sb.append( "available)" );
437        }
438        else
439        {
440            sb.append( "absent)" );
441        }
442
443        sb.append( "#candidate<" ).append( uuidCandidate ).append( ">:\n" );
444
445        sb.append( tabs + "  >>" ).append( greaterEqEvaluator ).append( '\n' );
446
447        if ( userIdxCursor != null )
448        {
449            sb.append( tabs + "  <user>\n" );
450            sb.append( userIdxCursor.toString( tabs + "    " ) );
451        }
452
453        if ( uuidIdxCursor != null )
454        {
455            sb.append( tabs + "  <uuid>\n" );
456            sb.append( uuidIdxCursor.toString( tabs + "  " ) );
457        }
458
459        return sb.toString();
460    }
461
462
463    /**
464     * @see Object#toString()
465     */
466    public String toString()
467    {
468        return toString( "" );
469    }
470}