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