You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ak...@apache.org on 2007/11/11 00:54:36 UTC

svn commit: r593826 - in /directory/apacheds/branches/bigbang/core-shared/src: main/java/org/apache/directory/server/core/ main/java/org/apache/directory/server/core/cursor/ test/java/org/apache/directory/server/core/cursor/

Author: akarasulu
Date: Sat Nov 10 15:54:34 2007
New Revision: 593826

URL: http://svn.apache.org/viewvc?rev=593826&view=rev
Log:
introducing (yet again - did this before in 2002) the concept of a Cursor to replace the use of NamingEnumerations

Added:
    directory/apacheds/branches/bigbang/core-shared/src/main/java/org/apache/directory/server/core/
    directory/apacheds/branches/bigbang/core-shared/src/main/java/org/apache/directory/server/core/cursor/
    directory/apacheds/branches/bigbang/core-shared/src/main/java/org/apache/directory/server/core/cursor/Cursor.java
    directory/apacheds/branches/bigbang/core-shared/src/main/java/org/apache/directory/server/core/cursor/ListCursor.java
    directory/apacheds/branches/bigbang/core-shared/src/test/java/org/apache/directory/server/core/cursor/ListCursorTest.java   (with props)

Added: directory/apacheds/branches/bigbang/core-shared/src/main/java/org/apache/directory/server/core/cursor/Cursor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/bigbang/core-shared/src/main/java/org/apache/directory/server/core/cursor/Cursor.java?rev=593826&view=auto
==============================================================================
--- directory/apacheds/branches/bigbang/core-shared/src/main/java/org/apache/directory/server/core/cursor/Cursor.java (added)
+++ directory/apacheds/branches/bigbang/core-shared/src/main/java/org/apache/directory/server/core/cursor/Cursor.java Sat Nov 10 15:54:34 2007
@@ -0,0 +1,236 @@
+/*
+ * 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.server.core.cursor;
+
+import java.io.IOException;
+
+
+/**
+ * A simple cursor concept for bidirectionally enumerating over elements.
+ * Cursors unlike iterators request to advance to an element by calling next()
+ * or previous() which returns true or false if the request succeeds.  Other
+ * operations for relative and absolute advances are provided.  If the cursor
+ * does not advance, then the Cursor is either positioned before the first
+ * element or after the last element in which case the user of the Cursor must
+ * stop advancing in the respective direction.  If an advance succeeds a get()
+ * operation retreives the current object at the Cursors position.
+ *
+ * Although this interface presumes Cursors can advance bidirectionally, one
+ * or more either direction may not be supported.  In this case
+ * implementations should throw UnsupportedOperationExceptions.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public interface Cursor<E>
+{
+    /**
+     * Positions this Curser before the first element.
+     *
+     * @throws IOException if there are problems positioning this cursor or if
+     * this Cursor is closed
+     * @throws UnsupportedOperationException if this operation is not supported
+     */
+    void beforeFirst() throws IOException;
+
+
+    /**
+     * Positions this Curser after the last element.
+     *
+     * @throws IOException if there are problems positioning this Cursor or if
+     * this Cursor is closed
+     * @throws UnsupportedOperationException if this operation is not supported
+     */
+    void afterLast() throws IOException;
+
+
+    /**
+     * Positions this Curser at the nth element.  Zero based indexing is used.
+     *
+     * If the specified position is past the first or last element, the Cursor
+     * is positioned before the first or after the last element respectively.
+     *
+     * @param absolutePosition the absolute position to move this Cursor to
+     * @return true if the position has been successfully changed to the
+     * element at the specified position, false otherwise
+     * @throws IOException if there are problems positioning this Cursor or if
+     * this Cursor is closed
+     * @throws UnsupportedOperationException if this operation is not supported
+     */
+    boolean absolute( int absolutePosition ) throws IOException;
+
+
+    /**
+     * Positions this Curser n places relative to the present position.  Zero
+     * based indexing is used and negative index values may be provided for
+     * representing the direction.
+     *
+     * If the specified position is past the first or last element, the Cursor
+     * is positioned before the first or after the last element respectively.
+     *
+     * @param relativePosition the relative position to move this Cursor to
+     * @return true if the position has been successfully changed to the
+     * element relative to the current position, false otherwise
+     * @throws IOException if there are problems positioning this Cursor or if
+     * this Cursor is closed
+     * @throws UnsupportedOperationException if this operation is not supported
+     */
+    boolean relative( int relativePosition ) throws IOException;
+
+
+    /**
+     * Positions this Curser at the first element.
+     *
+     * @return true if the position has been successfully changed to the first
+     * element, false otherwise
+     * @throws IOException if there are problems positioning this Cursor or if
+     * this Cursor is closed
+     * @throws UnsupportedOperationException if this operation is not supported
+     */
+    boolean first() throws IOException;
+
+
+    /**
+     * Positions this Curser at the last element.
+     *
+     * @return true if the position has been successfully changed to the last
+     * element, false otherwise
+     * @throws IOException if there are problems positioning this Cursor or if
+     * this Cursor is closed
+     * @throws UnsupportedOperationException if this operation is not supported
+     */
+    boolean last() throws IOException;
+
+
+    /**
+     * Checks if this Curser is positioned at the first element.
+     *
+     * @return true if the current position is at the first element, false
+     * otherwise
+     * @throws IOException if there are problems determining this Cursor's
+     * position, or if this Cursor is closed
+     * @throws UnsupportedOperationException if this operation is not supported
+     */
+    boolean isFirst() throws IOException;
+
+
+    /**
+     * Checks if this Curser is positioned at the last element.
+     *
+     * @return true if the current position is at the last element, false
+     * otherwise
+     * @throws IOException if there are problems determining this Cursor's
+     * position, or if this Cursor is closed
+     * @throws UnsupportedOperationException if this operation is not supported
+     */
+    boolean isLast() throws IOException;
+
+
+    /**
+     * Checks if this Curser is positioned after the last element.
+     *
+     * @return true if the current position is after the last element, false
+     * otherwise
+     * @throws IOException if there are problems determining this Cursor's
+     * position, or if this Cursor is closed
+     * @throws UnsupportedOperationException if this operation is not supported
+     */
+    boolean isAfterLast() throws IOException;
+
+
+    /**
+     * Checks if this Curser is positioned before the first element.
+     *
+     * @return true if the current position is before the first element, false
+     * otherwise
+     * @throws IOException if there are problems determining this Cursor's
+     * position, or if this Cursor is closed
+     * @throws UnsupportedOperationException if this operation is not supported
+     */
+    boolean isBeforeFirst() throws IOException;
+
+
+    /**
+     * Checks if this Curser is closed.  Calls to this operation should not
+     * fail with exceptions if and only if the cursor is in the closed state.
+     *
+     * @return true if this Cursor is closed, false otherwise
+     * @throws IOException if there are problems determining the cursor's closed state
+     * @throws UnsupportedOperationException if this operation is not supported
+     */
+    boolean isClosed() throws IOException;
+
+
+    /**
+     * Advances this Cursor to the previous position.
+     *
+     * @return true if the advance succeeded, false otherwise
+     * @throws IOException if there are problems advancing to the next position
+     * @throws UnsupportedOperationException if advances in this direction are not supported
+     */
+    boolean previous() throws IOException;
+
+
+    /**
+     * Advances this Cursor to the next position.
+     *
+     * @return true if the advance succeeded, false otherwise
+     * @throws IOException if there are problems advancing to this Cursor to
+     * the next position, or if this Cursor is closed
+     * @throws UnsupportedOperationException if advances in this direction are not supported
+     */
+    boolean next() throws IOException;
+
+
+    /**
+     * Gets the object at the current position.  Cursor implementations may
+     * choose to reuse element objects by re-populating them on advances
+     * instead of creating new objects on each advance.
+     *
+     * @return the object at the current position
+     * @throws IOException if the object at this Cursor's current position
+     * cannot be retrieved, or if this Cursor is closed
+     */
+    E get() throws IOException;
+
+
+    /**
+     * Gets whether or not this Cursor will return the same element object
+     * instance on get() operations for any position of this Cursor.  Some
+     * Cursor implementations may reuse the same element copying values into
+     * it for every position rather than creating and emiting new element
+     * objects on each advance.  Some Cursor implementations may return
+     * different elements for each position yet the same element instance
+     * is returned for the same position. In these cases this method should
+     * return true.
+     *
+     * @return true if elements are reused by this Cursor
+     */
+    boolean isElementReused();
+
+
+    /**
+     * Closes this Cursor and frees any resources it my have allocated.
+     * Repeated calls to this method after this Cursor has already been
+     * called should not fail with exceptions.
+     *
+     * @throws IOException if this Cursor cannot be closed
+     */
+    void close() throws IOException;
+}

Added: directory/apacheds/branches/bigbang/core-shared/src/main/java/org/apache/directory/server/core/cursor/ListCursor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/bigbang/core-shared/src/main/java/org/apache/directory/server/core/cursor/ListCursor.java?rev=593826&view=auto
==============================================================================
--- directory/apacheds/branches/bigbang/core-shared/src/main/java/org/apache/directory/server/core/cursor/ListCursor.java (added)
+++ directory/apacheds/branches/bigbang/core-shared/src/main/java/org/apache/directory/server/core/cursor/ListCursor.java Sat Nov 10 15:54:34 2007
@@ -0,0 +1,358 @@
+/*
+ * 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.server.core.cursor;
+
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * 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>
+ * @version $Rev$, $Date$
+ */
+public class ListCursor<E> implements Cursor<E>
+{
+    private final List<E> list;
+    private final int start;
+    private final int end;
+    private boolean closed;
+    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 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 )
+    {
+        if ( start < 0 || start > list.size() )
+        {
+            throw new IllegalArgumentException( "start index '" + start + "' out of range" );
+        }
+
+        if ( end < 0 || end > list.size() )
+        {
+            throw new IllegalArgumentException( "end index '" + end + "' out of range" );
+        }
+
+        // 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( "start index '" + start + "' greater than or equal to end index '"
+                    + end + "' just does not make sense" );
+        }
+
+        //noinspection ConstantConditions
+        if ( list != null )
+        {
+            this.list = list;
+        }
+        else
+        {
+            //noinspection unchecked
+            this.list = Collections.EMPTY_LIST;
+        }
+
+        this.start = start;
+        this.end = 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( 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( 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( 0, list, list.size() );
+    }
+
+
+    /**
+     * Creates a new ListCursor without any elements.
+     */
+    public ListCursor()
+    {
+        //noinspection unchecked
+        this( 0, Collections.EMPTY_LIST, 0 );
+    }
+
+
+    protected void checkClosed( String operation ) throws IOException
+    {
+        if ( closed )
+        {
+            throw new IOException( "Attempting " + operation + " operation on a closed Cursor." );
+        }
+    }
+
+
+    public void beforeFirst() throws IOException
+    {
+        checkClosed( "beforeFirst()" );
+        this.index = -1;
+    }
+
+
+    public void afterLast() throws IOException
+    {
+        checkClosed( "afterLast()" );
+        this.index = end;
+    }
+
+
+    public boolean absolute( int index ) throws IOException
+    {
+        checkClosed( "absolute()" );
+
+        if ( index < start )
+        {
+            this.index = -1;
+            return false;
+        }
+
+        if ( index >= end )
+        {
+            this.index = end;
+            return false;
+        }
+
+        this.index = index;
+        return true;
+    }
+
+
+    public boolean relative( int index ) throws IOException
+    {
+        checkClosed( "relative()" );
+
+        if ( this.index + index < start )
+        {
+            this.index = -1;
+            return false;
+        }
+
+        if ( this.index + index >= end )
+        {
+            this.index = end;
+            return false;
+        }
+
+        this.index += index;
+        return true;
+    }
+
+
+    public boolean first() throws IOException
+    {
+        checkClosed( "first()" );
+
+        if ( list.size() > 0 )
+        {
+            index = start;
+            return true;
+        }
+
+        return false;
+    }
+
+
+    public boolean last() throws IOException
+    {
+        checkClosed( "last()" );
+
+        if ( list.size() > 0 )
+        {
+            index = end - 1;
+            return true;
+        }
+        
+        return false;
+    }
+
+
+    public boolean isFirst() throws IOException
+    {
+        checkClosed( "isFirst()" );
+        return list.size() > 0 && index == start;
+    }
+
+
+    public boolean isLast() throws IOException
+    {
+        checkClosed( "isLast()" );
+        return list.size() > 0 && index == end - 1;
+
+    }
+
+
+    public boolean isAfterLast() throws IOException
+    {
+        checkClosed( "isAfterLast()" );
+        return index == end;
+    }
+
+
+    public boolean isBeforeFirst() throws IOException
+    {
+        checkClosed( "isBeforeFirst()" );
+        return index == -1;
+    }
+
+
+    public boolean isClosed()
+    {
+        return closed;
+    }
+
+
+    public boolean previous() throws IOException
+    {
+        checkClosed( "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;
+    }
+
+
+    public boolean next() throws IOException
+    {
+        checkClosed( "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;
+    }
+
+
+    public E get() throws IOException
+    {
+        checkClosed( "get()" );
+        if ( index < start || index >= end )
+        {
+            throw new IOException( "Cursor not positioned at an element" );
+        }
+
+        return list.get( index );
+    }
+
+
+    public boolean isElementReused()
+    {
+        return true;
+    }
+
+
+    public void close()
+    {
+        closed = true;
+    }
+}

Added: directory/apacheds/branches/bigbang/core-shared/src/test/java/org/apache/directory/server/core/cursor/ListCursorTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/branches/bigbang/core-shared/src/test/java/org/apache/directory/server/core/cursor/ListCursorTest.java?rev=593826&view=auto
==============================================================================
--- directory/apacheds/branches/bigbang/core-shared/src/test/java/org/apache/directory/server/core/cursor/ListCursorTest.java (added)
+++ directory/apacheds/branches/bigbang/core-shared/src/test/java/org/apache/directory/server/core/cursor/ListCursorTest.java Sat Nov 10 15:54:34 2007
@@ -0,0 +1,589 @@
+/*
+ * 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.server.core.cursor;
+
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * Tests the ListCursor class.  The assertXxxx() methods defined in this class
+ * can be collected in an abstract test case class that can be used to test
+ * the behavior of any Cursor implementation down the line.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class ListCursorTest extends TestCase
+{
+    public void testEmptyList() throws IOException
+    {
+        ListCursor<String> cursor = new ListCursor<String>();
+
+        assertFirstLastOnNewCursor( cursor, 0, 0, 0 );
+        assertAbsolute( cursor, 0, 0, 0 );
+        assertRelative( cursor, 0, 0, 0 );
+
+        // close test
+        cursor.close();
+        assertClosed( cursor, "cursor.isCloased() should return true after closing the cursor", true );
+    }
+
+
+    public void testSingleElementList() throws IOException
+    {
+        ListCursor<String> cursor = new ListCursor<String>( Collections.singletonList( "singleton" ) );
+        assertFirstLastOnNewCursor( cursor, 1, 0, 1 );
+        assertAbsolute( cursor, 1, 0, 1 );
+        assertRelative( cursor, 1, 0, 1 );
+        cursor.close();
+
+        // close test
+        cursor.close();
+        assertClosed( cursor, "cursor.isCloased() should return true after closing the cursor", true );
+
+        // bad bounds: start = end is senseless
+        try
+        {
+            cursor = new ListCursor<String>( Collections.singletonList( "singleton" ), 0 );
+            cursor.close();
+            fail( "when the start = end bounds this is senseless and should complain" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        // bad bounds: start = end is senseless
+        try
+        {
+            cursor = new ListCursor<String>( 1, Collections.singletonList( "singleton" ) );
+            cursor.close();
+            fail( "when the start = end bounds this is senseless and should complain" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        // bad bounds: start > end is senseless
+        try
+        {
+            cursor = new ListCursor<String>( 5, Collections.singletonList( "singleton" ) );
+            cursor.close();
+            fail( "when the start = end bounds this is senseless and should complain" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        // bad bounds: end < start is senseless too in another way :)
+        try
+        {
+            cursor = new ListCursor<String>( Collections.singletonList( "singleton" ), -5 );
+            cursor.close();
+            fail( "when the start = end bounds this is senseless and should complain" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        // bad bounds: start out of range
+        try
+        {
+            cursor = new ListCursor<String>( -5, Collections.singletonList( "singleton" ) );
+            cursor.close();
+            fail( "when the start = end bounds this is senseless and should complain" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        // bad bounds: end out of range
+        try
+        {
+            cursor = new ListCursor<String>( Collections.singletonList( "singleton" ), 5 );
+            cursor.close();
+            fail( "when the start = end bounds this is senseless and should complain" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+    }
+
+
+    public void testManyElementList() throws IOException
+    {
+        List<String> list = new ArrayList<String>();
+        list.add( "item 1" );
+        list.add( "item 2" );
+        list.add( "item 3" );
+        list.add( "item 4" );
+        list.add( "item 5" );
+
+        // test with bounds of the list itself
+        ListCursor<String> cursor = new ListCursor<String>( list );
+        assertFirstLastOnNewCursor( cursor, 5, 0, 5 );
+        assertAbsolute( cursor, 5, 0, 5 );
+        assertRelative( cursor, 5, 0, 5 );
+        cursor.close();
+
+        // test with nonzero lower bound
+        cursor = new ListCursor<String>( 1, list );
+        assertFirstLastOnNewCursor( cursor, 5, 1, 5 );
+        assertAbsolute( cursor, 5, 1, 5 );
+        assertRelative( cursor, 5, 1, 5 );
+        cursor.close();
+
+        // test with nonzero lower bound and upper bound
+        cursor = new ListCursor<String>( 1, list, 4 );
+        assertFirstLastOnNewCursor( cursor, 5, 1, 4 );
+        assertAbsolute( cursor, 5, 1, 4 );
+        assertRelative( cursor, 5, 1, 4 );
+
+        // close test
+        cursor.close();
+        assertClosed( cursor, "cursor.isCloased() should return true after closing the cursor", true );
+
+        // bad bounds: start = end is senseless
+        try
+        {
+            cursor = new ListCursor<String>( list, 0 );
+            cursor.close();
+            fail( "when the start = end bounds this is senseless and should complain" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        // bad bounds: start = end is senseless
+        try
+        {
+            cursor = new ListCursor<String>( 5, list );
+            cursor.close();
+            fail( "when the start = end bounds this is senseless and should complain" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        // bad bounds: start > end is senseless
+        try
+        {
+            cursor = new ListCursor<String>( 10, list );
+            cursor.close();
+            fail( "when the start = end bounds this is senseless and should complain" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        // bad bounds: end < start is senseless too in another way :)
+        try
+        {
+            cursor = new ListCursor<String>( list, -5 );
+            cursor.close();
+            fail( "when the start = end bounds this is senseless and should complain" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        // bad bounds: start out of range
+        try
+        {
+            cursor = new ListCursor<String>( -5, list );
+            cursor.close();
+            fail( "when the start = end bounds this is senseless and should complain" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+
+        // bad bounds: end out of range
+        try
+        {
+            cursor = new ListCursor<String>( list, 10 );
+            cursor.close();
+            fail( "when the start = end bounds this is senseless and should complain" );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            assertNotNull( e );
+        }
+    }
+
+
+    @SuppressWarnings ( { "ConstantConditions" } )
+    protected void assertFirstLastOnNewCursor( Cursor cursor, int listSize, int lowerBound, int upperBound )
+            throws IOException
+    {
+        assertNotNull( cursor );
+
+        String prefix = "[size, " + listSize + "] [lower, " + lowerBound + "] [upper, " + upperBound + "]: ";
+
+        assertFalse( prefix + "new cursor should not be positioned after last", cursor.isAfterLast() );
+        assertTrue( prefix + "new cursor should be positioned before first", cursor.isBeforeFirst() );
+        assertFalse( prefix + "new cursor should not be closed", cursor.isClosed() );
+        assertFalse( prefix + "new cursor should not be positioned at first", cursor.isFirst() );
+        assertFalse( prefix + "new cursor should not be positioned at last", cursor.isLast() );
+
+        // beforeFirst and afterLast tests
+        cursor.afterLast();
+        assertTrue( prefix + "cursor.afterLast() should return true on isAfterLast()", cursor.isAfterLast() );
+        assertFalse( prefix + "cursor.afterLast() should return false on isBeforeFirst()", cursor.isBeforeFirst() );
+        assertFalse( prefix + "cursor.afterLast() should return false on isFirst()", cursor.isFirst() );
+        assertFalse( prefix + "cursor.afterLast() should return false on isLast()", cursor.isLast() );
+
+        cursor.beforeFirst();
+        assertTrue( prefix + "cursor.beforeFirst() should return true on isBeforeFirst()", cursor.isBeforeFirst() );
+        assertFalse( prefix + "cursor.beforeFirst() should return false on isAfterLast()", cursor.isAfterLast() );
+        assertFalse( prefix + "cursor.beforeFirst() should return false on isFirst()", cursor.isFirst() );
+        assertFalse( prefix + "cursor.beforeFirst() should return false on isLast()", cursor.isLast() );
+
+        // first() tests
+        cursor.afterLast();
+        if ( listSize <= 0 )
+        {
+            assertFalse( "cursor.first() on empty cursor should return false", cursor.first() );
+            assertFalse( "cursor.first() on empty cursor should return false on isFirst()", cursor.isFirst() );
+            assertFalse( "cursor.first() on empty cursor should should change position state", cursor.isBeforeFirst() );
+            assertTrue( "cursor.first() on empty cursor should should change position state", cursor.isAfterLast() );
+        }
+        else
+        {
+            assertTrue( prefix + "cursor.first() should return true", cursor.first() );
+            assertTrue( prefix + "cursor.first() should return true on isFirst()", cursor.isFirst() );
+            assertFalse( prefix + "cursor.first() should change position", cursor.isBeforeFirst() );
+            assertFalse( prefix + "cursor.first() should change position", cursor.isAfterLast() );
+        }
+
+        // last() tests
+        cursor.beforeFirst();
+        if ( listSize <= 0 )
+        {
+            assertFalse( "cursor.last() on empty cursor should return false", cursor.last() );
+            assertFalse( "cursor.last() on empty cursor should return false on isLast()", cursor.isLast() );
+            assertFalse( "cursor.last() on empty cursor should should change position state", cursor.isAfterLast() );
+            assertTrue( "cursor.last() on empty cursor should should change position state", cursor.isBeforeFirst() );
+        }
+        else
+        {
+            assertTrue( prefix + "cursor.last() should return true", cursor.last() );
+            assertTrue( prefix + "cursor.last() should return true on isLast()", cursor.isLast() );
+            assertFalse( prefix + "cursor.last() should not park position after last", cursor.isAfterLast() );
+            assertFalse( prefix + "cursor.last() should not park position before first", cursor.isBeforeFirst() );
+        }
+
+        // next() tests
+        cursor.beforeFirst();
+        if ( listSize <= 0 )
+        {
+            assertFalse( "empty cursor.next() should return false", cursor.next() );
+            assertTrue( "empty cursor.next() should change pos to after last", cursor.isAfterLast() );
+            assertFalse( "empty cursor.next() should change pos to after last", cursor.isBeforeFirst() );
+        }
+        else
+        {
+            assertTrue( prefix + "cursor.next() should return true", cursor.next() );
+            assertTrue( prefix + "cursor.next() should change pos to first element", cursor.isFirst() );
+            assertFalse( prefix + "cursor.next() should not change pos to after last", cursor.isAfterLast() );
+            assertFalse( prefix + "cursor.next() should not change pos to before first", cursor.isBeforeFirst() );
+
+            while( cursor.next() )
+            {
+                assertFalse( prefix + "cursor.next() should not change pos to before first", cursor.isBeforeFirst() );
+                assertFalse( prefix + "cursor.next() should not change pos to first after first advance forward",
+                        cursor.isFirst() );
+            }
+
+            assertTrue( prefix + "cursor.next() failure should put pos to after last", cursor.isAfterLast() );
+        }
+
+        // previous() tests
+        cursor.afterLast();
+        if ( listSize <= 0 )
+        {
+            assertFalse( "empty cursor.previous() should return false", cursor.previous() );
+            assertTrue( "empty cursor.previous() should change pos to before first", cursor.isBeforeFirst() );
+            assertFalse( "empty cursor.previous() should change pos to before first", cursor.isAfterLast() );
+        }
+        else
+        {
+            assertTrue( prefix + "cursor.previous() should return true", cursor.previous() );
+            assertTrue( prefix + "cursor.previous() should change pos to last element", cursor.isLast() );
+            assertFalse( prefix + "cursor.previous() should not change pos to before first", cursor.isBeforeFirst() );
+            assertFalse( prefix + "cursor.previous() should not change pos to after last", cursor.isAfterLast() );
+
+            while( cursor.previous() )
+            {
+                assertFalse( prefix + "cursor.previous() should not change pos to after last", cursor.isAfterLast() );
+                assertFalse( prefix + "cursor.previous() should not change pos to last after first advance backward",
+                        cursor.isLast() );
+            }
+
+            assertTrue( prefix + "cursor.previous() failure should put pos to before first", cursor.isBeforeFirst() );
+        }
+    }
+
+
+    protected void assertAbsolute( Cursor cursor, int listSize, int lowerBound, int upperBound )
+            throws IOException
+    {
+        String prefix = "[size, " + listSize + "] [lower, " + lowerBound + "] [upper, " + upperBound + "]: ";
+
+        // test absolute() advance with change of position below lower bound
+        cursor.afterLast();
+        assertFalse( prefix + "cursor.absolute(" + ( lowerBound - 1 ) +
+                ") should return false and change state to before first", cursor.absolute( lowerBound - 1 ) );
+        assertTrue( prefix + "cursor.relative(" + ( lowerBound - 1 ) +
+                ") should change pos to before first", cursor.isBeforeFirst() );
+        assertFalse( prefix + "cursor.relative(" + ( lowerBound - 1 ) +
+                ") should --NOT-- change pos to after last", cursor.isAfterLast() );
+
+        if ( listSize == 0 )
+        {
+            // Corner case!!!  Technically the 0th index is the 1st element
+            // which is greater than 0 elements which is the size of the list
+            // so technically the observed state change for index = 0 should be
+            // the same as when index > 0.
+            cursor.beforeFirst();
+            assertFalse( "empty cursor.absolute(0) should fail but change state to after last", cursor.absolute( 0 ) );
+            assertFalse( "empty cursor.absolute(0) should change pos to after last", cursor.isBeforeFirst() );
+            assertTrue( "empty cursor.absolute(0) should change pos to after last", cursor.isAfterLast() );
+        }
+
+        // test absolute() advance with change of position above upper bound
+        cursor.beforeFirst();
+        assertFalse( prefix + "cursor.absolute(" + ( upperBound + 1 )
+                + ") should return false but change state to after last", cursor.absolute( upperBound + 1 ) );
+        assertFalse( prefix + "cursor.absolute(" + ( upperBound + 1 ) + ") should change pos to after last",
+                cursor.isBeforeFirst() );
+        assertTrue( prefix + "cursor.absolute(" + ( upperBound + 1 ) + ") should change pos to after last",
+                cursor.isAfterLast() );
+    }
+
+
+    protected void assertRelative( Cursor cursor, int listSize, int lowerBound, int upperBound )
+            throws IOException
+    {
+        String prefix = "[size, " + listSize + "] [lower, " + lowerBound + "] [upper, " + upperBound + "]: ";
+
+        // test relative() advance which changes position below lower bound
+        cursor.afterLast();
+        int relativePos = - ( upperBound - lowerBound + 1 );
+
+        assertFalse( prefix + "cursor.relative(" + relativePos +
+                ") should return false and change state to before first", cursor.relative( - ( upperBound + 1 ) ) );
+        assertTrue( prefix + "cursor.relative(" + relativePos +
+                ") should change pos to before first", cursor.isBeforeFirst() );
+        assertFalse( prefix + "cursor.relative(" + relativePos +
+                ") should --NOT-- change pos to after last", cursor.isAfterLast() );
+
+        // make sure relative(0) does not change pos if begin state is after last
+        cursor.afterLast();
+        assertFalse( prefix + "cursor.relative(0) should return false and have no effect on pos",
+                cursor.relative( 0 ) );
+        assertFalse( prefix + "cursor.relative(0) should have no effect on changing state", cursor.isBeforeFirst() );
+        assertTrue( prefix + "cursor.relative(0) should have no effect on changing state", cursor.isAfterLast() );
+
+        // make sure relative(0) does not change pos if begin state is before first
+        cursor.beforeFirst();
+        assertFalse( prefix + "cursor.relative(0) should return false and have no effect on pos",
+                cursor.relative( 0 ) );
+        assertTrue( prefix + "cursor.relative(0) should have no effect on changing state", cursor.isBeforeFirst() );
+        assertFalse( prefix + "cursor.relative(0) should have no effect on changing state", cursor.isAfterLast() );
+
+        // make relative() advance which changes position above upper bound
+        cursor.beforeFirst();
+        assertFalse( prefix + "cursor.relative(" + ( upperBound + 1 )
+                + ") should return false but change state to after last", cursor.relative( upperBound + 1 ) );
+        assertFalse( prefix + "cursor.relative(" + ( upperBound + 1 ) + ") should change pos to after last",
+                cursor.isBeforeFirst() );
+        assertTrue( prefix + "cursor.relative(" + ( upperBound + 1 ) + ") should change pos to after last",
+                cursor.isAfterLast() );
+    }
+
+
+    protected void assertClosed( Cursor cursor, String msg, boolean expected )
+    {
+        try
+        {
+            assertEquals( msg, expected, cursor.isClosed() );
+        }
+        catch ( IOException e )
+        {
+            fail( "cursor.isClosed() test should not fail after closing the cursor" );
+        }
+
+        try
+        {
+            cursor.close();
+        }
+        catch ( IOException e )
+        {
+            fail( "cursor.close() after closing the cursor should not fail with exceptions" );
+        }
+
+
+        try
+        {
+            cursor.absolute( 1 );
+            fail( "cursor.absolute() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            cursor.afterLast();
+            fail( "cursor.afterLast() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            cursor.beforeFirst();
+            fail( "cursor.beforeFirst() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            cursor.first();
+            fail( "cursor.first() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            cursor.get();
+            fail( "cursor.get() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            cursor.isAfterLast();
+            fail( "cursor.isAfterLast() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            cursor.isBeforeFirst();
+            fail( "cursor.isBeforeFirst() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            cursor.isFirst();
+            fail( "cursor.isFirst() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            cursor.isLast();
+            fail( "cursor.isLast() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            cursor.last();
+            fail( "cursor.last() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            cursor.next();
+            fail( "cursor.next() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            cursor.previous();
+            fail( "cursor.previous() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+
+        try
+        {
+            cursor.relative( 1 );
+            fail( "cursor.relative() after closing the cursor should fail with an IOException" );
+        }
+        catch ( IOException e )
+        {
+            assertNotNull( e );
+        }
+    }
+}
\ No newline at end of file

Propchange: directory/apacheds/branches/bigbang/core-shared/src/test/java/org/apache/directory/server/core/cursor/ListCursorTest.java
------------------------------------------------------------------------------
    svn:executable = *