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.Comparator;
024
025import org.apache.directory.api.i18n.I18n;
026import org.apache.directory.api.ldap.model.constants.Loggers;
027import org.apache.directory.api.ldap.model.exception.LdapException;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031
032/**
033 * A Cursor over a single element.
034 *
035 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
036 * @param <E> The type of element on which this cursor will iterate
037 */
038public class SingletonCursor<E> extends AbstractCursor<E>
039{
040    /** A dedicated log for cursors */
041    private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
042
043    /** Speedup for logs */
044    private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
045
046    /** A flag to tell if the cursor is set before the first element */
047    private boolean beforeFirst = true;
048
049    /** A flag to tell if the cursor is set after the last element */
050    private boolean afterLast;
051
052    /** A flag to tell if the cursor is on the element */
053    private boolean onSingleton;
054
055    /** The comparator used for this cursor. */
056    private final Comparator<E> comparator;
057
058    /** The unique element stored in the cursor */
059    private final E singleton;
060
061
062    /**
063     * Creates a new instance of SingletonCursor.
064     *
065     * @param singleton The unique element to store into this cursor
066     */
067    public SingletonCursor( E singleton )
068    {
069        this( singleton, null );
070    }
071
072
073    /**
074     * Creates a new instance of SingletonCursor, with its associated
075     * comparator
076     *
077     * @param singleton The unique element to store into this cursor
078     * @param comparator The associated comparator
079     */
080    public SingletonCursor( E singleton, Comparator<E> comparator )
081    {
082        if ( IS_DEBUG )
083        {
084            LOG_CURSOR.debug( "Creating SingletonCursor {}", this );
085        }
086
087        this.singleton = singleton;
088        this.comparator = comparator;
089    }
090
091
092    /**
093     * {@inheritDoc}
094     */
095    @Override
096    public boolean available()
097    {
098        return onSingleton;
099    }
100
101
102    /**
103     * {@inheritDoc}
104     */
105    @Override
106    public void before( E element ) throws LdapException, CursorException
107    {
108        checkNotClosed( "before()" );
109
110        if ( comparator == null )
111        {
112            throw new UnsupportedOperationException( I18n.err( I18n.ERR_02010_NO_COMPARATOR_CANT_MOVE_BEFORE ) );
113        }
114
115        int comparison = comparator.compare( singleton, element );
116
117        if ( comparison < 0 )
118        {
119            first();
120        }
121        else
122        {
123            beforeFirst();
124        }
125    }
126
127
128    /**
129     * {@inheritDoc}
130     */
131    @Override
132    public void after( E element ) throws LdapException, CursorException
133    {
134        checkNotClosed( "after()" );
135
136        if ( comparator == null )
137        {
138            throw new UnsupportedOperationException( I18n.err( I18n.ERR_02011_NO_COMPARATOR_CANT_MOVE_AFTER ) );
139        }
140
141        int comparison = comparator.compare( singleton, element );
142
143        if ( comparison > 0 )
144        {
145            first();
146        }
147        else
148        {
149            afterLast();
150        }
151    }
152
153
154    /**
155     * {@inheritDoc}
156     */
157    @Override
158    public void beforeFirst() throws LdapException, CursorException
159    {
160        checkNotClosed( "beforeFirst" );
161        beforeFirst = true;
162        afterLast = false;
163        onSingleton = false;
164    }
165
166
167    /**
168     * {@inheritDoc}
169     */
170    @Override
171    public void afterLast() throws LdapException, CursorException
172    {
173        checkNotClosed( "afterLast" );
174        beforeFirst = false;
175        afterLast = true;
176        onSingleton = false;
177    }
178
179
180    /**
181     * {@inheritDoc}
182     */
183    @Override
184    public boolean first() throws LdapException, CursorException
185    {
186        checkNotClosed( "first" );
187        beforeFirst = false;
188        onSingleton = true;
189        afterLast = false;
190
191        return true;
192    }
193
194
195    /**
196     * {@inheritDoc}
197     */
198    @Override
199    public boolean last() throws LdapException, CursorException
200    {
201        checkNotClosed( "last" );
202        beforeFirst = false;
203        onSingleton = true;
204        afterLast = false;
205
206        return true;
207    }
208
209
210    /**
211     * {@inheritDoc}
212     */
213    @Override
214    public boolean isFirst()
215    {
216        return onSingleton;
217    }
218
219
220    /**
221     * {@inheritDoc}
222     */
223    @Override
224    public boolean isLast()
225    {
226        return onSingleton;
227    }
228
229
230    /**
231     * {@inheritDoc}
232     */
233    @Override
234    public boolean isAfterLast()
235    {
236        return afterLast;
237    }
238
239
240    /**
241     * {@inheritDoc}
242     */
243    @Override
244    public boolean isBeforeFirst()
245    {
246        return beforeFirst;
247    }
248
249
250    /**
251     * {@inheritDoc}
252     */
253    @Override
254    public boolean previous() throws LdapException, CursorException
255    {
256        checkNotClosed( "previous" );
257
258        if ( beforeFirst )
259        {
260            return false;
261        }
262
263        if ( afterLast )
264        {
265            beforeFirst = false;
266            onSingleton = true;
267            afterLast = false;
268
269            return true;
270        }
271
272        // must be on the singleton
273        beforeFirst = true;
274        onSingleton = false;
275        afterLast = false;
276
277        return false;
278    }
279
280
281    /**
282     * {@inheritDoc}
283     */
284    @Override
285    public boolean next() throws LdapException, CursorException
286    {
287        checkNotClosed( "next" );
288
289        if ( beforeFirst )
290        {
291            beforeFirst = false;
292            onSingleton = true;
293            afterLast = false;
294
295            return true;
296        }
297
298        if ( afterLast )
299        {
300            return false;
301        }
302
303        // must be on the singleton
304        beforeFirst = false;
305        onSingleton = false;
306        afterLast = true;
307
308        return false;
309    }
310
311
312    /**
313     * {@inheritDoc}
314     */
315    @Override
316    public E get() throws CursorException
317    {
318        checkNotClosed( "get" );
319
320        if ( onSingleton )
321        {
322            return singleton;
323        }
324
325        if ( beforeFirst )
326        {
327            throw new InvalidCursorPositionException( I18n.err( I18n.ERR_02012_CANNOT_ACCESS_IF_BEFORE_FIRST ) );
328        }
329        else
330        {
331            throw new InvalidCursorPositionException( I18n.err( I18n.ERR_02013_CANNOT_ACCESS_IF_AFTER_LAST ) );
332        }
333    }
334
335
336    /**
337     * {@inheritDoc}
338     */
339    @Override
340    public void close() throws IOException
341    {
342        if ( IS_DEBUG )
343        {
344            LOG_CURSOR.debug( "Closing SingletonCursor {}", this );
345        }
346
347        super.close();
348    }
349
350
351    /**
352     * {@inheritDoc}
353     */
354    @Override
355    public void close( Exception cause ) throws IOException
356    {
357        if ( IS_DEBUG )
358        {
359            LOG_CURSOR.debug( "Closing SingletonCursor {}", this );
360        }
361
362        super.close( cause );
363    }
364}