blob: 89c157956e2ef340e2b1271d94e90c424a4b6d8a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.directory.shared.ldap.cursor;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.apache.directory.shared.i18n.I18n;
/**
* A simple implementation of a Cursor on a {@link List}. Optionally, the
* Cursor may be limited to a specific range within the list.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class ListCursor<E> extends AbstractCursor<E>
{
/** The inner List */
private final List<E> list;
/** The associated comparator */
private final Comparator<E> comparator;
/** The starting position for the cursor in the list. It can be > 0 */
private final int start;
/** The ending position for the cursor in the list. It can be < List.size() */
private final int end;
/** The current position in the list */
private int index = -1;
/**
* Creates a new ListCursor with lower (inclusive) and upper (exclusive)
* bounds.
*
* As with all Cursors, this ListCursor requires a successful return from
* advance operations (next() or previous()) to properly return values
* using the get() operation.
*
* @param comparator an optional comparator to use for ordering
* @param start the lower bound index
* @param list the list this ListCursor operates on
* @param end the upper bound index
*/
public ListCursor( Comparator<E> comparator, int start, List<E> list, int end )
{
if ( list == null )
{
list = Collections.emptyList();
}
if ( ( start < 0 ) || ( start > list.size() ) )
{
throw new IllegalArgumentException( I18n.err( I18n.ERR_02005, start ) );
}
if ( ( end < 0 ) || ( end > list.size() ) )
{
throw new IllegalArgumentException( I18n.err( I18n.ERR_02006, end ) );
}
// check list is not empty list since the empty list is the only situation
// where we allow for start to equal the end: in other cases it makes no sense
if ( ( list.size() > 0 ) && ( start >= end ) )
{
throw new IllegalArgumentException( I18n.err( I18n.ERR_02007, start, end ) );
}
this.comparator = comparator;
this.list = list;
this.start = start;
this.end = end;
}
/**
* Creates a new ListCursor with lower (inclusive) and upper (exclusive)
* bounds.
*
* As with all Cursors, this ListCursor requires a successful return from
* advance operations (next() or previous()) to properly return values
* using the get() operation.
*
* @param start the lower bound index
* @param list the list this ListCursor operates on
* @param end the upper bound index
*/
public ListCursor( int start, List<E> list, int end )
{
this( null, start, list, end );
}
/**
* Creates a new ListCursor with a specific upper (exclusive) bound: the
* lower (inclusive) bound defaults to 0.
*
* @param list the backing for this ListCursor
* @param end the upper bound index representing the position after the
* last element
*/
public ListCursor( List<E> list, int end )
{
this( null, 0, list, end );
}
/**
* Creates a new ListCursor with a specific upper (exclusive) bound: the
* lower (inclusive) bound defaults to 0. We also provide a comparator.
*
* @param comparator The comparator to use for the <E> elements
* @param list the backing for this ListCursor
* @param end the upper bound index representing the position after the
* last element
*/
public ListCursor( Comparator<E> comparator, List<E> list, int end )
{
this( comparator, 0, list, end );
}
/**
* Creates a new ListCursor with a lower (inclusive) bound: the upper
* (exclusive) bound is the size of the list.
*
* @param start the lower (inclusive) bound index: the position of the
* first entry
* @param list the backing for this ListCursor
*/
public ListCursor( int start, List<E> list )
{
this( null, start, list, list.size() );
}
/**
* Creates a new ListCursor with a lower (inclusive) bound: the upper
* (exclusive) bound is the size of the list. We also provide a comparator.
*
* @param comparator The comparator to use for the <E> elements
* @param start the lower (inclusive) bound index: the position of the
* first entry
* @param list the backing for this ListCursor
*/
public ListCursor( Comparator<E> comparator, int start, List<E> list )
{
this( comparator, start, list, list.size() );
}
/**
* Creates a new ListCursor without specific bounds: the bounds are
* acquired from the size of the list.
*
* @param list the backing for this ListCursor
*/
public ListCursor( List<E> list )
{
this( null, 0, list, list.size() );
}
/**
* Creates a new ListCursor without specific bounds: the bounds are
* acquired from the size of the list. We also provide a comparator.
*
* @param comparator The comparator to use for the <E> elements
* @param list the backing for this ListCursor
*/
public ListCursor( Comparator<E> comparator, List<E> list )
{
this( comparator, 0, list, list.size() );
}
/**
* Creates a new ListCursor without any elements.
*/
@SuppressWarnings("unchecked")
public ListCursor()
{
this( null, 0, Collections.EMPTY_LIST, 0 );
}
/**
* Creates a new ListCursor without any elements. We also provide
* a comparator.
*
* @param comparator The comparator to use for the <E> elements
*/
@SuppressWarnings("unchecked")
public ListCursor( Comparator<E> comparator )
{
this( comparator, 0, Collections.EMPTY_LIST, 0 );
}
/**
* {@inheritDoc}
*/
public boolean available()
{
return index >= 0 && index < end;
}
/**
* @throws IllegalStateException if the underlying list is not sorted
* and/or a comparator is not provided.
*/
public void before( E element ) throws Exception
{
checkNotClosed( "before()" );
if ( comparator == null )
{
throw new IllegalStateException();
}
// handle some special cases
if ( list.size() == 0 )
{
return;
}
else if ( list.size() == 1 )
{
if ( comparator.compare( element, list.get( 0 ) ) <= 0 )
{
beforeFirst();
}
else
{
afterLast();
}
}
// TODO might want to add some code here to utilize the comparator
throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008 ) );
}
/**
* {@inheritDoc}
*/
public void after( E element ) throws Exception
{
checkNotClosed( "after()" );
if ( comparator == null )
{
throw new IllegalStateException();
}
// handle some special cases
if ( list.size() == 0 )
{
return;
}
else if ( list.size() == 1 )
{
if ( comparator.compare( element, list.get( 0 ) ) >= 0 )
{
afterLast();
}
else
{
beforeFirst();
}
}
// TODO might want to add some code here to utilize the comparator
throw new UnsupportedOperationException( I18n.err( I18n.ERR_02008 ) );
}
/**
* {@inheritDoc}
*/
public void beforeFirst() throws Exception
{
checkNotClosed( "beforeFirst()" );
this.index = -1;
}
/**
* {@inheritDoc}
*/
public void afterLast() throws Exception
{
checkNotClosed( "afterLast()" );
this.index = end;
}
/**
* {@inheritDoc}
*/
public boolean first() throws Exception
{
checkNotClosed( "first()" );
if ( list.size() > 0 )
{
index = start;
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
public boolean last() throws Exception
{
checkNotClosed( "last()" );
if ( list.size() > 0 )
{
index = end - 1;
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isFirst() throws Exception
{
checkNotClosed( "isFirst()" );
return list.size() > 0 && index == start;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isLast() throws Exception
{
checkNotClosed( "isLast()" );
return list.size() > 0 && index == end - 1;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isAfterLast() throws Exception
{
checkNotClosed( "isAfterLast()" );
return index == end;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isBeforeFirst() throws Exception
{
checkNotClosed( "isBeforeFirst()" );
return index == -1;
}
/**
* {@inheritDoc}
*/
public boolean previous() throws Exception
{
checkNotClosed( "previous()" );
// if parked at -1 we cannot go backwards
if ( index == -1 )
{
return false;
}
// if the index moved back is still greater than or eq to start then OK
if ( index - 1 >= start )
{
index--;
return true;
}
// if the index currently less than or equal to start we need to park it at -1 and return false
if ( index <= start )
{
index = -1;
return false;
}
if ( list.size() <= 0 )
{
index = -1;
}
return false;
}
/**
* {@inheritDoc}
*/
public boolean next() throws Exception
{
checkNotClosed( "next()" );
// if parked at -1 we advance to the start index and return true
if ( list.size() > 0 && index == -1 )
{
index = start;
return true;
}
// if the index plus one is less than the end then increment and return true
if ( list.size() > 0 && index + 1 < end )
{
index++;
return true;
}
// if the index plus one is equal to the end then increment and return false
if ( list.size() > 0 && index + 1 == end )
{
index++;
return false;
}
if ( list.size() <= 0 )
{
index = end;
}
return false;
}
/**
* {@inheritDoc}
*/
public E get() throws Exception
{
checkNotClosed( "get()" );
if ( index < start || index >= end )
{
throw new IOException( I18n.err( I18n.ERR_02009 ) );
}
return list.get( index );
}
/**
* {@inheritDoc}
*/
public boolean isElementReused()
{
return true;
}
}