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