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.impl.avl;
021
022
023import java.io.IOException;
024
025import org.apache.directory.api.ldap.model.constants.Loggers;
026import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
027import org.apache.directory.api.ldap.model.cursor.Cursor;
028import org.apache.directory.api.ldap.model.cursor.CursorException;
029import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
030import org.apache.directory.api.ldap.model.cursor.SingletonCursor;
031import org.apache.directory.api.ldap.model.cursor.Tuple;
032import org.apache.directory.api.ldap.model.exception.LdapException;
033import org.apache.directory.server.core.avltree.AvlSingletonOrOrderedSetCursor;
034import org.apache.directory.server.core.avltree.AvlTree;
035import org.apache.directory.server.core.avltree.AvlTreeCursor;
036import org.apache.directory.server.core.avltree.SingletonOrOrderedSet;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040
041/**
042 * A Cursor which walks and advance over AvlTables that may contain duplicate
043 * keys with values stored in an AvlTree.  All duplicate keys are traversed
044 * returning the key and the value in a Tuple.
045 *
046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
047 */
048public class AvlTableDupsCursor<K, V> extends AbstractCursor<Tuple<K, V>>
049{
050    private static final Logger LOG = LoggerFactory.getLogger( AvlTableDupsCursor.class );
051
052    /** A dedicated log for cursors */
053    private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
054
055    /** Speedup for logs */
056    private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
057
058    /** The AVL backed table this Cursor traverses over. */
059    private final AvlTable<K, V> table;
060
061    /**
062     * The underlying wrapped cursor which returns Tuples whose values are
063     * either V objects or AvlTree objects.
064     */
065    private final AvlSingletonOrOrderedSetCursor<K, V> wrappedCursor;
066
067    /**
068     * A Cursor over a set of value objects for the current key held in the
069     * containerTuple.  A new Cursor will be set for each new key as we
070     * traverse.  The Cursor traverses over either a AvlTree object full
071     * of values in a multi-valued key or it traverses over a BTree which
072     * contains the values in the key field of it's Tuples.
073     */
074    private Cursor<V> dupsCursor;
075
076    /** The current Tuple returned from the wrapped cursor. */
077    private final Tuple<K, SingletonOrOrderedSet<V>> wrappedTuple = new Tuple<>();
078
079    /**
080     * The Tuple that is used to return values via the get() method. This
081     * same Tuple instance will be returned every time.  At different
082     * positions it may return different values for the same key.
083     */
084    private final Tuple<K, V> returnedTuple = new Tuple<>();
085
086    /** Whether or not a value is available when get() is called. */
087    private boolean valueAvailable;
088
089
090    /**
091     * Creates a new instance of AvlTableDupsCursor.
092     *
093     * @param table the AvlTable to build a Cursor on.
094     */
095    public AvlTableDupsCursor( AvlTable<K, V> table )
096    {
097        if ( IS_DEBUG )
098        {
099            LOG_CURSOR.debug( "Creating AvlTableDupsCursor {}", this );
100        }
101
102        this.table = table;
103        this.wrappedCursor = new AvlSingletonOrOrderedSetCursor<>( table.getAvlTreeMap() );
104        LOG.debug( "Created on table {}", table.getName() );
105    }
106
107
108    /**
109     * {@inheritDoc}
110     */
111    public boolean available()
112    {
113        return valueAvailable;
114    }
115
116
117    /**
118     * {@inheritDoc}
119     */
120    public void beforeKey( K key ) throws Exception
121    {
122        beforeValue( key, null );
123    }
124
125
126    /**
127     * {@inheritDoc}
128     */
129    public void beforeValue( K key, V value ) throws LdapException, CursorException
130    {
131        checkNotClosed();
132        wrappedCursor.beforeKey( key );
133
134        if ( dupsCursor != null )
135        {
136            try
137            {
138                dupsCursor.close();
139            }
140            catch ( IOException ioe )
141            {
142                throw new LdapException( ioe.getMessage(), ioe );
143            }
144        }
145
146        if ( wrappedCursor.next() )
147        {
148            wrappedTuple.setBoth( wrappedCursor.get() );
149
150            if ( wrappedTuple.getValue().isOrderedSet() )
151            {
152                AvlTree<V> avlTree = wrappedTuple.getValue().getOrderedSet();
153                dupsCursor = new AvlTreeCursor<>( avlTree );
154            }
155            else
156            {
157                dupsCursor = new SingletonCursor<>(
158                    wrappedTuple.getValue().getSingleton(), wrappedCursor.getValuComparator() );
159            }
160
161            if ( value == null )
162            {
163                clearValue();
164                return;
165            }
166
167            /*
168             * The cursor over the values is only advanced if we're on the
169             * same key as the primary cursor.  This is because we want this
170             * method to really position us within a set of duplicate key
171             * entries at the proper value.
172             */
173            if ( table.getKeyComparator().compare( wrappedTuple.getKey(), key ) == 0 )
174            {
175                dupsCursor.before( value );
176            }
177
178            clearValue();
179            return;
180        }
181
182        clearValue();
183        wrappedTuple.setKey( null );
184        wrappedTuple.setValue( null );
185    }
186
187
188    /**
189     * {@inheritDoc}
190     */
191    public void afterKey( K key ) throws Exception
192    {
193        afterValue( key, null );
194    }
195
196
197    /**
198     * {@inheritDoc}
199     */
200    public void afterValue( K key, V value ) throws LdapException, CursorException
201    {
202        checkNotClosed();
203
204        if ( dupsCursor != null )
205        {
206            try
207            {
208                dupsCursor.close();
209            }
210            catch ( IOException ioe )
211            {
212                throw new LdapException( ioe.getMessage(), ioe );
213            }
214        }
215
216        /*
217         * There is a subtle difference between after and before handling
218         * with dupicate key values.  Say we have the following tuples:
219         *
220         * (0, 0)
221         * (1, 1)
222         * (1, 2)
223         * (1, 3)
224         * (2, 2)
225         *
226         * If we request an after cursor on (1, 2).  We must make sure that
227         * the container cursor does not advance after the entry with key 1
228         * since this would result in us skip returning (1. 3) on the call to
229         * next which will incorrectly return (2, 2) instead.
230         *
231         * So if the value is null in the element then we don't care about
232         * this obviously since we just want to advance past the duplicate key
233         * values all together.  But when it is not null, then we want to
234         * go right before this key instead of after it.
235         */
236
237        if ( value == null )
238        {
239            wrappedCursor.afterKey( key );
240        }
241        else
242        {
243            wrappedCursor.beforeKey( key );
244        }
245
246        if ( wrappedCursor.next() )
247        {
248            wrappedTuple.setBoth( wrappedCursor.get() );
249            SingletonOrOrderedSet<V> values = wrappedTuple.getValue();
250
251            if ( values.isOrderedSet() )
252            {
253                AvlTree<V> set = values.getOrderedSet();
254                dupsCursor = new AvlTreeCursor<>( set );
255            }
256            else
257            {
258                dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() );
259            }
260
261            if ( value == null )
262            {
263                clearValue();
264
265                return;
266            }
267
268            // only advance the dupsCursor if we're on same key
269            if ( table.getKeyComparator().compare( wrappedTuple.getKey(), key ) == 0 )
270            {
271                dupsCursor.after( value );
272            }
273
274            clearValue();
275            return;
276        }
277
278        clearValue();
279        wrappedTuple.setKey( null );
280        wrappedTuple.setValue( null );
281    }
282
283
284    /**
285     * {@inheritDoc}
286     */
287    public void after( Tuple<K, V> element ) throws LdapException, CursorException
288    {
289        afterValue( element.getKey(), element.getValue() );
290    }
291
292
293    /**
294     * {@inheritDoc}
295     */
296    public void afterLast() throws LdapException, CursorException
297    {
298        checkNotClosed();
299        clearValue();
300        wrappedCursor.afterLast();
301        wrappedTuple.setKey( null );
302        wrappedTuple.setValue( null );
303
304        if ( dupsCursor != null )
305        {
306            try
307            {            
308                dupsCursor.close();
309            }
310            catch ( IOException ioe )
311            {
312                throw new LdapException( ioe.getMessage(), ioe );
313            }
314        }
315
316        dupsCursor = null;
317    }
318
319
320    /**
321     * {@inheritDoc}
322     */
323    public void before( Tuple<K, V> element ) throws LdapException, CursorException
324    {
325        beforeValue( element.getKey(), element.getValue() );
326    }
327
328
329    /**
330     * {@inheritDoc}
331     */
332    public void beforeFirst() throws LdapException, CursorException
333    {
334        checkNotClosed();
335        clearValue();
336        wrappedCursor.beforeFirst();
337        wrappedTuple.setKey( null );
338        wrappedTuple.setValue( null );
339
340        if ( dupsCursor != null )
341        {
342            try
343            {
344                dupsCursor.close();
345            }
346            catch ( IOException ioe )
347            {
348                throw new LdapException( ioe.getMessage(), ioe );
349            }
350        }
351
352        dupsCursor = null;
353    }
354
355
356    /**
357     * {@inheritDoc}
358     */
359    public boolean first() throws LdapException, CursorException
360    {
361        checkNotClosed();
362        clearValue();
363
364        if ( dupsCursor != null )
365        {
366            try
367            {
368                dupsCursor.close();
369            }
370            catch ( IOException ioe )
371            {
372                throw new LdapException( ioe.getMessage(), ioe );
373            }
374        }
375
376        dupsCursor = null;
377
378        if ( wrappedCursor.first() )
379        {
380            wrappedTuple.setBoth( wrappedCursor.get() );
381            SingletonOrOrderedSet<V> values = wrappedTuple.getValue();
382
383            if ( values.isOrderedSet() )
384            {
385                dupsCursor = new AvlTreeCursor<>( values.getOrderedSet() );
386            }
387            else
388            {
389                dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() );
390            }
391
392            /*
393             * Since only tables with duplicate keys enabled use this
394             * cursor, entries must have at least one value, and therefore
395             * call to last() will always return true.
396             */
397            dupsCursor.first();
398            valueAvailable = true;
399            returnedTuple.setKey( wrappedTuple.getKey() );
400            returnedTuple.setValue( dupsCursor.get() );
401
402            return true;
403        }
404
405        return false;
406    }
407
408
409    /**
410     * {@inheritDoc}
411     */
412    public Tuple<K, V> get() throws CursorException
413    {
414        checkNotClosed();
415
416        if ( !valueAvailable )
417        {
418            throw new InvalidCursorPositionException();
419        }
420
421        return returnedTuple;
422    }
423
424
425    /**
426     * {@inheritDoc}
427     */
428    public boolean last() throws LdapException, CursorException
429    {
430        checkNotClosed();
431        clearValue();
432
433        if ( dupsCursor != null )
434        {
435            try
436            {
437                dupsCursor.close();
438            }
439            catch ( IOException ioe )
440            {
441                throw new LdapException( ioe.getMessage(), ioe );
442            }
443        }
444
445        if ( wrappedCursor.last() )
446        {
447            wrappedTuple.setBoth( wrappedCursor.get() );
448            SingletonOrOrderedSet<V> values = wrappedTuple.getValue();
449
450            if ( values.isOrderedSet() )
451            {
452                dupsCursor = new AvlTreeCursor<>( values.getOrderedSet() );
453            }
454            else
455            {
456                dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() );
457            }
458
459            /*
460             * Since only tables with duplicate keys enabled use this
461             * cursor, entries must have at least one value, and therefore
462             * call to last() will always return true.
463             */
464            dupsCursor.last();
465            valueAvailable = true;
466            returnedTuple.setKey( wrappedTuple.getKey() );
467            returnedTuple.setValue( dupsCursor.get() );
468
469            return true;
470        }
471
472        return false;
473    }
474
475
476    /**
477     * {@inheritDoc}
478     */
479    public boolean next() throws LdapException, CursorException
480    {
481        checkNotClosed();
482
483        /*
484         * If the cursor over the values of the current key is null or is
485         * extinguished then we need to advance to the next key.
486         */
487        if ( null == dupsCursor || !dupsCursor.next() )
488        {
489            if ( dupsCursor != null )
490            {
491                try
492                {
493                    dupsCursor.close();
494                }
495                catch ( IOException ioe )
496                {
497                    throw new LdapException( ioe.getMessage(), ioe );
498                }
499            }
500
501            /*
502             * If the wrappedCursor cursor has more elements we get the next
503             * key/AvlTree Tuple to work with and get a cursor over it.
504             */
505            if ( wrappedCursor.next() )
506            {
507                wrappedTuple.setBoth( wrappedCursor.get() );
508                SingletonOrOrderedSet<V> values = wrappedTuple.getValue();
509
510                if ( values.isOrderedSet() )
511                {
512                    dupsCursor = new AvlTreeCursor<>( values.getOrderedSet() );
513                }
514                else
515                {
516                    dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() );
517                }
518
519                /*
520                 * Since only tables with duplicate keys enabled use this
521                 * cursor, entries must have at least one value, and therefore
522                 * call to next() after bringing the cursor to beforeFirst()
523                 * will always return true.
524                 */
525                dupsCursor.beforeFirst();
526                dupsCursor.next();
527            }
528            else
529            {
530                dupsCursor = null;
531
532                return false;
533            }
534        }
535
536        /*
537         * If we get to this point then cursor has more elements and
538         * wrappedTuple holds the Tuple containing the key and the
539         * AvlTree of values for that key which the Cursor traverses.  All we
540         * need to do is populate our tuple object with the key and the value
541         * in the cursor.
542         */
543        returnedTuple.setKey( wrappedTuple.getKey() );
544        returnedTuple.setValue( dupsCursor.get() );
545
546        valueAvailable = true;
547        return true;
548    }
549
550
551    /**
552     * {@inheritDoc}
553     */
554    public boolean previous() throws LdapException, CursorException
555    {
556        checkNotClosed();
557        /*
558         * If the cursor over the values of the current key is null or is
559         * extinguished then we need to advance to the previous key.
560         */
561        if ( ( null == dupsCursor ) || !dupsCursor.previous() )
562        {
563            if ( dupsCursor != null )
564            {
565                try
566                {
567                    dupsCursor.close();
568                }
569                catch ( IOException ioe )
570                {
571                    throw new LdapException( ioe.getMessage(), ioe );
572                }
573            }
574
575            /*
576             * If the wrappedCursor cursor has more elements we get the previous
577             * key/AvlTree Tuple to work with and get a cursor over it's
578             * values.
579             */
580            if ( wrappedCursor.previous() )
581            {
582                wrappedTuple.setBoth( wrappedCursor.get() );
583                SingletonOrOrderedSet<V> values = wrappedTuple.getValue();
584
585                if ( values.isOrderedSet() )
586                {
587                    dupsCursor = new AvlTreeCursor<>( values.getOrderedSet() );
588                }
589                else
590                {
591                    dupsCursor = new SingletonCursor<>( values.getSingleton(), wrappedCursor.getValuComparator() );
592                }
593
594                /*
595                 * Since only tables with duplicate keys enabled use this
596                 * cursor, entries must have at least one value, and therefore
597                 * call to previous() after bringing the cursor to afterLast()
598                 * will always return true.
599                 */
600                dupsCursor.afterLast();
601                dupsCursor.previous();
602            }
603            else
604            {
605                dupsCursor = null;
606
607                return false;
608            }
609        }
610
611        returnedTuple.setKey( wrappedTuple.getKey() );
612        returnedTuple.setValue( dupsCursor.get() );
613
614        valueAvailable = true;
615        return true;
616    }
617
618
619    @Override
620    public void close() throws IOException
621    {
622        if ( IS_DEBUG )
623        {
624            LOG_CURSOR.debug( "Closing AvlTableDupsCursor {}", this );
625        }
626
627        if ( dupsCursor != null )
628        {
629            dupsCursor.close();
630        }
631
632        wrappedCursor.close();
633    }
634
635
636    @Override
637    public void close( Exception reason ) throws IOException
638    {
639        if ( IS_DEBUG )
640        {
641            LOG_CURSOR.debug( "Closing AvlTableDupsCursor {}", this );
642        }
643
644        if ( dupsCursor != null )
645        {
646            dupsCursor.close();
647        }
648
649        wrappedCursor.close();
650    }
651
652
653    /**
654     * Clears the returned Tuple and makes sure valueAvailable returns false.
655     */
656    private void clearValue()
657    {
658        returnedTuple.setKey( null );
659        returnedTuple.setValue( null );
660        valueAvailable = false;
661    }
662}