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