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().getValue() );
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().getValue() );
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().getValue() );
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    public boolean previous() throws LdapException, CursorException
287    {
288        checkNotClosed();
289
290        if ( userIdxCursor != null )
291        {
292            /*
293             * No need to do the same check that is done in next() since
294             * values are decreasing with calls to previous().  We will
295             * always have lesser values.
296             */
297            return setAvailable( userIdxCursor.previous() );
298        }
299
300        while ( uuidIdxCursor.previous() )
301        {
302            checkNotClosed();
303            uuidCandidate = uuidIdxCursor.get();
304
305            if ( lessEqEvaluator.evaluate( partitionTxn, uuidCandidate ) )
306            {
307                return setAvailable( true );
308            }
309            else
310            {
311                uuidCandidate = null;
312            }
313        }
314
315        return setAvailable( false );
316    }
317
318
319    /**
320     * {@inheritDoc}
321     */
322    public boolean next() throws LdapException, CursorException
323    {
324        checkNotClosed();
325
326        if ( userIdxCursor != null )
327        {
328            /*
329             * We have to check and make sure the next value complies by
330             * being less than or eq to the expression node's value.  We need
331             * to do this since values are increasing and we must limit to our
332             * upper bound.
333             */
334            while ( userIdxCursor.next() )
335            {
336                checkNotClosed();
337                IndexEntry<?, String> candidate = userIdxCursor.get();
338
339                if ( lessEqEvaluator.getComparator().compare( candidate.getKey(),
340                    lessEqEvaluator.getExpression().getValue().getValue() ) <= 0 )
341                {
342                    return setAvailable( true );
343                }
344            }
345
346            return setAvailable( false );
347        }
348
349        while ( uuidIdxCursor.next() )
350        {
351            checkNotClosed();
352            uuidCandidate = uuidIdxCursor.get();
353
354            if ( lessEqEvaluator.evaluate( partitionTxn, uuidCandidate ) )
355            {
356                return setAvailable( true );
357            }
358            else
359            {
360                uuidCandidate = null;
361            }
362        }
363
364        return setAvailable( false );
365    }
366
367
368    /**
369     * {@inheritDoc}
370     */
371    public IndexEntry<V, String> get() throws CursorException
372    {
373        checkNotClosed();
374
375        if ( userIdxCursor != null )
376        {
377            if ( available() )
378            {
379                return userIdxCursor.get();
380            }
381
382            throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
383        }
384
385        if ( available() )
386        {
387            return ( IndexEntry<V, String> ) uuidCandidate;
388        }
389
390        throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
391    }
392
393
394    /**
395     * {@inheritDoc}
396     */
397    @Override
398    public void close() throws IOException
399    {
400        if ( IS_DEBUG )
401        {
402            LOG_CURSOR.debug( "Closing LessEqCursor {}", this );
403        }
404
405        super.close();
406
407        if ( userIdxCursor != null )
408        {
409            userIdxCursor.close();
410        }
411        else
412        {
413            uuidIdxCursor.close();
414            uuidCandidate = null;
415        }
416    }
417
418
419    /**
420     * {@inheritDoc}
421     */
422    @Override
423    public void close( Exception cause ) throws IOException
424    {
425        LOG_CURSOR.debug( "Closing LessEqCursor {}", this );
426        super.close( cause );
427
428        if ( userIdxCursor != null )
429        {
430            userIdxCursor.close( cause );
431        }
432        else
433        {
434            uuidIdxCursor.close( cause );
435            uuidCandidate = null;
436        }
437    }
438
439
440    /**
441     * @see Object#toString()
442     */
443    @Override
444    public String toString( String tabs )
445    {
446        StringBuilder sb = new StringBuilder();
447
448        sb.append( tabs ).append( "LessEqCursor (" );
449
450        if ( available() )
451        {
452            sb.append( "available)" );
453        }
454        else
455        {
456            sb.append( "absent)" );
457        }
458
459        sb.append( "#candidate<" ).append( uuidCandidate ).append( ">:\n" );
460
461        sb.append( tabs + "  >>" ).append( lessEqEvaluator ).append( '\n' );
462
463        if ( userIdxCursor != null )
464        {
465            sb.append( tabs + "  <user>\n" );
466            sb.append( userIdxCursor.toString( tabs + "    " ) );
467        }
468
469        if ( uuidIdxCursor != null )
470        {
471            sb.append( tabs + "  <uuid>\n" );
472            sb.append( uuidIdxCursor.toString( tabs + "  " ) );
473        }
474
475        return sb.toString();
476    }
477
478
479    /**
480     * @see Object#toString()
481     */
482    public String toString()
483    {
484        return toString( "" );
485    }
486}