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    public boolean available()
096    {
097        return onSingleton;
098    }
099
100
101    /**
102     * {@inheritDoc}
103     */
104    public void before( E element ) throws LdapException, CursorException
105    {
106        checkNotClosed( "before()" );
107
108        if ( comparator == null )
109        {
110            throw new UnsupportedOperationException( I18n.err( I18n.ERR_02010_NO_COMPARATOR_CANT_MOVE_BEFORE ) );
111        }
112
113        int comparison = comparator.compare( singleton, element );
114
115        if ( comparison < 0 )
116        {
117            first();
118        }
119        else
120        {
121            beforeFirst();
122        }
123    }
124
125
126    /**
127     * {@inheritDoc}
128     */
129    public void after( E element ) throws LdapException, CursorException
130    {
131        checkNotClosed( "after()" );
132
133        if ( comparator == null )
134        {
135            throw new UnsupportedOperationException( I18n.err( I18n.ERR_02011_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    public void beforeFirst() throws LdapException, CursorException
155    {
156        checkNotClosed( "beforeFirst" );
157        beforeFirst = true;
158        afterLast = false;
159        onSingleton = false;
160    }
161
162
163    /**
164     * {@inheritDoc}
165     */
166    public void afterLast() throws LdapException, CursorException
167    {
168        checkNotClosed( "afterLast" );
169        beforeFirst = false;
170        afterLast = true;
171        onSingleton = false;
172    }
173
174
175    /**
176     * {@inheritDoc}
177     */
178    public boolean first() throws LdapException, CursorException
179    {
180        checkNotClosed( "first" );
181        beforeFirst = false;
182        onSingleton = true;
183        afterLast = false;
184
185        return true;
186    }
187
188
189    /**
190     * {@inheritDoc}
191     */
192    public boolean last() throws LdapException, CursorException
193    {
194        checkNotClosed( "last" );
195        beforeFirst = false;
196        onSingleton = true;
197        afterLast = false;
198
199        return true;
200    }
201
202
203    /**
204     * {@inheritDoc}
205     */
206    @Override
207    public boolean isFirst()
208    {
209        return onSingleton;
210    }
211
212
213    /**
214     * {@inheritDoc}
215     */
216    @Override
217    public boolean isLast()
218    {
219        return onSingleton;
220    }
221
222
223    /**
224     * {@inheritDoc}
225     */
226    @Override
227    public boolean isAfterLast()
228    {
229        return afterLast;
230    }
231
232
233    /**
234     * {@inheritDoc}
235     */
236    @Override
237    public boolean isBeforeFirst()
238    {
239        return beforeFirst;
240    }
241
242
243    /**
244     * {@inheritDoc}
245     */
246    public boolean previous() throws LdapException, CursorException
247    {
248        checkNotClosed( "previous" );
249
250        if ( beforeFirst )
251        {
252            return false;
253        }
254
255        if ( afterLast )
256        {
257            beforeFirst = false;
258            onSingleton = true;
259            afterLast = false;
260
261            return true;
262        }
263
264        // must be on the singleton
265        beforeFirst = true;
266        onSingleton = false;
267        afterLast = false;
268
269        return false;
270    }
271
272
273    /**
274     * {@inheritDoc}
275     */
276    public boolean next() throws LdapException, CursorException
277    {
278        checkNotClosed( "next" );
279
280        if ( beforeFirst )
281        {
282            beforeFirst = false;
283            onSingleton = true;
284            afterLast = false;
285
286            return true;
287        }
288
289        if ( afterLast )
290        {
291            return false;
292        }
293
294        // must be on the singleton
295        beforeFirst = false;
296        onSingleton = false;
297        afterLast = true;
298
299        return false;
300    }
301
302
303    /**
304     * {@inheritDoc}
305     */
306    public E get() throws CursorException
307    {
308        checkNotClosed( "get" );
309
310        if ( onSingleton )
311        {
312            return singleton;
313        }
314
315        if ( beforeFirst )
316        {
317            throw new InvalidCursorPositionException( I18n.err( I18n.ERR_02012_CANNOT_ACCESS_IF_BEFORE_FIRST ) );
318        }
319        else
320        {
321            throw new InvalidCursorPositionException( I18n.err( I18n.ERR_02013_CANNOT_ACCESS_IF_AFTER_LAST ) );
322        }
323    }
324
325
326    /**
327     * {@inheritDoc}
328     */
329    @Override
330    public void close() throws IOException
331    {
332        if ( IS_DEBUG )
333        {
334            LOG_CURSOR.debug( "Closing SingletonCursor {}", this );
335        }
336
337        super.close();
338    }
339
340
341    /**
342     * {@inheritDoc}
343     */
344    @Override
345    public void close( Exception cause ) throws IOException
346    {
347        if ( IS_DEBUG )
348        {
349            LOG_CURSOR.debug( "Closing SingletonCursor {}", this );
350        }
351
352        super.close( cause );
353    }
354}