You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by tn...@apache.org on 2013/04/16 21:53:49 UTC

svn commit: r1468578 - in /commons/proper/collections/trunk/src: main/java/org/apache/commons/collections/queue/ test/java/org/apache/commons/collections/queue/ test/resources/data/test/

Author: tn
Date: Tue Apr 16 19:53:49 2013
New Revision: 1468578

URL: http://svn.apache.org/r1468578
Log:
[COLLECTIONS-432] Add CircularFifoQueue based on CircularFifoBuffer.

Added:
    commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/CircularFifoQueue.java   (with props)
    commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/queue/CircularFifoQueueTest.java   (with props)
    commons/proper/collections/trunk/src/test/resources/data/test/CircularFifoQueue.emptyCollection.version4.obj   (with props)
    commons/proper/collections/trunk/src/test/resources/data/test/CircularFifoQueue.fullCollection.version4.obj   (with props)
Modified:
    commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/package-info.java

Added: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/CircularFifoQueue.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/CircularFifoQueue.java?rev=1468578&view=auto
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/CircularFifoQueue.java (added)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/CircularFifoQueue.java Tue Apr 16 19:53:49 2013
@@ -0,0 +1,415 @@
+/*
+ * 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.commons.collections.queue;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.AbstractCollection;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+
+import org.apache.commons.collections.BoundedCollection;
+
+/**
+ * CircularFifoQueue is a first-in first-out queue with a fixed size that
+ * replaces its oldest element if full.
+ * <p>
+ * The removal order of a {@link CircularFifoQueue} is based on the
+ * insertion order; elements are removed in the same order in which they
+ * were added.  The iteration order is the same as the removal order.
+ * <p>
+ * The {@link #add(Object)}, {@link #remove()} and {@link #get()} operations
+ * all perform in constant time.  All other operations perform in linear
+ * time or worse.
+ * <p>
+ * This queue prevents null objects from being added.
+ *
+ * @since 4.0
+ * @version $Id$
+ */
+public class CircularFifoQueue<E> extends AbstractCollection<E>
+    implements Queue<E>, BoundedCollection<E>, Serializable {
+
+    /** Serialization version. */
+    private static final long serialVersionUID = -8423413834657610406L;
+
+    /** Underlying storage array. */
+    private transient E[] elements;
+
+    /** Array index of first (oldest) queue element. */
+    private transient int start = 0;
+
+    /**
+     * Index mod maxElements of the array position following the last queue
+     * element.  Queue elements start at elements[start] and "wrap around"
+     * elements[maxElements-1], ending at elements[decrement(end)].
+     * For example, elements = {c,a,b}, start=1, end=1 corresponds to
+     * the queue [a,b,c].
+     */
+    private transient int end = 0;
+
+    /** Flag to indicate if the queue is currently full. */
+    private transient boolean full = false;
+
+    /** Capacity of the queue. */
+    private final int maxElements;
+
+    /**
+     * Constructor that creates a queue with the default size of 32.
+     */
+    public CircularFifoQueue() {
+        this(32);
+    }
+
+    /**
+     * Constructor that creates a queue with the specified size.
+     *
+     * @param size  the size of the queue (cannot be changed)
+     * @throws IllegalArgumentException  if the size is &lt; 1
+     */
+    @SuppressWarnings("unchecked")
+    public CircularFifoQueue(final int size) {
+        if (size <= 0) {
+            throw new IllegalArgumentException("The size must be greater than 0");
+        }
+        elements = (E[]) new Object[size];
+        maxElements = elements.length;
+    }
+
+    /**
+     * Constructor that creates a queue from the specified collection.
+     * The collection size also sets the queue size.
+     *
+     * @param coll  the collection to copy into the queue, may not be null
+     * @throws NullPointerException if the collection is null
+     */
+    public CircularFifoQueue(final Collection<E> coll) {
+        this(coll.size());
+        addAll(coll);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Write the queue out using a custom routine.
+     *
+     * @param out  the output stream
+     * @throws IOException if an I/O error occurs while writing to the output stream
+     */
+    private void writeObject(final ObjectOutputStream out) throws IOException {
+        out.defaultWriteObject();
+        out.writeInt(size());
+        for (final E e : this) {
+            out.writeObject(e);
+        }
+    }
+
+    /**
+     * Read the queue in using a custom routine.
+     *
+     * @param in  the input stream
+     * @throws IOException if an I/O error occurs while writing to the output stream
+     * @throws ClassNotFoundException if the class of a serialized object can not be found
+     */
+    @SuppressWarnings("unchecked")
+    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        elements = (E[]) new Object[maxElements];
+        final int size = in.readInt();
+        for (int i = 0; i < size; i++) {
+            elements[i] = (E) in.readObject();
+        }
+        start = 0;
+        full = size == maxElements;
+        if (full) {
+            end = 0;
+        } else {
+            end = size;
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns the number of elements stored in the queue.
+     *
+     * @return this queue's size
+     */
+    @Override
+    public int size() {
+        int size = 0;
+
+        if (end < start) {
+            size = maxElements - start + end;
+        } else if (end == start) {
+            size = full ? maxElements : 0;
+        } else {
+            size = end - start;
+        }
+
+        return size;
+    }
+
+    /**
+     * Returns true if this queue is empty; false otherwise.
+     *
+     * @return true if this queue is empty
+     */
+    @Override
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * A {@code CircularFifoQueue} can never be full, thus this returns always
+     * {@code false}.
+     *
+     * @return always returns {@code false}
+     */
+    public boolean isFull() {
+        return false;
+    }
+
+    private boolean isAtFullCapacity() {
+        return size() == maxElements;
+    }
+
+    /**
+     * Gets the maximum size of the collection (the bound).
+     *
+     * @return the maximum number of elements the collection can hold
+     */
+    public int maxSize() {
+        return maxElements;
+    }
+
+    /**
+     * Clears this queue.
+     */
+    @Override
+    public void clear() {
+        full = false;
+        start = 0;
+        end = 0;
+        Arrays.fill(elements, null);
+    }
+
+    /**
+     * Adds the given element to this queue. If the queue is full, the least recently added
+     * element is discarded so that a new element can be inserted.
+     *
+     * @param element  the element to add
+     * @return true, always
+     * @throws NullPointerException  if the given element is null
+     */
+    @Override
+    public boolean add(final E element) {
+        if (null == element) {
+            throw new NullPointerException("Attempted to add null object to queue");
+        }
+
+        if (isAtFullCapacity()) {
+            remove();
+        }
+
+        elements[end++] = element;
+
+        if (end >= maxElements) {
+            end = 0;
+        }
+
+        if (end == start) {
+            full = true;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns the element at the specified position in this queue.
+     *
+     * @param index the position of the element in the queue
+     * @return the element at position {@code index}
+     * @throws NoSuchElementException if the requested position is outside the range [0, size)
+     */
+    public E get(final int index) {
+        final int sz = size();
+        if (index < 0 || index >= sz) {
+            throw new NoSuchElementException(
+                    String.format("The specified index (%1$d) is outside the available range [0, %2$d)",
+                                  index, sz));
+        }
+        
+        final int idx = (start + index) % maxElements;
+        return elements[idx];
+    }
+ 
+    //-----------------------------------------------------------------------
+
+    /**
+     * Adds the given element to this queue. If the queue is full, the least recently added
+     * element is discarded so that a new element can be inserted.
+     *
+     * @param element  the element to add
+     * @return true, always
+     * @throws NullPointerException  if the given element is null
+     */
+    public boolean offer(E element) {
+        return add(element);
+    }
+
+    public E poll() {
+        if (isEmpty()) {
+            return null;
+        } else {
+            return remove();
+        }
+    }
+
+    public E element() {
+        if (isEmpty()) {
+            throw new NoSuchElementException("queue is empty");
+        } else {
+            return peek();
+        }
+    }
+
+    public E peek() {
+        if (isEmpty()) {
+            return null;
+        }
+        return elements[start];
+    }
+
+    public E remove() {
+        if (isEmpty()) {
+            throw new NoSuchElementException("queue is empty");
+        }
+
+        final E element = elements[start];
+        if (null != element) {
+            elements[start++] = null;
+
+            if (start >= maxElements) {
+                start = 0;
+            }
+            full = false;
+        }
+        return element;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Increments the internal index.
+     *
+     * @param index  the index to increment
+     * @return the updated index
+     */
+    private int increment(int index) {
+        index++;
+        if (index >= maxElements) {
+            index = 0;
+        }
+        return index;
+    }
+
+    /**
+     * Decrements the internal index.
+     *
+     * @param index  the index to decrement
+     * @return the updated index
+     */
+    private int decrement(int index) {
+        index--;
+        if (index < 0) {
+            index = maxElements - 1;
+        }
+        return index;
+    }
+
+    /**
+     * Returns an iterator over this queue's elements.
+     *
+     * @return an iterator over this queue's elements
+     */
+    @Override
+    public Iterator<E> iterator() {
+        return new Iterator<E>() {
+
+            private int index = start;
+            private int lastReturnedIndex = -1;
+            private boolean isFirst = full;
+
+            public boolean hasNext() {
+                return isFirst || index != end;
+            }
+
+            public E next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+                isFirst = false;
+                lastReturnedIndex = index;
+                index = increment(index);
+                return elements[lastReturnedIndex];
+            }
+
+            public void remove() {
+                if (lastReturnedIndex == -1) {
+                    throw new IllegalStateException();
+                }
+
+                // First element can be removed quickly
+                if (lastReturnedIndex == start) {
+                    CircularFifoQueue.this.remove();
+                    lastReturnedIndex = -1;
+                    return;
+                }
+
+                int pos = lastReturnedIndex + 1;
+                if (start < lastReturnedIndex && pos < end) {
+                    // shift in one part
+                    System.arraycopy(elements, pos, elements, lastReturnedIndex, end - pos);
+                } else {
+                    // Other elements require us to shift the subsequent elements
+                    while (pos != end) {
+                        if (pos >= maxElements) {
+                            elements[pos - 1] = elements[0];
+                            pos = 0;
+                        } else {
+                            elements[decrement(pos)] = elements[pos];
+                            pos = increment(pos);
+                        }
+                    }
+                }
+
+                lastReturnedIndex = -1;
+                end = decrement(end);
+                elements[end] = null;
+                full = false;
+                index = decrement(index);
+            }
+
+        };
+    }
+    
+}

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/CircularFifoQueue.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/CircularFifoQueue.java
------------------------------------------------------------------------------
    svn:keywords = Id Revision HeadURL

Propchange: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/CircularFifoQueue.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/package-info.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/package-info.java?rev=1468578&r1=1468577&r2=1468578&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/package-info.java (original)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/queue/package-info.java Tue Apr 16 19:53:49 2013
@@ -15,7 +15,12 @@
  * limitations under the License.
  */
 /**
- * This package contains decorators for the {@link java.util.Queue Queue} interface.
+ * This package contains implementations for the {@link java.util.Queue Queue} interface.
+ * <p>
+ * The following implementations are provided in the package:
+ * <ul>
+ *   <li>CircularFifoQueue - implements a queue with a fixed size that discards oldest when full
+ * </ul>
  * <p>
  * The following decorators are provided in the package:
  * <ul>

Added: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/queue/CircularFifoQueueTest.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/queue/CircularFifoQueueTest.java?rev=1468578&view=auto
==============================================================================
--- commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/queue/CircularFifoQueueTest.java (added)
+++ commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/queue/CircularFifoQueueTest.java Tue Apr 16 19:53:49 2013
@@ -0,0 +1,461 @@
+/*
+ * 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.commons.collections.queue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+
+import junit.framework.Test;
+import org.apache.commons.collections.BulkTest;
+import org.apache.commons.collections.collection.AbstractCollectionTest;
+
+/**
+ * Test cases for CircularFifoQueue.
+ *
+ * @version $Id$
+ */
+public class CircularFifoQueueTest<E> extends AbstractCollectionTest<E> {
+
+    public CircularFifoQueueTest(final String n) {
+        super(n);
+    }
+
+    public static Test suite() {
+        return BulkTest.makeSuite(CircularFifoQueueTest.class);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     *  Runs through the regular verifications, but also verifies that
+     *  the buffer contains the same elements in the same sequence as the
+     *  list.
+     */
+    @Override
+    public void verify() {
+        super.verify();
+        final Iterator<E> iterator1 = getCollection().iterator();
+        final Iterator<E> iterator2 = getConfirmed().iterator();
+        while (iterator2.hasNext()) {
+            assertTrue(iterator1.hasNext());
+            final Object o1 = iterator1.next();
+            final Object o2 = iterator2.next();
+            assertEquals(o1, o2);
+        }
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Overridden because CircularFifoQueue doesn't allow null elements.
+     * @return false
+     */
+    @Override
+    public boolean isNullSupported() {
+        return false;
+    }
+
+    /**
+     * Overridden because CircularFifoQueue isn't fail fast.
+     * @return false
+     */
+    @Override
+    public boolean isFailFastSupported() {
+        return false;
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Returns an empty ArrayList.
+     *
+     * @return an empty ArrayList
+     */
+    @Override
+    public Collection<E> makeConfirmedCollection() {
+        return new ArrayList<E>();
+    }
+
+    /**
+     * Returns a full ArrayList.
+     *
+     * @return a full ArrayList
+     */
+    @Override
+    public Collection<E> makeConfirmedFullCollection() {
+        final Collection<E> c = makeConfirmedCollection();
+        c.addAll(java.util.Arrays.asList(getFullElements()));
+        return c;
+    }
+
+    /**
+     * Returns an empty CircularFifoQueue that won't overflow.
+     *
+     * @return an empty CircularFifoQueue
+     */
+    @Override
+    public Collection<E> makeObject() {
+        return new CircularFifoQueue<E>(100);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
+     * Tests that the removal operation actually removes the first element.
+     */
+    @SuppressWarnings("unchecked")
+    public void testCircularFifoQueueCircular() {
+        final List<E> list = new ArrayList<E>();
+        list.add((E) "A");
+        list.add((E) "B");
+        list.add((E) "C");
+        final Queue<E> queue = new CircularFifoQueue<E>(list);
+
+        assertEquals(true, queue.contains("A"));
+        assertEquals(true, queue.contains("B"));
+        assertEquals(true, queue.contains("C"));
+
+        queue.add((E) "D");
+
+        assertEquals(false, queue.contains("A"));
+        assertEquals(true, queue.contains("B"));
+        assertEquals(true, queue.contains("C"));
+        assertEquals(true, queue.contains("D"));
+
+        assertEquals("B", queue.peek());
+        assertEquals("B", queue.remove());
+        assertEquals("C", queue.remove());
+        assertEquals("D", queue.remove());
+    }
+
+    /**
+     * Tests that the removal operation actually removes the first element.
+     */
+    public void testCircularFifoQueueRemove() {
+        resetFull();
+        final int size = getConfirmed().size();
+        for (int i = 0; i < size; i++) {
+            final Object o1 = getCollection().remove();
+            final Object o2 = getConfirmed().remove(0);
+            assertEquals("Removed objects should be equal", o1, o2);
+            verify();
+        }
+
+        try {
+            getCollection().remove();
+            fail("Empty queue should raise Underflow.");
+        } catch (final NoSuchElementException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Tests that the constructor correctly throws an exception.
+     */
+    public void testConstructorException1() {
+        try {
+            new CircularFifoQueue<E>(0);
+        } catch (final IllegalArgumentException ex) {
+            return;
+        }
+        fail();
+    }
+
+    /**
+     * Tests that the constructor correctly throws an exception.
+     */
+    public void testConstructorException2() {
+        try {
+            new CircularFifoQueue<E>(-20);
+        } catch (final IllegalArgumentException ex) {
+            return;
+        }
+        fail();
+    }
+
+    /**
+     * Tests that the constructor correctly throws an exception.
+     */
+    public void testConstructorException3() {
+        try {
+            new CircularFifoQueue<E>(null);
+        } catch (final NullPointerException ex) {
+            return;
+        }
+        fail();
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testRemoveError1() throws Exception {
+        // based on bug 33071
+        final CircularFifoQueue<E> fifo = new CircularFifoQueue<E>(5);
+        fifo.add((E) "1");
+        fifo.add((E) "2");
+        fifo.add((E) "3");
+        fifo.add((E) "4");
+        fifo.add((E) "5");
+
+        assertEquals("[1, 2, 3, 4, 5]", fifo.toString());
+
+        fifo.remove("3");
+        assertEquals("[1, 2, 4, 5]", fifo.toString());
+
+        fifo.remove("4");
+        assertEquals("[1, 2, 5]", fifo.toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testRemoveError2() throws Exception {
+        // based on bug 33071
+        final CircularFifoQueue<E> fifo = new CircularFifoQueue<E>(5);
+        fifo.add((E) "1");
+        fifo.add((E) "2");
+        fifo.add((E) "3");
+        fifo.add((E) "4");
+        fifo.add((E) "5");
+        fifo.add((E) "6");
+
+        assertEquals(5, fifo.size());
+        assertEquals("[2, 3, 4, 5, 6]", fifo.toString());
+
+        fifo.remove("3");
+        assertEquals("[2, 4, 5, 6]", fifo.toString());
+
+        fifo.remove("4");
+        assertEquals("[2, 5, 6]", fifo.toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testRemoveError3() throws Exception {
+        // based on bug 33071
+        final CircularFifoQueue<E> fifo = new CircularFifoQueue<E>(5);
+        fifo.add((E) "1");
+        fifo.add((E) "2");
+        fifo.add((E) "3");
+        fifo.add((E) "4");
+        fifo.add((E) "5");
+
+        assertEquals("[1, 2, 3, 4, 5]", fifo.toString());
+
+        fifo.remove("3");
+        assertEquals("[1, 2, 4, 5]", fifo.toString());
+
+        fifo.add((E) "6");
+        fifo.add((E) "7");
+        assertEquals("[2, 4, 5, 6, 7]", fifo.toString());
+
+        fifo.remove("4");
+        assertEquals("[2, 5, 6, 7]", fifo.toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testRemoveError4() throws Exception {
+        // based on bug 33071
+        final CircularFifoQueue<E> fifo = new CircularFifoQueue<E>(5);
+        fifo.add((E) "1");
+        fifo.add((E) "2");
+        fifo.add((E) "3");
+        fifo.add((E) "4");
+        fifo.add((E) "5");  // end=0
+        fifo.add((E) "6");  // end=1
+        fifo.add((E) "7");  // end=2
+
+        assertEquals("[3, 4, 5, 6, 7]", fifo.toString());
+
+        fifo.remove("4");  // remove element in middle of array, after start
+        assertEquals("[3, 5, 6, 7]", fifo.toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testRemoveError5() throws Exception {
+        // based on bug 33071
+        final CircularFifoQueue<E> fifo = new CircularFifoQueue<E>(5);
+        fifo.add((E) "1");
+        fifo.add((E) "2");
+        fifo.add((E) "3");
+        fifo.add((E) "4");
+        fifo.add((E) "5");  // end=0
+        fifo.add((E) "6");  // end=1
+        fifo.add((E) "7");  // end=2
+
+        assertEquals("[3, 4, 5, 6, 7]", fifo.toString());
+
+        fifo.remove("5");  // remove element at last pos in array
+        assertEquals("[3, 4, 6, 7]", fifo.toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testRemoveError6() throws Exception {
+        // based on bug 33071
+        final CircularFifoQueue<E> fifo = new CircularFifoQueue<E>(5);
+        fifo.add((E) "1");
+        fifo.add((E) "2");
+        fifo.add((E) "3");
+        fifo.add((E) "4");
+        fifo.add((E) "5");  // end=0
+        fifo.add((E) "6");  // end=1
+        fifo.add((E) "7");  // end=2
+
+        assertEquals("[3, 4, 5, 6, 7]", fifo.toString());
+
+        fifo.remove("6");  // remove element at position zero in array
+        assertEquals("[3, 4, 5, 7]", fifo.toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testRemoveError7() throws Exception {
+        // based on bug 33071
+        final CircularFifoQueue<E> fifo = new CircularFifoQueue<E>(5);
+        fifo.add((E) "1");
+        fifo.add((E) "2");
+        fifo.add((E) "3");
+        fifo.add((E) "4");
+        fifo.add((E) "5");  // end=0
+        fifo.add((E) "6");  // end=1
+        fifo.add((E) "7");  // end=2
+
+        assertEquals("[3, 4, 5, 6, 7]", fifo.toString());
+
+        fifo.remove("7");  // remove element at position one in array
+        assertEquals("[3, 4, 5, 6]", fifo.toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testRemoveError8() throws Exception {
+        // based on bug 33071
+        final CircularFifoQueue<E> fifo = new CircularFifoQueue<E>(5);
+        fifo.add((E) "1");
+        fifo.add((E) "2");
+        fifo.add((E) "3");
+        fifo.add((E) "4");
+        fifo.add((E) "5");  // end=0
+        fifo.add((E) "6");  // end=1
+        fifo.add((E) "7");  // end=2
+        fifo.add((E) "8");  // end=3
+
+        assertEquals("[4, 5, 6, 7, 8]", fifo.toString());
+
+        fifo.remove("7");  // remove element at position one in array, need to shift 8
+        assertEquals("[4, 5, 6, 8]", fifo.toString());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testRemoveError9() throws Exception {
+        // based on bug 33071
+        final CircularFifoQueue<E> fifo = new CircularFifoQueue<E>(5);
+        fifo.add((E) "1");
+        fifo.add((E) "2");
+        fifo.add((E) "3");
+        fifo.add((E) "4");
+        fifo.add((E) "5");  // end=0
+        fifo.add((E) "6");  // end=1
+        fifo.add((E) "7");  // end=2
+        fifo.add((E) "8");  // end=3
+
+        assertEquals("[4, 5, 6, 7, 8]", fifo.toString());
+
+        fifo.remove("8");  // remove element at position two in array
+        assertEquals("[4, 5, 6, 7]", fifo.toString());
+    }
+
+    //-----------------------------------------------------------------------
+    @SuppressWarnings("unchecked")
+    public void testRepeatedSerialization() throws Exception {
+        // bug 31433
+        final CircularFifoQueue<E> b = new CircularFifoQueue<E>(2);
+        b.add((E) "a");
+        assertEquals(1, b.size());
+        assertEquals(true, b.contains("a"));
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        new ObjectOutputStream(bos).writeObject(b);
+
+        final CircularFifoQueue<E> b2 = (CircularFifoQueue<E>) new ObjectInputStream(
+            new ByteArrayInputStream(bos.toByteArray())).readObject();
+
+        assertEquals(1, b2.size());
+        assertEquals(true, b2.contains("a"));
+        b2.add((E) "b");
+        assertEquals(2, b2.size());
+        assertEquals(true, b2.contains("a"));
+        assertEquals(true, b2.contains("b"));
+
+        bos = new ByteArrayOutputStream();
+        new ObjectOutputStream(bos).writeObject(b2);
+
+        final CircularFifoQueue<E> b3 = (CircularFifoQueue<E>) new ObjectInputStream(
+            new ByteArrayInputStream(bos.toByteArray())).readObject();
+
+        assertEquals(2, b3.size());
+        assertEquals(true, b3.contains("a"));
+        assertEquals(true, b3.contains("b"));
+        b3.add((E) "c");
+        assertEquals(2, b3.size());
+        assertEquals(true, b3.contains("b"));
+        assertEquals(true, b3.contains("c"));
+    }
+
+    public void testGetIndex() {
+        resetFull();
+        
+        final CircularFifoQueue<E> queue = getCollection();
+        final List<E> confirmed = getConfirmed();
+        for (int i = 0; i < confirmed.size(); i++) {
+            assertEquals(confirmed.get(i), queue.get(i));
+        }
+
+        // remove the first two elements and check again
+        queue.remove();
+        queue.remove();
+        
+        for (int i = 0; i < queue.size(); i++) {
+            assertEquals(confirmed.get(i + 2), queue.get(i));
+        }        
+    }
+
+    @Override
+    public String getCompatibilityVersion() {
+        return "4";
+    }
+
+//    public void testCreate() throws Exception {
+//        resetEmpty();
+//        writeExternalFormToDisk((java.io.Serializable) getCollection(), "src/test/resources/data/test/CircularFifoQueue.emptyCollection.version4.obj");
+//        resetFull();
+//        writeExternalFormToDisk((java.io.Serializable) getCollection(), "src/test/resources/data/test/CircularFifoQueue.fullCollection.version4.obj");
+//    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public CircularFifoQueue<E> getCollection() {
+        return (CircularFifoQueue<E>) super.getCollection();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<E> getConfirmed() {
+        return (List<E>) super.getConfirmed();
+    }
+}

Propchange: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/queue/CircularFifoQueueTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/queue/CircularFifoQueueTest.java
------------------------------------------------------------------------------
    svn:keywords = Id Revision HeadURL

Propchange: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/queue/CircularFifoQueueTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/proper/collections/trunk/src/test/resources/data/test/CircularFifoQueue.emptyCollection.version4.obj
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/resources/data/test/CircularFifoQueue.emptyCollection.version4.obj?rev=1468578&view=auto
==============================================================================
Binary file - no diff available.

Propchange: commons/proper/collections/trunk/src/test/resources/data/test/CircularFifoQueue.emptyCollection.version4.obj
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: commons/proper/collections/trunk/src/test/resources/data/test/CircularFifoQueue.fullCollection.version4.obj
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/resources/data/test/CircularFifoQueue.fullCollection.version4.obj?rev=1468578&view=auto
==============================================================================
Binary file - no diff available.

Propchange: commons/proper/collections/trunk/src/test/resources/data/test/CircularFifoQueue.fullCollection.version4.obj
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream