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 &lt;E&gt; 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    @Override
132    public boolean available()
133    {
134        return ( index >= 0 ) && ( index < set.length );
135    }
136
137
138    /**
139     * {@inheritDoc}
140     */
141    @Override
142    public void before( E element ) throws LdapException, CursorException
143    {
144        checkNotClosed( "before()" );
145
146        if ( comparator == null )
147        {
148            throw new IllegalStateException();
149        }
150
151        // handle some special cases
152        if ( set.length == 0 )
153        {
154            return;
155        }
156        else if ( set.length == 1 )
157        {
158            if ( comparator.compare( element, set[0] ) <= 0 )
159            {
160                beforeFirst();
161            }
162            else
163            {
164                afterLast();
165            }
166        }
167
168        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008_LIST_MAY_BE_SORTED ) );
169    }
170
171
172    /**
173     * {@inheritDoc}
174     */
175    @Override
176    public void after( E element ) throws LdapException, CursorException
177    {
178        checkNotClosed( "after()" );
179
180        if ( comparator == null )
181        {
182            throw new IllegalStateException();
183        }
184
185        // handle some special cases
186        if ( set.length == 0 )
187        {
188            return;
189        }
190        else if ( set.length == 1 )
191        {
192            if ( comparator.compare( element, set[0] ) >= 0 )
193            {
194                afterLast();
195            }
196            else
197            {
198                beforeFirst();
199            }
200        }
201
202        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008_LIST_MAY_BE_SORTED ) );
203    }
204
205
206    /**
207     * {@inheritDoc}
208     */
209    @Override
210    public void beforeFirst() throws LdapException, CursorException
211    {
212        checkNotClosed( "beforeFirst()" );
213        this.index = -1;
214    }
215
216
217    /**
218     * {@inheritDoc}
219     */
220    @Override
221    public void afterLast() throws LdapException, CursorException
222    {
223        checkNotClosed( "afterLast()" );
224        this.index = set.length;
225    }
226
227
228    /**
229     * {@inheritDoc}
230     */
231    @Override
232    public boolean first() throws LdapException, CursorException
233    {
234        checkNotClosed( "first()" );
235
236        if ( set.length > 0 )
237        {
238            index = 0;
239
240            return true;
241        }
242
243        return false;
244    }
245
246
247    /**
248     * {@inheritDoc}
249     */
250    @Override
251    public boolean last() throws LdapException, CursorException
252    {
253        checkNotClosed( "last()" );
254
255        if ( set.length > 0 )
256        {
257            index = set.length - 1;
258
259            return true;
260        }
261
262        return false;
263    }
264
265
266    /**
267     * {@inheritDoc}
268     */
269    @Override
270    public boolean isFirst()
271    {
272        return ( set.length > 0 ) && ( index == 0 );
273    }
274
275
276    /**
277     * {@inheritDoc}
278     */
279    @Override
280    public boolean isLast()
281    {
282        return ( set.length > 0 ) && ( index == set.length - 1 );
283    }
284
285
286    /**
287     * {@inheritDoc}
288     */
289    @Override
290    public boolean isAfterLast()
291    {
292        return index == set.length;
293    }
294
295
296    /**
297     * {@inheritDoc}
298     */
299    @Override
300    public boolean isBeforeFirst()
301    {
302        return index == -1;
303    }
304
305
306    /**
307     * {@inheritDoc}
308     */
309    @Override
310    public boolean previous() throws LdapException, CursorException
311    {
312        checkNotClosed( "previous()" );
313
314        // if parked at -1 we cannot go backwards
315        if ( index == -1 )
316        {
317            return false;
318        }
319
320        // if the index moved back is still greater than or eq to start then OK
321        if ( index - 1 >= 0 )
322        {
323            index--;
324
325            return true;
326        }
327
328        // if the index currently less than or equal to start we need to park it at -1 and return false
329        if ( index <= 0 )
330        {
331            index = -1;
332
333            return false;
334        }
335
336        if ( set.length <= 0 )
337        {
338            index = -1;
339        }
340
341        return false;
342    }
343
344
345    /**
346     * {@inheritDoc}
347     */
348    @Override
349    public boolean next() throws LdapException, CursorException
350    {
351        checkNotClosed( "next()" );
352
353        // if parked at -1 we advance to the start index and return true
354        if ( ( set.length > 0 ) && ( index == -1 ) )
355        {
356            index = 0;
357
358            return true;
359        }
360
361        // if the index plus one is less than the end then increment and return true
362        if ( ( set.length > 0 ) && ( index + 1 < set.length ) )
363        {
364            index++;
365
366            return true;
367        }
368
369        // if the index plus one is equal to the end then increment and return false
370        if ( ( set.length > 0 ) && ( index + 1 == set.length ) )
371        {
372            index++;
373
374            return false;
375        }
376
377        if ( set.length <= 0 )
378        {
379            index = set.length;
380        }
381
382        return false;
383    }
384
385
386    /**
387     * {@inheritDoc}
388     */
389    @Override
390    public E get() throws CursorException
391    {
392        checkNotClosed( "get()" );
393
394        if ( ( index < 0 ) || ( index >= set.length ) )
395        {
396            throw new CursorException( I18n.err( I18n.ERR_02009_CURSOR_NOT_POSITIONED ) );
397        }
398
399        return set[index];
400    }
401
402
403    /**
404     * {@inheritDoc}
405     */
406    @Override
407    public void close() throws IOException
408    {
409        if ( IS_DEBUG )
410        {
411            LOG_CURSOR.debug( "Closing ListCursor {}", this );
412        }
413
414        super.close();
415    }
416
417
418    /**
419     * {@inheritDoc}
420     */
421    @Override
422    public void close( Exception cause ) throws IOException
423    {
424        if ( IS_DEBUG )
425        {
426            LOG_CURSOR.debug( "Closing ListCursor {}", this );
427        }
428
429        super.close( cause );
430    }
431
432
433    /**
434     * @see Object#toString()
435     */
436    @Override
437    public String toString( String tabs )
438    {
439        StringBuilder sb = new StringBuilder();
440
441        sb.append( tabs ).append( "SetCursor :\n" );
442        sb.append( tabs ).append( "    Index : " ).append( index ).append( "\n" );
443
444        if ( ( set != null ) && ( set.length > 0 ) )
445        {
446            sb.append( tabs ).append( "    Size : " ).append( set.length ).append( "\n" );
447
448            // Don't print more than 100 elements...
449            int counter = 0;
450
451            for ( E e : set )
452            {
453                sb.append( tabs ).append( "    " ).append( e ).append( "\n" );
454                counter++;
455
456                if ( counter == MAX_PRINTED_ELEMENT )
457                {
458                    break;
459                }
460            }
461        }
462
463        return sb.toString();
464    }
465
466
467    /**
468     * @see Object#toString()
469     */
470    @Override
471    public String toString()
472    {
473        return toString( "" );
474    }
475}