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.core.partition.impl.btree.jdbm;
021    
022    
023    import org.apache.directory.server.i18n.I18n;
024    import org.apache.directory.server.xdbm.Tuple;
025    import org.apache.directory.server.xdbm.AbstractTupleCursor;
026    import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException;
027    
028    import jdbm.helper.TupleBrowser;
029    
030    import java.io.IOException;
031    
032    
033    /**
034     * A cursor for browsing tables with duplicates which returns the container
035     * for values rather than just the value.
036     *
037     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
038     * @version $$Rev$$
039     */
040    public class DupsContainerCursor<K,V> extends AbstractTupleCursor<K, DupsContainer<V>>
041    {
042        private final JdbmTable<K,V> table;
043    
044        private jdbm.helper.Tuple jdbmTuple = new jdbm.helper.Tuple();
045        private Tuple<K,DupsContainer<V>> returnedTuple = new Tuple<K,DupsContainer<V>>();
046        private TupleBrowser browser;
047        private boolean valueAvailable;
048        private Boolean forwardDirection;
049    
050    
051        /**
052         * Creates a Cursor over the tuples of a JDBM table.
053         *
054         * @param table the JDBM Table to build a Cursor over
055         * @throws java.io.IOException of there are problems accessing the BTree
056         */
057        public DupsContainerCursor( JdbmTable<K,V> table ) throws IOException
058        {
059            if ( ! table.isDupsEnabled() )
060            {
061                throw new IllegalStateException( I18n.err( I18n.ERR_572 ) );
062            }
063    
064            this.table = table;
065        }
066    
067    
068        private void clearValue()
069        {
070            returnedTuple.setKey( null );
071            returnedTuple.setValue( null );
072            jdbmTuple.setKey( null );
073            jdbmTuple.setValue( null );
074            valueAvailable = false;
075        }
076    
077    
078        public boolean available()
079        {
080            return valueAvailable;
081        }
082    
083    
084        public void beforeKey( K key ) throws Exception
085        {
086            checkNotClosed( "beforeKey()" );
087            browser = table.getBTree().browse( key );
088            forwardDirection = null;
089            clearValue();
090        }
091    
092    
093        @SuppressWarnings("unchecked")
094        public void afterKey( K key ) throws Exception
095        {
096            browser = table.getBTree().browse( key );
097            forwardDirection = null;
098    
099            /*
100             * While the next value is less than or equal to the element keep
101             * advancing forward to the next item.  If we cannot advance any
102             * further then stop and return.  If we find a value greater than
103             * the element then we stop, backup, and return so subsequent calls
104             * to getNext() will return a value greater than the element.
105             */
106            while ( browser.getNext( jdbmTuple ) )
107            {
108                checkNotClosed( "afterKey()" );
109                K next = ( K ) jdbmTuple.getKey();
110    
111                int nextCompared = table.getKeyComparator().compare( next, key );
112    
113                if ( nextCompared > 0 )
114                {
115                    browser.getPrevious( jdbmTuple );
116    
117                    // switch in direction bug workaround: when a JDBM browser
118                    // switches direction with next then previous as is occuring
119                    // here then two previous moves are needed.
120                    browser.getPrevious( jdbmTuple );
121                    forwardDirection = false;
122                    clearValue();
123                    return;
124                }
125            }
126    
127            clearValue();
128        }
129    
130    
131        public void beforeValue( K key, DupsContainer<V> value ) throws Exception
132        {
133            throw new UnsupportedOperationException( I18n.err( I18n.ERR_573 ) );
134        }
135    
136    
137        public void afterValue( K key, DupsContainer<V> value ) throws Exception
138        {
139            throw new UnsupportedOperationException( I18n.err( I18n.ERR_573 ) );
140        }
141    
142    
143        /**
144         * Positions this Cursor before the key of the supplied tuple.
145         *
146         * @param element the tuple who's key is used to position this Cursor
147         * @throws IOException if there are failures to position the Cursor
148         */
149        public void before( Tuple<K,DupsContainer<V>> element ) throws Exception
150        {
151            beforeKey( element.getKey() );
152        }
153    
154    
155        public void after( Tuple<K,DupsContainer<V>> element ) throws Exception
156        {
157            afterKey( element.getKey() );
158        }
159    
160    
161        public void beforeFirst() throws Exception
162        {
163            checkNotClosed( "afterKey()" );
164            browser = table.getBTree().browse();
165            forwardDirection = null;
166            clearValue();
167        }
168    
169    
170        public void afterLast() throws Exception
171        {
172            checkNotClosed( "afterKey()" );
173            browser = table.getBTree().browse( null );
174            forwardDirection = null;
175            clearValue();
176        }
177    
178    
179        public boolean first() throws Exception
180        {
181            beforeFirst();
182            return next();
183        }
184    
185    
186        public boolean last() throws Exception
187        {
188            afterLast();
189            return previous();
190        }
191    
192    
193        @SuppressWarnings("unchecked")
194        public boolean previous() throws Exception
195        {
196            checkNotClosed( "previous()" );
197            if ( browser == null )
198            {
199                afterLast();
200            }
201    
202            boolean advanceSuccess = browser.getPrevious( jdbmTuple );
203    
204            // only want to set this if the advance is a success which means we
205            // are not at front
206            if ( forwardDirection == null && advanceSuccess )
207            {
208                forwardDirection = false;
209            }
210    
211            if ( forwardDirection != null && forwardDirection )
212            {
213                advanceSuccess = browser.getPrevious( jdbmTuple );
214                forwardDirection = false;
215            }
216    
217            if ( advanceSuccess )
218            {
219                returnedTuple.setKey( ( K ) jdbmTuple.getKey() );
220                returnedTuple.setValue( table.getDupsContainer( ( byte[] ) jdbmTuple.getValue() ) );
221                return valueAvailable = true;
222            }
223            else
224            {
225                clearValue();
226                return false;
227            }
228        }
229    
230    
231        @SuppressWarnings("unchecked")
232        public boolean next() throws Exception
233        {
234            checkNotClosed( "next()" );
235            if ( browser == null )
236            {
237                beforeFirst();
238            }
239    
240            boolean advanceSuccess = browser.getNext( jdbmTuple );
241    
242            // only want to set this if the advance is a success which means
243            // we are not at end
244            if ( forwardDirection == null && advanceSuccess )
245            {
246                forwardDirection = true;
247            }
248    
249            if ( forwardDirection != null && ! forwardDirection )
250            {
251                advanceSuccess = browser.getNext( jdbmTuple );
252                forwardDirection = true;
253            }
254    
255            if ( advanceSuccess )
256            {
257                returnedTuple.setKey( ( K ) jdbmTuple.getKey() );
258                returnedTuple.setValue( table.getDupsContainer( ( byte[] ) jdbmTuple.getValue() ) );
259                return valueAvailable = true;
260            }
261            else
262            {
263                clearValue();
264                return false;
265            }
266        }
267    
268    
269        public Tuple<K,DupsContainer<V>> get() throws Exception
270        {
271            checkNotClosed( "get()" );
272            if ( valueAvailable )
273            {
274                return returnedTuple;
275            }
276    
277            throw new InvalidCursorPositionException();
278        }
279    
280    
281        public boolean isElementReused()
282        {
283            return true;
284        }
285    }