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.LessEqEvaluator;
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042
043/**
044 * A Cursor over entry candidates matching a LessEq 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 LessEqCursor<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 = I18n.err( I18n.ERR_716 );
061
062    /** An less eq evaluator for candidates */
063    private final LessEqEvaluator<V> lessEqEvaluator;
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 uudCandidate so it can be saved after
073     * call to evaluate() which changes the value so it's not referring to
074     * the String 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 LessEqCursor
081     * 
082     * @param partitionTxn The transaction to use
083     * @param store The store
084     * @param lessEqEvaluator The LessEqEvaluator
085     * @throws LdapException If the creation failed
086     * @throws IndexNotFoundException If the index was not found
087     */
088    @SuppressWarnings("unchecked")
089    public LessEqCursor( PartitionTxn partitionTxn, Store store, LessEqEvaluator<V> lessEqEvaluator ) 
090        throws LdapException, IndexNotFoundException
091    {
092        if ( IS_DEBUG )
093        {
094            LOG_CURSOR.debug( "Creating LessEqCursor {}", this );
095        }
096
097        this.lessEqEvaluator = lessEqEvaluator;
098        this.partitionTxn = partitionTxn;
099
100        AttributeType attributeType = lessEqEvaluator.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 expression node.  If the
138             * element's value is greater than this upper bound then we
139             * position the userIdxCursor after the last node.
140             *
141             * If the element's value is equal to this upper bound then we
142             * position the userIdxCursor right before the last node.
143             *
144             * If the element's value is smaller, then we delegate to the
145             * before() method of the userIdxCursor.
146             */
147            int compareValue = lessEqEvaluator.getComparator().compare( element.getKey(),
148                lessEqEvaluator.getExpression().getValue().getString() );
149
150            if ( compareValue > 0 )
151            {
152                afterLast();
153                return;
154            }
155            else if ( compareValue == 0 )
156            {
157                last();
158                previous();
159                setAvailable( false );
160                return;
161            }
162
163            userIdxCursor.before( element );
164            setAvailable( false );
165        }
166        else
167        {
168            super.before( element );
169        }
170    }
171
172
173    /**
174     * {@inheritDoc}
175     */
176    @Override
177    public void after( IndexEntry<V, String> element ) throws LdapException, CursorException
178    {
179        checkNotClosed();
180
181        if ( userIdxCursor != null )
182        {
183            int comparedValue = lessEqEvaluator.getComparator().compare( element.getKey(),
184                lessEqEvaluator.getExpression().getValue().getString() );
185
186            /*
187             * First we need to check and make sure this element is within
188             * bounds as mandated by the assertion node.  To do so we compare
189             * it's value with the value of the expression node.
190             *
191             * If the element's value is equal to or greater than this upper
192             * bound then we position the userIdxCursor after the last node.
193             *
194             * If the element's value is smaller, then we delegate to the
195             * after() method of the userIdxCursor.
196             */
197            if ( comparedValue >= 0 )
198            {
199                afterLast();
200                return;
201            }
202
203            // Element is in the valid range as specified by assertion
204            userIdxCursor.after( element );
205            setAvailable( false );
206        }
207        else
208        {
209            super.after( element );
210        }
211    }
212
213
214    /**
215     * {@inheritDoc}
216     */
217    public void beforeFirst() throws LdapException, CursorException
218    {
219        checkNotClosed();
220        
221        if ( userIdxCursor != null )
222        {
223            userIdxCursor.beforeFirst();
224        }
225        else
226        {
227            uuidIdxCursor.beforeFirst();
228            uuidCandidate = null;
229        }
230
231        setAvailable( false );
232    }
233
234
235    /**
236     * {@inheritDoc}
237     */
238    public void afterLast() throws LdapException, CursorException
239    {
240        checkNotClosed();
241        
242        if ( userIdxCursor != null )
243        {
244            IndexEntry<V, String> advanceTo = new IndexEntry<>();
245            //noinspection unchecked
246            String normalizedKey = lessEqEvaluator.getAttributeType().getEquality().getNormalizer().normalize( 
247                lessEqEvaluator.getExpression().getValue().getString() );
248            
249            advanceTo.setKey( ( V ) normalizedKey );
250            userIdxCursor.after( advanceTo );
251        }
252        else
253        {
254            uuidIdxCursor.afterLast();
255            uuidCandidate = null;
256        }
257
258        setAvailable( false );
259    }
260
261
262    /**
263     * {@inheritDoc}
264     */
265    public boolean first() throws LdapException, CursorException
266    {
267        beforeFirst();
268        return next();
269    }
270
271
272    /**
273     * {@inheritDoc}
274     */
275    public boolean last() throws LdapException, CursorException
276    {
277        afterLast();
278
279        return previous();
280    }
281
282
283    /**
284     * {@inheritDoc}
285     */
286    @Override
287    public boolean previous() throws LdapException, CursorException
288    {
289        checkNotClosed();
290
291        if ( userIdxCursor != null )
292        {
293            /*
294             * No need to do the same check that is done in next() since
295             * values are decreasing with calls to previous().  We will
296             * always have lesser values.
297             */
298            return setAvailable( userIdxCursor.previous() );
299        }
300
301        while ( uuidIdxCursor.previous() )
302        {
303            checkNotClosed();
304            uuidCandidate = uuidIdxCursor.get();
305
306            if ( lessEqEvaluator.evaluate( partitionTxn, uuidCandidate ) )
307            {
308                return setAvailable( true );
309            }
310            else
311            {
312                uuidCandidate = null;
313            }
314        }
315
316        return setAvailable( false );
317    }
318
319
320    /**
321     * {@inheritDoc}
322     */
323    @Override
324    public boolean next() throws LdapException, CursorException
325    {
326        checkNotClosed();
327
328        if ( userIdxCursor != null )
329        {
330            /*
331             * We have to check and make sure the next value complies by
332             * being less than or eq to the expression node's value.  We need
333             * to do this since values are increasing and we must limit to our
334             * upper bound.
335             */
336            while ( userIdxCursor.next() )
337            {
338                checkNotClosed();
339                IndexEntry<?, String> candidate = userIdxCursor.get();
340
341                if ( lessEqEvaluator.getComparator().compare( candidate.getKey(),
342                    lessEqEvaluator.getExpression().getValue().getString() ) <= 0 )
343                {
344                    return setAvailable( true );
345                }
346            }
347
348            return setAvailable( false );
349        }
350
351        while ( uuidIdxCursor.next() )
352        {
353            checkNotClosed();
354            uuidCandidate = uuidIdxCursor.get();
355
356            if ( lessEqEvaluator.evaluate( partitionTxn, uuidCandidate ) )
357            {
358                return setAvailable( true );
359            }
360            else
361            {
362                uuidCandidate = null;
363            }
364        }
365
366        return setAvailable( false );
367    }
368
369
370    /**
371     * {@inheritDoc}
372     */
373    public IndexEntry<V, String> get() throws CursorException
374    {
375        checkNotClosed();
376
377        if ( userIdxCursor != null )
378        {
379            if ( available() )
380            {
381                return userIdxCursor.get();
382            }
383
384            throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
385        }
386
387        if ( available() )
388        {
389            return ( IndexEntry<V, String> ) uuidCandidate;
390        }
391
392        throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
393    }
394
395
396    /**
397     * {@inheritDoc}
398     */
399    @Override
400    public void close() throws IOException
401    {
402        if ( IS_DEBUG )
403        {
404            LOG_CURSOR.debug( "Closing LessEqCursor {}", this );
405        }
406
407        super.close();
408
409        if ( userIdxCursor != null )
410        {
411            userIdxCursor.close();
412        }
413        else
414        {
415            uuidIdxCursor.close();
416            uuidCandidate = null;
417        }
418    }
419
420
421    /**
422     * {@inheritDoc}
423     */
424    @Override
425    public void close( Exception cause ) throws IOException
426    {
427        LOG_CURSOR.debug( "Closing LessEqCursor {}", this );
428        super.close( cause );
429
430        if ( userIdxCursor != null )
431        {
432            userIdxCursor.close( cause );
433        }
434        else
435        {
436            uuidIdxCursor.close( cause );
437            uuidCandidate = null;
438        }
439    }
440
441
442    /**
443     * @see Object#toString()
444     */
445    @Override
446    public String toString( String tabs )
447    {
448        StringBuilder sb = new StringBuilder();
449
450        sb.append( tabs ).append( "LessEqCursor (" );
451
452        if ( available() )
453        {
454            sb.append( "available)" );
455        }
456        else
457        {
458            sb.append( "absent)" );
459        }
460
461        sb.append( "#candidate<" ).append( uuidCandidate ).append( ">:\n" );
462
463        sb.append( tabs + "  >>" ).append( lessEqEvaluator ).append( '\n' );
464
465        if ( userIdxCursor != null )
466        {
467            sb.append( tabs + "  <user>\n" );
468            sb.append( userIdxCursor.toString( tabs + "    " ) );
469        }
470
471        if ( uuidIdxCursor != null )
472        {
473            sb.append( tabs + "  <uuid>\n" );
474            sb.append( uuidIdxCursor.toString( tabs + "  " ) );
475        }
476
477        return sb.toString();
478    }
479
480
481    /**
482     * @see Object#toString()
483     */
484    public String toString()
485    {
486        return toString( "" );
487    }
488}