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    package org.apache.directory.server.core.partition.impl.btree.jdbm;
020    
021    
022    import org.apache.directory.server.i18n.I18n;
023    import org.apache.directory.server.xdbm.Tuple;
024    import org.apache.directory.server.xdbm.AbstractTupleCursor;
025    import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException;
026    
027    import java.util.Comparator;
028    
029    import jdbm.helper.TupleBrowser;
030    import jdbm.btree.BTree;
031    
032    
033    /**
034     * Cursor over a set of values for the same key which are store in another
035     * BTree.  This Cursor is limited to the same key and it's tuples will always
036     * return the same key.
037     *
038     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
039     * @version $Rev$, $Date$
040     */
041    public class KeyTupleBTreeCursor<K,V> extends AbstractTupleCursor<K,V>
042    {
043        private final Comparator<V> comparator;
044        private final BTree btree;
045        private final K key;
046    
047        private jdbm.helper.Tuple valueTuple = new jdbm.helper.Tuple();
048        private Tuple<K,V> returnedTuple = new Tuple<K,V>();
049        private TupleBrowser browser;
050        private boolean valueAvailable;
051    
052    
053        /**
054         * Creates a Cursor over the tuples of a JDBM BTree.
055         *
056         * @param btree the JDBM BTree to build a Cursor over
057         * @param key the constant key for which values are returned
058         * @param comparator the Comparator used to determine <b>key</b> ordering
059         * @throws Exception of there are problems accessing the BTree
060         */
061        public KeyTupleBTreeCursor( BTree btree, K key, Comparator<V> comparator ) throws Exception
062        {
063            this.key = key;
064            this.btree = btree;
065            this.comparator = comparator;
066            this.browser = btree.browse();
067        }
068    
069    
070        private void clearValue()
071        {
072            returnedTuple.setKey( key );
073            returnedTuple.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            throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
087        }
088    
089    
090        public void afterKey( K key ) throws Exception
091        {
092            throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
093        }
094    
095    
096        public void beforeValue( K key, V value ) throws Exception
097        {
098            checkNotClosed( "beforeValue()" );
099            if ( key != null && ! key.equals( this.key ) )
100            {
101                throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
102            }
103    
104            browser = btree.browse( value );
105            clearValue();
106        }
107    
108    
109        @SuppressWarnings("unchecked")
110        public void afterValue( K key, V value ) throws Exception
111        {
112            if ( key != null && ! key.equals( this.key ) )
113            {
114                throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) );
115            }
116    
117            browser = btree.browse( value );
118    
119            /*
120             * While the next value is less than or equal to the element keep
121             * advancing forward to the next item.  If we cannot advance any
122             * further then stop and return.  If we find a value greater than
123             * the element then we stop, backup, and return so subsequent calls
124             * to getNext() will return a value greater than the element.
125             */
126            while ( browser.getNext( valueTuple ) )
127            {
128                checkNotClosed( "afterValue" );
129    
130                V next = ( V ) valueTuple.getKey();
131    
132                int nextCompared = comparator.compare( next, value );
133    
134                if ( nextCompared <= 0 )
135                {
136                    // just continue
137                }
138                else if ( nextCompared > 0 )
139                {
140                    /*
141                     * If we just have values greater than the element argument
142                     * then we are before the first element and cannot backup, and
143                     * the call below to getPrevious() will fail.  In this special
144                     * case we just reset the Cursor's browser and return.
145                     */
146                    if ( browser.getPrevious( valueTuple ) )
147                    {
148                    }
149                    else
150                    {
151                        browser = btree.browse( this.key );
152                    }
153    
154                    clearValue();
155                    return;
156                }
157            }
158    
159            clearValue();
160        }
161    
162    
163        /**
164         * Positions this Cursor over the same keys before the value of the
165         * supplied valueTuple.  The supplied element Tuple's key is not considered at
166         * all.
167         *
168         * @param element the valueTuple who's value is used to position this Cursor
169         * @throws Exception if there are failures to position the Cursor
170         */
171        public void before( Tuple<K,V> element ) throws Exception
172        {
173            checkNotClosed( "before()" );
174            browser = btree.browse( element.getValue() );
175            clearValue();
176        }
177    
178    
179        public void after( Tuple<K,V> element ) throws Exception
180        {
181            afterValue( key, element.getValue() );
182        }
183    
184    
185        public void beforeFirst() throws Exception
186        {
187            checkNotClosed( "beforeFirst()" );
188            browser = btree.browse();
189            clearValue();
190        }
191    
192    
193        public void afterLast() throws Exception
194        {
195            checkNotClosed( "afterLast()" );
196            browser = btree.browse( null );
197        }
198    
199    
200        public boolean first() throws Exception
201        {
202            beforeFirst();
203            return next();
204        }
205    
206    
207        public boolean last() throws Exception
208        {
209            afterLast();
210            return previous();
211        }
212    
213    
214        @SuppressWarnings("unchecked")
215        public boolean previous() throws Exception
216        {
217            checkNotClosed( "previous()" );
218            if ( browser.getPrevious( valueTuple ) )
219            {
220                // work around to fix direction change problem with jdbm browser
221                if ( returnedTuple.getValue() != null &&
222                    comparator.compare( ( V ) valueTuple.getKey(), returnedTuple.getValue() ) == 0 )
223                {
224                    browser.getPrevious( valueTuple ) ;
225                }
226                returnedTuple.setKey( key );
227                returnedTuple.setValue( ( V ) valueTuple.getKey() );
228                return valueAvailable = true;
229            }
230            else
231            {
232                clearValue();
233                return false;
234            }
235        }
236    
237    
238        @SuppressWarnings("unchecked")
239        public boolean next() throws Exception
240        {
241            checkNotClosed( "next()" );
242            if ( browser.getNext( valueTuple ) )
243            {
244                // work around to fix direction change problem with jdbm browser
245                if ( returnedTuple.getValue() != null &&
246                     comparator.compare( ( V ) valueTuple.getKey(), returnedTuple.getValue() ) == 0 )
247                {
248                    browser.getNext( valueTuple ) ;
249                }
250                returnedTuple.setKey( key );
251                returnedTuple.setValue( ( V ) valueTuple.getKey() );
252                return valueAvailable = true;
253            }
254            else
255            {
256                clearValue();
257                return false;
258            }
259        }
260    
261    
262        public Tuple<K,V> get() throws Exception
263        {
264            checkNotClosed( "get()" );
265            if ( valueAvailable )
266            {
267                return returnedTuple;
268            }
269    
270            throw new InvalidCursorPositionException();
271        }
272    
273    
274        public boolean isElementReused()
275        {
276            return true;
277        }
278    }