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