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