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.core.avltree;
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.CursorException;
028import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
029import org.apache.directory.api.ldap.model.exception.LdapException;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033
034/**
035 * A Cursor for an ArrayTree.
036 *
037 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
038 */
039public class ArrayTreeCursor<E> extends AbstractCursor<E>
040{
041    /** A dedicated log for cursors */
042    private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
043
044    /** Speedup for logs */
045    private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
046
047    /** The underlying ArrayTree */
048    private ArrayTree<E> array;
049
050    /** The current position/index in the array */
051    private int current;
052
053    /** The current position of this cursor, relative to the node */
054    private Position position;
055
056
057    /**
058     * Create a cursor on an ArrayTree
059     * @param array The array we want a cursor for
060     */
061    public ArrayTreeCursor( ArrayTree<E> array )
062    {
063        if ( IS_DEBUG )
064        {
065            LOG_CURSOR.debug( "Creating ArrayTreeCursor {}", this );
066        }
067
068        this.array = array;
069        position = Position.BEFORE_FIRST;
070    }
071
072
073    /**
074     * {@inheritDoc}
075     */
076    public void after( E element ) throws LdapException, CursorException
077    {
078        checkNotClosed();
079
080        if ( element == null )
081        {
082            afterLast();
083            return;
084        }
085
086        current = array.getAfterPosition( element );
087
088        if ( current == -1 )
089        {
090            // As the element has not been found, we move after the last position
091            position = Position.AFTER_LAST;
092        }
093        else
094        {
095            // the cursor should be positioned after the given element
096            // we just fetched the next greater element so the cursor
097            // is positioned before the fetched element
098            position = Position.BEFORE_NODE;
099        }
100    }
101
102
103    /**
104     * {@inheritDoc}
105     */
106    public void afterLast() throws LdapException, CursorException
107    {
108        checkNotClosed();
109
110        current = -1;
111        position = Position.AFTER_LAST;
112    }
113
114
115    /**
116     * {@inheritDoc}
117     */
118    public boolean available()
119    {
120        return position == Position.ON_NODE;
121    }
122
123
124    /**
125     * {@inheritDoc}
126     */
127    public void before( E element ) throws LdapException, CursorException
128    {
129        checkNotClosed();
130
131        if ( element == null )
132        {
133            beforeFirst();
134            return;
135        }
136
137        current = array.getBeforePosition( element );
138
139        if ( current == -1 )
140        {
141            // If the element has not been found, move to thea first position
142            position = Position.BEFORE_FIRST;
143        }
144        else
145        {
146            // the cursor should be positioned before the given element
147            // we just fetched the next less element so the cursor
148            // is positioned after the fetched element
149            position = Position.AFTER_NODE;
150        }
151    }
152
153
154    /**
155     * {@inheritDoc}
156     */
157    public void beforeFirst() throws LdapException, CursorException
158    {
159        checkNotClosed();
160
161        current = -1;
162        position = Position.BEFORE_FIRST;
163    }
164
165
166    /**
167     * {@inheritDoc}
168     */
169    public boolean first() throws LdapException, CursorException
170    {
171        checkNotClosed();
172
173        if ( array.isEmpty() )
174        {
175            current = -1;
176            position = Position.BEFORE_FIRST;
177            return false;
178        }
179        else
180        {
181            current = 0;
182            position = Position.ON_NODE;
183            return true;
184        }
185    }
186
187
188    /**
189     * {@inheritDoc}
190     */
191    public E get() throws CursorException
192    {
193        checkNotClosed();
194
195        if ( position == Position.ON_NODE )
196        {
197            return array.get( current );
198        }
199
200        throw new InvalidCursorPositionException();
201    }
202
203
204    /**
205     * {@inheritDoc}
206     */
207    public boolean last() throws LdapException, CursorException
208    {
209        checkNotClosed();
210
211        if ( array.isEmpty() )
212        {
213            current = -1;
214            position = Position.AFTER_LAST;
215            return false;
216        }
217        else
218        {
219            current = array.size() - 1;
220            position = Position.ON_NODE;
221            return true;
222        }
223    }
224
225
226    /**
227     * {@inheritDoc}
228     */
229    public boolean next() throws LdapException, CursorException
230    {
231        checkNotClosed();
232
233        // If the array is empty, return false
234        if ( array.size() == 0 )
235        {
236            return false;
237        }
238
239        switch ( position )
240        {
241            case BEFORE_FIRST:
242                return first();
243
244            case BEFORE_NODE:
245                position = Position.ON_NODE;
246                return true;
247
248            case ON_NODE:
249            case AFTER_NODE:
250                current++;
251                if ( current > array.size() - 1 )
252                {
253                    afterLast();
254                    return false;
255                }
256                else
257                {
258                    position = Position.ON_NODE;
259                    return true;
260                }
261
262            case AFTER_LAST:
263                return false;
264
265            default:
266                throw new IllegalStateException( "Unexpected position " + position );
267        }
268    }
269
270
271    /**
272     * {@inheritDoc}
273     */
274    public boolean previous() throws LdapException, CursorException
275    {
276        checkNotClosed();
277
278        if ( array.size() == 0 )
279        {
280            return false;
281        }
282
283        switch ( position )
284        {
285            case BEFORE_FIRST:
286                return false;
287
288            case BEFORE_NODE:
289            case ON_NODE:
290                current--;
291                if ( current < 0 )
292                {
293                    beforeFirst();
294                    return false;
295                }
296                else
297                {
298                    position = Position.ON_NODE;
299                    return true;
300                }
301
302            case AFTER_NODE:
303                position = Position.ON_NODE;
304                return true;
305
306            case AFTER_LAST:
307                return last();
308
309            default:
310                throw new IllegalStateException( "Unexpected position " + position );
311        }
312    }
313
314
315    /**
316     * {@inheritDoc}
317     */
318    @Override
319    public void close() throws IOException
320    {
321        if ( IS_DEBUG )
322        {
323            LOG_CURSOR.debug( "Closing ArrayTreeCursor {}", this );
324        }
325
326        super.close();
327    }
328
329
330    /**
331     * {@inheritDoc}
332     */
333    @Override
334    public void close( Exception reason ) throws IOException
335    {
336        if ( IS_DEBUG )
337        {
338            LOG_CURSOR.debug( "Closing ArrayTreeCursor {}", this );
339        }
340
341        super.close( reason );
342    }
343
344
345    /**
346     * @see Object#toString()
347     */
348    @Override
349    public String toString( String tabs )
350    {
351        StringBuilder sb = new StringBuilder();
352
353        sb.append( tabs ).append( "ArrayTreeCursor (" );
354
355        if ( available() )
356        {
357            sb.append( "available)" );
358            sb.append( "#<" ).append( current ).append( ":" ).append( array.get( current ) ).append( ">" );
359        }
360        else
361        {
362            sb.append( "absent)" );
363        }
364
365        return sb.toString();
366    }
367
368
369    /**
370     * @see Object#toString()
371     */
372    public String toString()
373    {
374        return toString( "" );
375    }
376}