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 */
019package org.apache.directory.api.ldap.model.cursor;
020
021
022import java.io.IOException;
023import java.util.Collections;
024import java.util.Comparator;
025import java.util.Set;
026
027import org.apache.directory.api.i18n.I18n;
028import org.apache.directory.api.ldap.model.constants.Loggers;
029import org.apache.directory.api.ldap.model.exception.LdapException;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033
034/**
035 * A simple implementation of a Cursor on a {@link Set}.  Optionally, the
036 * Cursor may be limited to a specific range within the list.
037 *
038 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
039 * @param <E> The element on which this cursor will iterate
040 */
041public class SetCursor<E> extends AbstractCursor<E>
042{
043    /** A dedicated log for cursors */
044    private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
045
046    /** Speedup for logs */
047    private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
048
049    /** The inner Set */
050    private final E[] set;
051
052    /** The associated comparator */
053    private final Comparator<E> comparator;
054
055    /** The current position in the list */
056    private int index = -1;
057
058    /** A limit to what we can print */
059    private static final int MAX_PRINTED_ELEMENT = 100;
060
061
062    /**
063     * Creates a new SetCursor.
064     *
065     * As with all Cursors, this SetCursor requires a successful return from
066     * advance operations (next() or previous()) to properly return values
067     * using the get() operation.
068     *
069     * @param comparator an optional comparator to use for ordering
070     * @param set the Set this StCursor operates on
071     */
072    @SuppressWarnings("unchecked")
073    public SetCursor( Comparator<E> comparator, Set<E> set )
074    {
075        if ( set == null )
076        {
077            set = Collections.EMPTY_SET;
078        }
079
080        if ( IS_DEBUG )
081        {
082            LOG_CURSOR.debug( "Creating SetCursor {}", this );
083        }
084
085        this.comparator = comparator;
086        this.set = ( E[] ) set.toArray();
087    }
088
089
090    /**
091     * Creates a new SetCursor
092     *
093     * As with all Cursors, this SetCursor requires a successful return from
094     * advance operations (next() or previous()) to properly return values
095     * using the get() operation.
096     *
097     * @param set the Set this SetCursor operates on
098     */
099    public SetCursor( Set<E> set )
100    {
101        this( null, set );
102    }
103
104
105    /**
106     * Creates a new SetCursor without any elements.
107     */
108    @SuppressWarnings("unchecked")
109    public SetCursor()
110    {
111        this( null, Collections.EMPTY_SET );
112    }
113
114
115    /**
116     * Creates a new SetCursor without any elements. We also provide 
117     * a comparator.
118     * 
119     * @param comparator The comparator to use for the <E> elements
120     */
121    @SuppressWarnings("unchecked")
122    public SetCursor( Comparator<E> comparator )
123    {
124        this( comparator, Collections.EMPTY_SET );
125    }
126
127
128    /**
129     * {@inheritDoc}
130     */
131    public boolean available()
132    {
133        return ( index >= 0 ) && ( index < set.length );
134    }
135
136
137    /**
138     * {@inheritDoc}
139     */
140    public void before( E element ) throws LdapException, CursorException
141    {
142        checkNotClosed( "before()" );
143
144        if ( comparator == null )
145        {
146            throw new IllegalStateException();
147        }
148
149        // handle some special cases
150        if ( set.length == 0 )
151        {
152            return;
153        }
154        else if ( set.length == 1 )
155        {
156            if ( comparator.compare( element, set[0] ) <= 0 )
157            {
158                beforeFirst();
159            }
160            else
161            {
162                afterLast();
163            }
164        }
165
166        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008_LIST_MAY_BE_SORTED ) );
167    }
168
169
170    /**
171     * {@inheritDoc}
172     */
173    public void after( E element ) throws LdapException, CursorException
174    {
175        checkNotClosed( "after()" );
176
177        if ( comparator == null )
178        {
179            throw new IllegalStateException();
180        }
181
182        // handle some special cases
183        if ( set.length == 0 )
184        {
185            return;
186        }
187        else if ( set.length == 1 )
188        {
189            if ( comparator.compare( element, set[0] ) >= 0 )
190            {
191                afterLast();
192            }
193            else
194            {
195                beforeFirst();
196            }
197        }
198
199        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008_LIST_MAY_BE_SORTED ) );
200    }
201
202
203    /**
204     * {@inheritDoc}
205     */
206    public void beforeFirst() throws LdapException, CursorException
207    {
208        checkNotClosed( "beforeFirst()" );
209        this.index = -1;
210    }
211
212
213    /**
214     * {@inheritDoc}
215     */
216    public void afterLast() throws LdapException, CursorException
217    {
218        checkNotClosed( "afterLast()" );
219        this.index = set.length;
220    }
221
222
223    /**
224     * {@inheritDoc}
225     */
226    public boolean first() throws LdapException, CursorException
227    {
228        checkNotClosed( "first()" );
229
230        if ( set.length > 0 )
231        {
232            index = 0;
233
234            return true;
235        }
236
237        return false;
238    }
239
240
241    /**
242     * {@inheritDoc}
243     */
244    public boolean last() throws LdapException, CursorException
245    {
246        checkNotClosed( "last()" );
247
248        if ( set.length > 0 )
249        {
250            index = set.length - 1;
251
252            return true;
253        }
254
255        return false;
256    }
257
258
259    /**
260     * {@inheritDoc}
261     */
262    @Override
263    public boolean isFirst()
264    {
265        return ( set.length > 0 ) && ( index == 0 );
266    }
267
268
269    /**
270     * {@inheritDoc}
271     */
272    @Override
273    public boolean isLast()
274    {
275        return ( set.length > 0 ) && ( index == set.length - 1 );
276    }
277
278
279    /**
280     * {@inheritDoc}
281     */
282    @Override
283    public boolean isAfterLast()
284    {
285        return index == set.length;
286    }
287
288
289    /**
290     * {@inheritDoc}
291     */
292    @Override
293    public boolean isBeforeFirst()
294    {
295        return index == -1;
296    }
297
298
299    /**
300     * {@inheritDoc}
301     */
302    public boolean previous() throws LdapException, CursorException
303    {
304        checkNotClosed( "previous()" );
305
306        // if parked at -1 we cannot go backwards
307        if ( index == -1 )
308        {
309            return false;
310        }
311
312        // if the index moved back is still greater than or eq to start then OK
313        if ( index - 1 >= 0 )
314        {
315            index--;
316
317            return true;
318        }
319
320        // if the index currently less than or equal to start we need to park it at -1 and return false
321        if ( index <= 0 )
322        {
323            index = -1;
324
325            return false;
326        }
327
328        if ( set.length <= 0 )
329        {
330            index = -1;
331        }
332
333        return false;
334    }
335
336
337    /**
338     * {@inheritDoc}
339     */
340    public boolean next() throws LdapException, CursorException
341    {
342        checkNotClosed( "next()" );
343
344        // if parked at -1 we advance to the start index and return true
345        if ( ( set.length > 0 ) && ( index == -1 ) )
346        {
347            index = 0;
348
349            return true;
350        }
351
352        // if the index plus one is less than the end then increment and return true
353        if ( ( set.length > 0 ) && ( index + 1 < set.length ) )
354        {
355            index++;
356
357            return true;
358        }
359
360        // if the index plus one is equal to the end then increment and return false
361        if ( ( set.length > 0 ) && ( index + 1 == set.length ) )
362        {
363            index++;
364
365            return false;
366        }
367
368        if ( set.length <= 0 )
369        {
370            index = set.length;
371        }
372
373        return false;
374    }
375
376
377    /**
378     * {@inheritDoc}
379     */
380    public E get() throws CursorException
381    {
382        checkNotClosed( "get()" );
383
384        if ( ( index < 0 ) || ( index >= set.length ) )
385        {
386            throw new CursorException( I18n.err( I18n.ERR_02009_CURSOR_NOT_POSITIONED ) );
387        }
388
389        return set[index];
390    }
391
392
393    /**
394     * {@inheritDoc}
395     */
396    @Override
397    public void close() throws IOException
398    {
399        if ( IS_DEBUG )
400        {
401            LOG_CURSOR.debug( "Closing ListCursor {}", this );
402        }
403
404        super.close();
405    }
406
407
408    /**
409     * {@inheritDoc}
410     */
411    @Override
412    public void close( Exception cause ) throws IOException
413    {
414        if ( IS_DEBUG )
415        {
416            LOG_CURSOR.debug( "Closing ListCursor {}", this );
417        }
418
419        super.close( cause );
420    }
421
422
423    /**
424     * @see Object#toString()
425     */
426    public String toString( String tabs )
427    {
428        StringBuilder sb = new StringBuilder();
429
430        sb.append( tabs ).append( "SetCursor :\n" );
431        sb.append( tabs ).append( "    Index : " ).append( index ).append( "\n" );
432
433        if ( ( set != null ) && ( set.length > 0 ) )
434        {
435            sb.append( tabs ).append( "    Size : " ).append( set.length ).append( "\n" );
436
437            // Don't print more than 100 elements...
438            int counter = 0;
439
440            for ( E e : set )
441            {
442                sb.append( tabs ).append( "    " ).append( e ).append( "\n" );
443                counter++;
444
445                if ( counter == MAX_PRINTED_ELEMENT )
446                {
447                    break;
448                }
449            }
450        }
451
452        return sb.toString();
453    }
454
455
456    /**
457     * @see Object#toString()
458     */
459    public String toString()
460    {
461        return toString( "" );
462    }
463}