diff --git a/src/main/java/org/apache/commons/collections4/deque/BlockingDeque.java b/src/main/java/org/apache/commons/collections4/deque/BlockingDeque.java new file mode 100644 index 0000000000..a85fd30759 --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/deque/BlockingDeque.java @@ -0,0 +1,525 @@ +/* + * 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.collections4.deque; + +import java.io.Serializable; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; + +import java.util.Deque; +import java.util.concurrent.locks.Condition; + +import java.util.concurrent.locks.ReentrantLock; + +/** + * Decorates another {@link Deque} to support blocking operations that wait for the deque + * to become non-empty when retrieving an element, and wait for space to become available + * in the deque when storing an element if the decorated deque has a capacity limit. + *

+ * This deque exists to support blocking operations for the decorated deque. It is thread + * safe. + *

+ * + * @param the type of elements held in this deque + * @since 4.5 + */ +public class BlockingDeque extends AbstractCollection implements Deque, Serializable { + + /** Serialization version */ + private static final long serialVersionUID = 3961982897426944557L; + + final Deque deque; + final ReentrantLock lock = new ReentrantLock(); + + /** Condition for waiting takes */ + private final Condition notEmpty = lock.newCondition(); + + /** Condition for waiting puts */ + private final Condition notFull = lock.newCondition(); + + /** + * Factory method to create a blocking deque. + * + * @param + * the type of the elements in the deque + * @param deque + * the deque to decorate, must not be null + * @return a new blocking Deque + * @throws NullPointerException + * if deque is null + */ + public static BlockingDeque blockingDeque(final Deque deque) { + return new BlockingDeque<>(deque); + } + + /** + * Constructor that wraps (not copies). + * + * @param deque + * the deque to decorate, must not be null + * @throws NullPointerException + * if deque is null + */ + public BlockingDeque(final Deque deque) { + if (deque == null) { + throw new NullPointerException(); + } + this.deque = deque; + } + + /** + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + */ + @Override + public void addFirst(final E e) { + if (!offerFirst(e)) + throw new IllegalStateException("Deque is full"); + } + + /** + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + */ + @Override + public void addLast(final E e) { + if (!offerLast(e)) + throw new IllegalStateException("Deque is full"); + } + + /** + * @return {@code true} if the element was added to this deque, else + * {@code false} + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + @Override + public boolean offerFirst(final E e) { + lock.lock(); + try { + boolean r; + if (r = deque.offerFirst(e)) + notEmpty.signal(); + return r; + } catch (Exception ex) { + throw ex; + } finally { + lock.unlock(); + } + } + + /** + * @return {@code true} if the element was added to this deque, else + * {@code false} + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + @Override + public boolean offerLast(final E e) { + lock.lock(); + try { + boolean r; + if (r = deque.offerLast(e)) + notEmpty.signal(); + return r; + } catch (Exception ex) { + throw ex; + } finally { + lock.unlock(); + } + } + + /** + * Inserts the specified element at the front of this deque. It will be + * blocked and wait for space to become available if the deque is full. + * + * @param e the element to add + * @throws InterruptedException if interrupted while waiting + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + public void putFirst(final E e) throws InterruptedException { + lock.lock(); + try { + while (!deque.offerFirst(e)) + notFull.await(); + notEmpty.signal(); + } finally { + lock.unlock(); + } + } + + /** + * Inserts the specified element at the end of this deque. It will be + * blocked and wait for space to become available if the deque is full. + * + * @param e the element to add + * @throws InterruptedException if interrupted while waiting + * @throws NullPointerException if the specified element is null and this + * deque does not permit null elements + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this deque + */ + public void putLast(final E e) throws InterruptedException { + lock.lock(); + try { + while (!deque.offerLast(e)) + notFull.await(); + notEmpty.signal(); + } finally { + lock.unlock(); + } + } + + @Override + public E removeFirst() { + lock.lock(); + try { + E e; + if ((e = deque.removeFirst()) != null) + notFull.signal(); + return e; + } finally { + lock.unlock(); + } + } + + @Override + public E removeLast() { + lock.lock(); + try { + E e; + if ((e = deque.removeLast()) != null) + notFull.signal(); + return e; + } finally { + lock.unlock(); + } + } + + /** + * Retrieves and removes the first element of this deque, waiting + * if necessary until an element becomes available. + * + * @return the head of this deque + * @throws InterruptedException if interrupted while waiting + */ + public E takeFirst() throws InterruptedException { + lock.lock(); + try { + while (deque.size() == 0) + notEmpty.await(); + E x = deque.removeFirst(); + notFull.signal(); + return x; + } finally { + lock.unlock(); + } + } + + /** + * Retrieves and removes the last element of this deque, waiting + * if necessary until an element becomes available. + * + * @return the tail of this deque + * @throws InterruptedException if interrupted while waiting + */ + public E takeLast() throws InterruptedException { + lock.lock(); + try { + while (deque.size() == 0) + notEmpty.await(); + E x = deque.removeLast(); + notFull.signal(); + return x; + } finally { + lock.unlock(); + } + } + + @Override + public E pollFirst() { + lock.lock(); + try { + E e; + if ((e = deque.pollFirst()) != null) + notFull.signal(); + return e; + } finally { + lock.unlock(); + } + } + + @Override + public E pollLast() { + lock.lock(); + try { + E e; + if ((e = deque.pollLast()) != null) + notFull.signal(); + return e; + } finally { + lock.unlock(); + } + } + + @Override + public E getFirst() { + lock.lock(); + try { + return deque.getFirst(); + } finally { + lock.unlock(); + } + } + + @Override + public E getLast() { + lock.lock(); + try { + return deque.getLast(); + } finally { + lock.unlock(); + } + } + + @Override + public E peekFirst() { + lock.lock(); + try { + return deque.peekFirst(); + } finally { + lock.unlock(); + } + } + + @Override + public E peekLast() { + lock.lock(); + try { + return deque.peekLast(); + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeFirstOccurrence(final Object o) { + lock.lock(); + try { + boolean r; + if (r = deque.removeFirstOccurrence(o)) + notEmpty.signal(); + return r; + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeLastOccurrence(final Object o) { + lock.lock(); + try { + boolean r; + if (r = deque.removeLastOccurrence(o)) + notEmpty.signal(); + return r; + } finally { + lock.unlock(); + } + } + + @Override + public boolean add(final E e) { + addLast(e); + return true; + } + + @Override + public boolean offer(final E e) { + return offerLast(e); + } + + @Override + public E remove() { + return removeFirst(); + } + + @Override + public E poll() { + return pollFirst(); + } + + @Override + public E element() { + return getFirst(); + } + + @Override + public E peek() { + return peekFirst(); + } + + @Override + public void push(final E e) { + addFirst(e); + } + + @Override + public E pop() { + return removeFirst(); + } + + @Override + public boolean remove(final Object o) { + return removeFirstOccurrence(o); + } + + @Override + public boolean containsAll(final Collection c) { + lock.lock(); + try { + return deque.containsAll(c); + } finally { + lock.unlock(); + } + } + + @Override + public boolean addAll(final Collection c) { + lock.lock(); + try { + boolean r; + if (r = deque.addAll(c)) + notEmpty.signalAll(); + return r; + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeAll(final Collection c) { + lock.lock(); + try { + boolean r; + if (r = deque.removeAll(c)) + notFull.signalAll(); + return r; + } finally { + lock.unlock(); + } + } + + @Override + public boolean retainAll(final Collection c) { + lock.lock(); + try { + boolean r; + r = deque.retainAll(c); + if (r == true) { + notFull.signalAll(); + } + return r; + } finally { + lock.unlock(); + } + } + + @Override + public void clear() { + lock.lock(); + try { + deque.clear(); + notFull.signalAll(); + } finally { + lock.unlock(); + } + } + + @Override + public boolean contains(final Object o) { + lock.lock(); + try { + return deque.contains(o); + } finally { + lock.unlock(); + } + } + + @Override + public int size() { + lock.lock(); + try { + return deque.size(); + } finally { + lock.unlock(); + } + } + + @Override + public boolean isEmpty() { + lock.lock(); + try { + return deque.isEmpty(); + } finally { + lock.unlock(); + } + } + + @Override + public Iterator iterator() { + return deque.iterator(); + } + + @Override + public Object[] toArray() { + lock.lock(); + try { + return deque.toArray(); + } finally { + lock.unlock(); + } + } + + @Override + public T[] toArray(final T[] a) { + lock.lock(); + try { + return deque.toArray(a); + } finally { + lock.unlock(); + } + } + + @Override + public Iterator descendingIterator() { + return deque.descendingIterator(); + } +} diff --git a/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java b/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java new file mode 100644 index 0000000000..702c0ae45c --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/deque/CircularDeque.java @@ -0,0 +1,636 @@ +/* + * 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.collections4.deque; + + +import org.apache.commons.collections4.BoundedCollection; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.Collection; + +import java.util.Deque; +import java.util.NoSuchElementException; +import java.util.List; + +import java.util.ConcurrentModificationException; +import java.util.Objects; +import java.util.Iterator; +/** + * CircularDeque is a deque with a fixed size that replaces its oldest + * element if full. + *

+ * The removal order of a {@link CircularDeque} 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. + *

+ *

+ * The {@link #add(Object)}, {@link #remove()}, {@link #peek()}, {@link #poll}, + * {@link #offer(Object)} operations all perform in constant time. + * All other operations perform in linear time or worse. + *

+ *

+ * This deque prevents null objects from being added. + *

+ * + * @param the type of elements in this collection + * @since 4.5 + */ +public class CircularDeque extends AbstractCollection + implements Deque, BoundedCollection, Serializable { + + /** Serialization version */ + private static final long serialVersionUID = 6013343683263686185L; + + /** Underlying storage array. */ + private transient E[] elements; + + /** Array index of first (oldest) deque element. */ + private transient int head = 0; + + /** + * Index mod maxElements of the array position following the last deque + * element. Deque elements start at elements[head] and "wrap around" + * elements[maxElements-1], ending at elements[decrement(tail)]. + * For example, elements = {c,a,b}, start=1, end=1 corresponds to + * the deque [a,b,c]. + */ + private transient int tail = 0; + + /** Flag to indicate if the deque is currently full. */ + private transient boolean full = false; + + /** Capacity of the deque. */ + private final int maxElements; + + /** + * Constructor that creates a deque with the default size of 32. + */ + public CircularDeque() { + this(32); + } + + /** + * Constructor that creates a deque with the specified size. + * + * @param size the size of the deque (cannot be changed) + * @throws IllegalArgumentException if the size is < 1 + */ + @SuppressWarnings("unchecked") + public CircularDeque(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 deque from the specified collection. + * The collection size also sets the deque size. + * + * @param coll the collection to copy into the deque, may not be null + * @throws NullPointerException if the collection is null + */ + public CircularDeque(final Collection coll) { + if (coll == null) { + throw new NullPointerException("Collection must not be null"); + } + + elements = (E[]) new Object[coll.size()]; + maxElements = elements.length; + + addAll(coll); + } + + /** + * Write the deque 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 deque 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(); + } + head = 0; + full = size == maxElements; + if (full) { + tail = 0; + } else { + tail = size; + } + } + + /** + * Returns true if this deque is empty; false otherwise. + * + * @return true if this deque is empty + */ + public boolean isEmpty() { + return size() == 0; + } + + /** + * Returns {@code true} if the capacity limit of this deque has been reached, + * i.e. the number of elements stored in the deque equals its maximum size. + * + * @return {@code true} if the capacity limit has been reached, {@code false} otherwise + * @since 4.5 + */ + public boolean isAtFullCapacity() { + return size() == maxElements; + } + + /** + * Increase the internal index. + * + * @param index the index to increment + * @return the updated index + */ + private int increaseIndex(int index) { + index++; + if (index >= maxElements) { + index = 0; + } + return index; + } + + /** + * Decrease the internal index. + * + * @param index the index to decrement + * @return the updated index + */ + private int decreaseIndex(int index) { + index--; + if (index < 0) { + index = maxElements - 1; + } + return index; + } + + /** + * Adds the given element before the head of this deque. If the deque is full, + * the tail element will be overridden so that a new element can be inserted. + * + * @param e the element to add + */ + @Override + public void addFirst(final E e) { + if (isAtFullCapacity()) { + remove(); + } + + if (head == 0) { + head = maxElements - 1; + } else { + head--; + } + + elements[head] = e; + + if (tail == head) { + full = true; + } + } + + /** + * Adds the given element at the tail of this deque. If the deque is full, + * the head element will be overridden so that a new element can be inserted. + * + * @param e the element to add + */ + @Override + public void addLast(final E e) { + if (isAtFullCapacity()) { + removeFirst(); + } + + elements[tail++] = e; + + if (tail >= maxElements) { + tail = 0; + } + + if (tail == head) { + full = true; + } + } + + @Override + public boolean offerFirst(final E e) { + addFirst(e); + return true; + } + + @Override + public boolean offerLast(final E e) { + addLast(e); + return true; + } + + @Override + public E removeFirst() { + if (isEmpty()) { + throw new NoSuchElementException("queue is empty"); + } + + final E element = elements[head]; + + elements[head++] = null; + + if (head >= maxElements) { + head = 0; + } + full = false; + + return element; + } + + @Override + public E removeLast() { + if (isEmpty()) { + throw new NoSuchElementException("queue is empty"); + } + + final E element = elements[tail]; + elements[tail--] = null; + + if (tail < 0) { + tail = maxElements - 1; + } + full = false; + + return element; + } + + @Override + public E pollFirst() { + if (isEmpty()) { + return null; + } + return removeFirst(); + } + + @Override + public E pollLast() { + if (isEmpty()) { + return null; + } + return removeLast(); + } + + @Override + public E getFirst() { + if (isEmpty()) { + throw new NoSuchElementException("deque is empty"); + } + return elements[head]; + } + + @Override + public E getLast() { + if (isEmpty()) { + throw new NoSuchElementException("deque is empty"); + } + + return elements[decreaseIndex(tail)]; + } + + @Override + public E peekFirst() { + if (isEmpty()) { + return null; + } + return elements[head]; + } + + @Override + public E peekLast() { + if (isEmpty()) { + return null; + } + + return elements[decreaseIndex(tail)]; + } + + @Override + public boolean removeFirstOccurrence(final Object o) { + int i = head; + while (true) { + if (Objects.equals(o,elements[i])) { + delete(i); + return true; + } + if (i == decreaseIndex(tail)) { + return false; + } + i = increaseIndex(i); + } + } + + @Override + public boolean removeLastOccurrence(final Object o) { + int i = decreaseIndex(tail); + while (true) { + if (Objects.equals(o,elements[i])) { + delete(i); + return true; + } + if (i == head) { + return false; + } + i = decreaseIndex(i); + } + } + + @Override + public boolean add(final E e) { + if (isAtFullCapacity()) { + remove(); + } + + elements[tail++] = e; + + if (tail >= maxElements) { + tail = 0; + } + + if (tail == head) { + full = true; + } + + return true; + } + + @Override + public boolean offer(final E e) { + return offerLast(e); + } + + @Override + public E remove() { + return removeFirst(); + } + + @Override + public E poll() { + return pollFirst(); + } + + @Override + public E element() { + return getFirst(); + } + + @Override + public E peek() { + return peekFirst(); + } + + @Override + public void push(final E e) { + addFirst(e); + } + + @Override + public E pop() { + return removeFirst(); + } + + + /** + * Removes the element at the specified position in the elements array, + * adjusting head and tail as necessary. This can result in motion of + * elements backwards or forwards in the array. + * + *

This method is called delete rather than remove to emphasize + * that its semantics differ from those of {@link List#remove(int)}. + * + * @return true if elements moved backwards + */ + private boolean delete(final int i) { + if (i == head) { + elements[i] = null; + head = increaseIndex(head); + full = false; + return true; + } + + if (i == decreaseIndex(tail)) { + elements[i] = null; + tail = decreaseIndex(tail); + full = false; + return true; + } + + final int front = i - head; + final int back = tail - i - 1; + + if (head < tail) { + if (front < back) { + System.arraycopy(elements, head, elements, head + 1, front); + head++; + } else { + System.arraycopy(elements, i + 1, elements, i, back); + tail--; + } + } else if (head > tail) { + if (i > head) { + System.arraycopy(elements, head, elements, head + 1, front); + head++; + } else if (i < tail) { + System.arraycopy(elements, i + 1, elements, i, back); + tail--; + } else { + throw new ConcurrentModificationException(); + } + } + full = false; + return true; + } + + /** + * Returns the number of elements stored in the deque. + * + * @return this deque's size + */ + @Override + public int size() { + int size = 0; + + if (tail < head) { + size = maxElements - head + tail; + } else if (tail == head) { + size = full ? maxElements : 0; + } else { + size = tail - head; + } + + return size; + } + + /** + * {@inheritDoc} + *

+ * A {@code CircularDeque} can never be full, thus this returns always + * {@code false}. + * + * @return always returns {@code false} + */ + @Override + public boolean isFull() { + return false; + } + + @Override + public int maxSize() { + return maxElements; + } + + @Override + public Iterator iterator() { + return new Iterator() { + + private int index = head; + private int lastReturnedIndex = -1; + private boolean isFirst = full; + + @Override + public boolean hasNext() { + return isFirst || index != tail; + } + + @Override + public E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + isFirst = false; + lastReturnedIndex = index; + index = increaseIndex(index); + return elements[lastReturnedIndex]; + } + + @Override + public void remove() { + if (lastReturnedIndex == -1) { + throw new IllegalStateException(); + } + + // First element can be removed quickly + if (lastReturnedIndex == head) { + CircularDeque.this.remove(); + lastReturnedIndex = -1; + return; + } + + int pos = lastReturnedIndex + 1; + if (head < lastReturnedIndex && pos < tail) { + // shift in one part + System.arraycopy(elements, pos, elements, lastReturnedIndex, tail - pos); + } else { + // Other elements require us to shift the subsequent elements + while (pos != tail) { + if (pos >= maxElements) { + elements[pos - 1] = elements[0]; + pos = 0; + } else { + elements[decreaseIndex(pos)] = elements[pos]; + pos = increaseIndex(pos); + } + } + } + + lastReturnedIndex = -1; + tail = decreaseIndex(tail); + elements[tail] = null; + full = false; + index = decreaseIndex(index); + } + + }; + } + + + @Override + public Iterator descendingIterator() { + return new DescendingIterator(); + } + + private class DescendingIterator implements Iterator { + /* + * This class is nearly a mirror-image of DeqIterator, using + * tail instead of head for initial cursor, and head instead of + * tail for fence. + */ + private int cursor = tail; + private int fence = head; + private int lastRet = -1; + private boolean isFull = full; + + public boolean hasNext() { + return isFull || cursor != fence; + } + + public E next() { + if (!hasNext()) + throw new NoSuchElementException(); + cursor = decreaseIndex(cursor); + @SuppressWarnings("unchecked") + E result = (E) elements[cursor]; + if (head != fence ) + throw new ConcurrentModificationException(); + lastRet = cursor; + return result; + } + + public void remove() { + if (lastRet < 0) + throw new IllegalStateException(); + if (!delete(lastRet)) { + cursor = increaseIndex(cursor); + fence = head; + } + lastRet = -1; + isFull = false; + } + } +} diff --git a/src/main/java/org/apache/commons/collections4/deque/PredicatedDeque.java b/src/main/java/org/apache/commons/collections4/deque/PredicatedDeque.java new file mode 100644 index 0000000000..a02c5005e7 --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/deque/PredicatedDeque.java @@ -0,0 +1,217 @@ +/* + * 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.collections4.deque; + + +import org.apache.commons.collections4.Predicate; +import org.apache.commons.collections4.collection.PredicatedCollection; + +import java.util.Deque; +import java.util.Iterator; + +/** + * Decorates another {@link Deque} to validate that additions + * match a specified predicate. + *

+ * This deque exists to provide validation for the decorated deque. + * It is normally created to decorate an empty deque. + * If an object cannot be added to the deque, an IllegalArgumentException is thrown. + *

+ *

+ * One usage would be to ensure that no null entries are added to the deque. + *

+ *
Deque deque = PredicatedDeque.predicatedDeque(new UnboundedFifoDeque(), NotNullPredicate.INSTANCE);
+ * + * @param the type of elements held in this deque + * @since 4.5 + */ +public class PredicatedDeque extends PredicatedCollection implements Deque { + + /** Serialization version */ + private static final long serialVersionUID = 7027705675493389763L; + + /** + * Factory method to create a predicated (validating) deque. + *

+ * If there are any elements already in the deque being decorated, they + * are validated. + * + * @param the type of the elements in the deque + * @param deque the deque to decorate, must not be null + * @param predicate the predicate to use for validation, must not be null + * @return a new predicated deque + * @throws NullPointerException if deque or predicate is null + * @throws IllegalArgumentException if the deque contains invalid elements + */ + public static PredicatedDeque predicatedDeque(final Deque deque, + final Predicate predicate) { + return new PredicatedDeque<>(deque, predicate); + } + + + + /** + * Constructor that wraps (not copies). + *

+ * If there are any elements already in the collection being decorated, they + * are validated. + * + * @param deque the collection to decorate, must not be null + * @param predicate the predicate to use for validation, must not be null + * @throws NullPointerException if collection or predicate is null + * @throws IllegalArgumentException if the collection contains invalid elements + */ + protected PredicatedDeque(Deque deque, Predicate predicate) { + super(deque, predicate); + } + + /** + * Gets the deque being decorated. + * + * @return the decorated deque + */ + @Override + protected Deque decorated() { + return (Deque) super.decorated(); + } + + /** + * Override to validate the object being added to ensure it matches + * the predicate. + * + * @param e the object being added + * @throws IllegalArgumentException if the add is invalid + */ + + @Override + public void addFirst(final E e) { + validate(e); + decorated().addFirst(e); + } + + @Override + public void addLast(final E e) { + validate(e); + decorated().addLast(e); + } + + @Override + public boolean offerFirst(final E e) { + validate(e); + return decorated().offerFirst(e); + } + + @Override + public boolean offerLast(final E e) { + validate(e); + return decorated().offerLast(e); + } + + @Override + public E removeFirst() { + return decorated().removeFirst(); + } + + @Override + public E removeLast() { + return decorated().removeLast(); + } + + @Override + public E pollFirst() { + return decorated().pollFirst(); + } + + @Override + public E pollLast() { + return decorated().pollLast(); + } + + @Override + public E getFirst() { + return decorated().getFirst(); + } + + @Override + public E getLast() { + return decorated().getLast(); + } + + @Override + public E peekFirst() { + return decorated().peekFirst(); + } + + @Override + public E peekLast() { + return decorated().peekLast(); + } + + @Override + public boolean removeFirstOccurrence(final Object o) { + validate((E) o); + return decorated().removeFirstOccurrence(o); + } + + @Override + public boolean removeLastOccurrence(final Object o) { + validate((E) o); + return decorated().removeLastOccurrence(o); + } + + @Override + public boolean offer(final E e) { + validate(e); + return decorated().offer(e); + } + + @Override + public E remove() { + return decorated().remove(); + } + + @Override + public E poll() { + return decorated().poll(); + } + + @Override + public E element() { + return decorated().element(); + } + + @Override + public E peek() { + return decorated().peek(); + } + + @Override + public void push(final E e) { + validate(e); + decorated().push(e); + } + + @Override + public E pop() { + return decorated().pop(); + } + + @Override + public Iterator descendingIterator() { + return decorated().descendingIterator(); + } +} diff --git a/src/main/java/org/apache/commons/collections4/deque/SynchronizedDeque.java b/src/main/java/org/apache/commons/collections4/deque/SynchronizedDeque.java new file mode 100644 index 0000000000..e750f4cd82 --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/deque/SynchronizedDeque.java @@ -0,0 +1,261 @@ +/* + * 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.collections4.deque; + +import org.apache.commons.collections4.collection.SynchronizedCollection; +import java.util.Deque; +import java.util.Iterator; + + +/** + * Decorates another {@link Deque} to synchronize its behaviour for a multi-threaded environment. + *

+ * Methods are synchronized, then forwarded to the decorated deque. Iterators must be separately synchronized around the + * loop. + *

+ * + * @param the type of the elements in the collection + * @since 4.5 + */ +public class SynchronizedDeque extends SynchronizedCollection implements Deque { + + /** Serialization version */ + private static final long serialVersionUID = 4565577797338001569L; + + /** + * Factory method to create a synchronized deque. + * + * @param + * the type of the elements in the deque + * @param deque + * the deque to decorate, must not be null + * @return a new synchronized Deque + * @throws NullPointerException + * if deque is null + */ + public static SynchronizedDeque synchronizedDeque(final Deque deque) { + return new SynchronizedDeque<>(deque); + } + + /** + * Constructor that wraps (not copies). + * + * @param deque + * the deque to decorate, must not be null + * @throws NullPointerException + * if deque is null + */ + protected SynchronizedDeque(final Deque deque) { + super(deque); + } + + /** + * Constructor that wraps (not copies). + * + * @param deque + * the deque to decorate, must not be null + * @param lock + * the lock to use, must not be null + * @throws NullPointerException + * if deque or lock is null + */ + protected SynchronizedDeque(final Deque deque, final Object lock) { + super(deque, lock); + } + + /** + * Gets the deque being decorated. + * + * @return the decorated deque + */ + @Override + protected Deque decorated() { + return (Deque) super.decorated(); + } + + @Override + public E element() { + synchronized (lock) { + return decorated().element(); + } + } + + @Override + public boolean equals(final Object object) { + if (object == this) { + return true; + } + synchronized (lock) { + return decorated().equals(object); + } + } + + @Override + public int hashCode() { + synchronized (lock) { + return decorated().hashCode(); + } + } + + @Override + public void addFirst(E e) { + synchronized (lock) { + decorated().addFirst(e); + } + } + + @Override + public void addLast(E e) { + synchronized (lock) { + decorated().addLast(e); + } + } + + @Override + public boolean offerFirst(E e) { + synchronized (lock) { + return decorated().offerFirst(e); + } + } + + @Override + public boolean offerLast(E e) { + synchronized (lock) { + return decorated().offerLast(e); + } + } + + @Override + public E removeFirst() { + synchronized (lock) { + return decorated().removeFirst(); + } + } + + @Override + public E removeLast() { + synchronized (lock) { + return decorated().removeLast(); + } + } + + @Override + public E pollFirst() { + synchronized (lock) { + return decorated().pollFirst(); + } + } + + @Override + public E pollLast() { + synchronized (lock) { + return decorated().pollLast(); + } + } + + @Override + public E getFirst() { + synchronized (lock) { + return decorated().getFirst(); + } + } + + @Override + public E getLast() { + synchronized (lock) { + return decorated().getLast(); + } + } + + @Override + public E peekFirst() { + synchronized (lock) { + return decorated().peekFirst(); + } + } + + @Override + public E peekLast() { + synchronized (lock) { + return decorated().peekLast(); + } + } + + @Override + public boolean removeFirstOccurrence(Object o) { + synchronized (lock) { + return decorated().removeFirstOccurrence(o); + } + } + + @Override + public boolean removeLastOccurrence(Object o) { + synchronized (lock) { + return decorated().removeLastOccurrence(o); + } + } + + @Override + public boolean offer(final E e) { + synchronized (lock) { + return decorated().offer(e); + } + } + + @Override + public E peek() { + synchronized (lock) { + return decorated().peek(); + } + } + + @Override + public void push(E e) { + synchronized (lock) { + decorated().push(e); + } + } + + @Override + public E pop() { + synchronized (lock) { + return decorated().pop(); + } + } + + @Override + public Iterator descendingIterator() { + synchronized (lock) { + return decorated().descendingIterator(); + } + } + + @Override + public E poll() { + synchronized (lock) { + return decorated().poll(); + } + } + + @Override + public E remove() { + synchronized (lock) { + return decorated().remove(); + } + } + +} diff --git a/src/main/java/org/apache/commons/collections4/deque/TransformedDeque.java b/src/main/java/org/apache/commons/collections4/deque/TransformedDeque.java new file mode 100644 index 0000000000..dd480ebe5b --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/deque/TransformedDeque.java @@ -0,0 +1,223 @@ +/* + * 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.collections4.deque; + +import org.apache.commons.collections4.Transformer; +import org.apache.commons.collections4.collection.TransformedCollection; + +import java.util.Collection; +import java.util.Deque; +import java.util.Iterator; + +/** + * Decorates another {@link Deque} to transform objects that are added. + *

+ * The add/offer methods are affected by this class. + * Thus objects must be removed or searched for using their transformed form. + * For example, if the transformation converts Strings to Integers, you must + * use the Integer form to remove objects. + *

+ * + * @param the type of elements held in this deque + * @since 4.5 + */ + +public class TransformedDeque extends TransformedCollection implements Deque { + + /** Serialization version */ + private static final long serialVersionUID = 7959728067506831816L; + + /** + * Factory method to create a transforming deque. + *

+ * If there are any elements already in the deque being decorated, they + * are NOT transformed. + * Contrast this with {@link #transformedDeque(Deque, Transformer)}. + * + * @param the type of the elements in the deque + * @param deque the deque to decorate, must not be null + * @param transformer the transformer to use for conversion, must not be null + * @return a new transformed Deque + * @throws NullPointerException if deque or transformer is null + */ + public static TransformedDeque transformingDeque(final Deque deque, + final Transformer transformer) { + return new TransformedDeque<>(deque, transformer); + } + + /** + * Factory method to create a transformed deque that will transform + * existing contents of the specified deque. + *

+ * If there are any elements already in the deque being decorated, they + * will be transformed by this method. + * Contrast this with {@link #transformingDeque(Deque, Transformer)}. + * + * @param the type of the elements in the deque + * @param deque the deque to decorate, must not be null + * @param transformer the transformer to use for conversion, must not be null + * @return a new transformed Deque + * @throws NullPointerException if deque or transformer is null + * @since 4.0 + */ + public static TransformedDeque transformedDeque(final Deque deque, + final Transformer transformer) { + // throws NullPointerException if deque or transformer is null + final TransformedDeque decorated = new TransformedDeque<>(deque, transformer); + if (deque.size() > 0) { + @SuppressWarnings("unchecked") // deque is type + final E[] values = (E[]) deque.toArray(); // NOPMD - false positive for generics + deque.clear(); + for (final E value : values) { + decorated.decorated().add(transformer.transform(value)); + } + } + return decorated; + } + /** + * Constructor that wraps (not copies). + *

+ * If there are any elements already in the deque being decorated, they + * are NOT transformed. + * + * @param deque the deque to decorate, must not be null + * @param transformer the transformer to use for conversion, must not be null + * @throws NullPointerException if deque or transformer is null + */ + protected TransformedDeque(final Deque deque, final Transformer transformer) { + super(deque, transformer); + } + + /** + * Gets the decorated deque. + * + * @return the decorated deque + */ + protected Deque getDeque() { + return (Deque) decorated(); + } + + @Override + public void addFirst(E e) { + getDeque().addFirst(transform(e)); + } + + @Override + public void addLast(E e) { + getDeque().addLast(transform(e)); + } + + @Override + public boolean offerFirst(E e) { + return getDeque().offerFirst(transform(e)); + } + + @Override + public boolean offerLast(E e) { + return getDeque().offerLast(transform(e)); + } + + @Override + public E removeFirst() { + return getDeque().removeFirst(); + } + + @Override + public E removeLast() { + return getDeque().removeLast(); + } + + @Override + public E pollFirst() { + return getDeque().pollFirst(); + } + + @Override + public E pollLast() { + return getDeque().pollLast(); + } + + @Override + public E getFirst() { + return getDeque().getFirst(); + } + + @Override + public E getLast() { + return getDeque().getLast(); + } + + @Override + public E peekFirst() { + return getDeque().peekFirst(); + } + + @Override + public E peekLast() { + return getDeque().peekLast(); + } + + @Override + public boolean removeFirstOccurrence(Object o) { + return getDeque().removeFirstOccurrence(transform((E) o)); + } + + @Override + public boolean removeLastOccurrence(Object o) { + return getDeque().removeLastOccurrence(transform((E) o)); + } + + @Override + public boolean offer(E e) { + return getDeque().offer(transform(e)); + } + + @Override + public E remove() { + return getDeque().remove(); + } + + @Override + public E poll() { + return getDeque().poll(); + } + + @Override + public E element() { + return getDeque().element(); + } + + @Override + public E peek() { + return getDeque().peek(); + } + + @Override + public void push(E e) { + getDeque().push(transform(e)); + } + + @Override + public E pop() { + return getDeque().pop(); + } + + @Override + public Iterator descendingIterator() { + return getDeque().descendingIterator(); + } +} diff --git a/src/main/java/org/apache/commons/collections4/deque/package-info.java b/src/main/java/org/apache/commons/collections4/deque/package-info.java new file mode 100644 index 0000000000..99540598e1 --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/deque/package-info.java @@ -0,0 +1,34 @@ +/* + * 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. + */ +/** + * This package contains implementations for the {@link java.util.Deque Deque} interface. + *

+ * The following implementations are provided in the package: + *

    + *
  • CircularDeque - implements a Deque with a fixed size that discards oldest when full + *
+ *

+ * The following decorators are provided in the package: + *

    + *
  • Predicated - ensures that only elements that are valid according to a predicate can be added + *
  • Transformed - transforms elements added to the queue + *
  • Blocking - ensures the collection is thread-safe and can be operated concurrently + *
  • Synchronized - ensures the collection is thread-safe + *
+ * + */ +package org.apache.commons.collections4.deque; \ No newline at end of file diff --git a/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java new file mode 100644 index 0000000000..a5c62c2454 --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/deque/AbstractDequeTest.java @@ -0,0 +1,807 @@ +/* + * 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.collections4.deque; + +import org.apache.commons.collections4.collection.AbstractCollectionTest; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Arrays; + +import java.util.Collection; +import java.util.Iterator; + +import java.util.LinkedList; +import java.util.Deque; +import java.util.NoSuchElementException; + +/** + * Abstract test class for {@link java.util.Deque} methods and contracts. + *

+ * To use, simply extend this class, and implement + * the {@link #makeObject} method. + *

+ * If your {@link Deque} fails one of these tests by design, + * you may still use this base set of cases. Simply override the + * test case (method) your {@link Deque} fails or override one of the + * protected methods from AbstractCollectionTest. + * + * @since 4.5 + */ +public abstract class AbstractDequeTest extends AbstractCollectionTest { + /** + * JUnit constructor. + * + * @param testName the test class name + */ + public AbstractDequeTest(final String testName) { + super(testName); + } + + /** + * Returns true if the collections produced by + * {@link #makeObject()} and {@link #makeFullCollection()} + * support the set operation.

+ * Default implementation returns true. Override if your collection + * class does not support set. + */ + public boolean isSetSupported() { + return true; + } + + + /** + * Verifies that the test deque implementation matches the confirmed deque + * implementation. + */ + @Override + public void verify() { + super.verify(); + final Iterator iterator1 = getCollection().iterator(); + for (final E e : getConfirmed()) { + assertTrue(iterator1.hasNext()); + final Object o1 = iterator1.next(); + final Object o2 = e; + assertEquals(o1, o2); + } + } + + /** + * Returns an empty {@link LinkedList}. + */ + @Override + public Collection makeConfirmedCollection() { + final LinkedList list = new LinkedList<>(); + return list; + } + + /** + * Returns a full {@link LinkedList}. + */ + @Override + public Collection makeConfirmedFullCollection() { + final LinkedList list = new LinkedList<>(); + list.addAll(Arrays.asList(getFullElements())); + return list; + } + + /** + * Returns {@link #makeObject()}. + * + * @return an empty deque to be used for testing + */ + @Override + public abstract Deque makeObject(); + + /** + * {@inheritDoc} + */ + @Override + public Deque makeFullCollection() { + // only works if Deque supports optional "addAll(Collection)" + final Deque deque = makeObject(); + deque.addAll(Arrays.asList(getFullElements())); + return deque; + } + + /** + * Returns the {@link #collection} field cast to a {@link Deque}. + * + * @return the collection field as a Deque + */ + @Override + public Deque getCollection() { + return (Deque) super.getCollection(); + } + + /** + * Tests {@link Deque#addFirst(E e)}. + */ + public void testDequeAddFirst() { + if (!isAddSupported()) { + return; + } + + final E[] elements = getFullElements(); + resetEmpty(); + int size = 0; + + for (final E element : elements) { + getCollection().addFirst(element); + getConfirmed().add(element); + size++; + assertEquals("Deque size should grow after add", size, getCollection().size()); + assertTrue("Deque should contain added element", getCollection().contains(element)); + } + final Deque deque = makeObject(); + for (int i = 0; i < size; i++) { + deque.push(getCollection().pop()); + } + setCollection(deque); + verify(); + } + + /** + * Tests {@link Deque#addLast(E e)}. + */ + public void testDequeAddLast() { + if (!isAddSupported()) { + return; + } + + resetEmpty(); + int size = 0; + final E[] elements = getFullElements(); + for (final E element : elements) { + getCollection().addLast(element); + getConfirmed().add(element); + verify(); + size++; + assertEquals("Deque size should grow after add", size, getCollection().size()); + assertTrue("Deque should contain added element", getCollection().contains(element)); + } + } + + /** + * Tests {@link Deque#offerFirst(E e)}. + */ + public void testDequeOfferFirst() { + if (!isAddSupported()) { + return; + } + + resetEmpty(); + final E[] elements = getFullElements(); + int size = 0; + for (final E element : elements) { + final boolean r = getCollection().offerFirst(element); + getConfirmed().add(element); + if (r) { + size++; + } + assertEquals("Deque size should grow after add", size, getCollection().size()); + assertTrue("Deque should contain added element", getCollection().contains(element)); + } + final Deque deque = makeObject(); + for (int i = 0; i < size; i++) { + deque.push(getCollection().pop()); + } + setCollection(deque); + verify(); + } + + /** + * Tests {@link Deque#offerLast(E e)}. + */ + public void testDequeOfferLast() { + if (!isAddSupported()) { + return; + } + + resetEmpty(); + int size = 0; + final E[] elements = getFullElements(); + for (final E element : elements) { + final boolean r = getCollection().offerLast(element); + getConfirmed().add(element); + verify(); + if (r) { + size++; + } + assertEquals("Deque size should grow after add", size, getCollection().size()); + assertTrue("Deque should contain added element", getCollection().contains(element)); + } + } + + /** + * Tests {@link Deque#removeFirst()}. + */ + public void testDequeRemoveFirst() { + if (!isRemoveSupported()) { + return; + } + + resetEmpty(); + + try { + getCollection().removeFirst(); + fail("Deque.removeFirst should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + + resetFull(); + + final int max = getFullElements().length; + for (int i = 0; i < max; i++) { + final E element = getCollection().removeFirst(); + final boolean success = getConfirmed().remove(element); + assertTrue("removeFirst should return correct element", success); + verify(); + } + + try { + getCollection().element(); + fail("Deque.removeFirst should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + } + + /** + * Tests {@link Deque#removeLast()}. + */ + public void testDequeRemoveLast() { + if (!isRemoveSupported()) { + return; + } + + resetEmpty(); + + try { + getCollection().removeLast(); + fail("Deque.remove should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + + resetFull(); + + final int max = getFullElements().length; + for (int i = 0; i < max; i++) { + final E element = getCollection().removeLast(); + LinkedList list = (LinkedList) getConfirmed(); + list.removeLast(); + verify(); + } + + try { + getCollection().element(); + fail("Deque.remove should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + } + + /** + * Tests {@link Deque#pollFirst()}. + */ + public void testDequePollFirst() { + if (!isRemoveSupported()) { + return; + } + + resetEmpty(); + + E element = getCollection().pollFirst(); + assertNull(element); + + resetFull(); + + final int max = getFullElements().length; + for (int i = 0; i < max; i++) { + element = getCollection().pollFirst(); + final boolean success = getConfirmed().remove(element); + assertTrue("poll should return correct element", success); + verify(); + } + + element = getCollection().pollFirst(); + assertNull(element); + } + + /** + * Tests {@link Deque#pollLast()}. + */ + public void testDequePollLast() { + if (!isRemoveSupported()) { + return; + } + + resetEmpty(); + + E element = getCollection().pollLast(); + assertNull(element); + + resetFull(); + + final int max = getFullElements().length; + for (int i = 0; i < max; i++) { + getCollection().pollLast(); + LinkedList list = (LinkedList) getConfirmed(); + list.removeLast(); + verify(); + } + + element = getCollection().pollLast(); + assertNull(element); + } + + /** + * Tests {@link Deque#getFirst()}. + */ + public void testDequeGetFirst() { + resetEmpty(); + + try { + getCollection().getFirst(); + fail("Deque.element should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + + resetFull(); + + assertTrue(getConfirmed().contains(getCollection().getFirst())); + + if (!isRemoveSupported()) { + return; + } + + final int max = getFullElements().length; + for (int i = 0; i < max; i++) { + final E element = getCollection().getFirst(); + + if (!isNullSupported()) { + assertNotNull(element); + } + + assertTrue(getConfirmed().contains(element)); + + getCollection().remove(element); + getConfirmed().remove(element); + + verify(); + } + + try { + getCollection().getFirst(); + fail("Deque.element should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + } + + /** + * Tests {@link Deque#getLast()}. + */ + public void testDequeGetLast() { + resetEmpty(); + + try { + getCollection().getLast(); + fail("Deque.element should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + + resetFull(); + + if (!isRemoveSupported()) { + return; + } + + final int max = getFullElements().length; + for (int i = 0; i < max; i++) { + final E element = getCollection().getLast(); + + if (!isNullSupported()) { + assertNotNull(element); + } + assertTrue(getConfirmed().contains(element)); + getCollection().remove(element); + getConfirmed().remove(element); + verify(); + } + + try { + getCollection().getLast(); + fail("Deque.element should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + } + + /** + * Tests {@link Deque#peekFirst()}. + */ + public void testDequePeekFirst() { + if (!isRemoveSupported()) { + return; + } + + resetEmpty(); + + E element = getCollection().peekFirst(); + assertNull(element); + + resetFull(); + + final int max = getFullElements().length; + for (int i = 0; i < max; i++) { + element = getCollection().peekFirst(); + + if (!isNullSupported()) { + assertNotNull(element); + } + + assertTrue(getConfirmed().contains(element)); + + getCollection().remove(element); + getConfirmed().remove(element); + + verify(); + } + + element = getCollection().peekFirst(); + assertNull(element); + } + + /** + * Tests {@link Deque#peekLast()}. + */ + public void testDequePeekLast() { + if (!isRemoveSupported()) { + return; + } + + resetEmpty(); + + E element = getCollection().peekLast(); + assertNull(element); + + resetFull(); + + final int max = getFullElements().length; + for (int i = 0; i < max; i++) { + element = getCollection().peekLast(); + + if (!isNullSupported()) { + assertNotNull(element); + } + + assertTrue(getConfirmed().contains(element)); + + getCollection().remove(element); + getConfirmed().remove(element); + + verify(); + } + + element = getCollection().peekLast(); + assertNull(element); + } + /** + * Tests {@link Deque#offer(Object)}. + */ + public void testDequeOffer() { + if (!isAddSupported()) { + return; + } + + resetEmpty(); + int size = 0; + final E[] elements = getFullElements(); + for (final E element : elements) { + final boolean r = getCollection().offer(element); + getConfirmed().add(element); + verify(); + if (r) { + size++; + } + assertEquals("Deque size should grow after add", size, getCollection().size()); + assertTrue("Deque should contain added element", getCollection().contains(element)); + } + } + + /** + * Tests {@link Deque#element()}. + */ + public void testDequeElement() { + resetEmpty(); + + try { + getCollection().element(); + fail("Deque.element should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + + resetFull(); + + assertTrue(getConfirmed().contains(getCollection().element())); + + if (!isRemoveSupported()) { + return; + } + + final int max = getFullElements().length; + for (int i = 0; i < max; i++) { + final E element = getCollection().element(); + + if (!isNullSupported()) { + assertNotNull(element); + } + + assertTrue(getConfirmed().contains(element)); + + getCollection().remove(element); + getConfirmed().remove(element); + + verify(); + } + + try { + getCollection().element(); + fail("Deque.element should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + } + + /** + * Tests {@link Deque#peek()}. + */ + public void testDequePeek() { + if (!isRemoveSupported()) { + return; + } + + resetEmpty(); + + E element = getCollection().peek(); + assertNull(element); + + resetFull(); + + final int max = getFullElements().length; + for (int i = 0; i < max; i++) { + element = getCollection().peek(); + + if (!isNullSupported()) { + assertNotNull(element); + } + + assertTrue(getConfirmed().contains(element)); + + getCollection().remove(element); + getConfirmed().remove(element); + + verify(); + } + + element = getCollection().peek(); + assertNull(element); + } + + /** + * Tests {@link Deque#remove()}. + */ + public void testDequeRemove() { + if (!isRemoveSupported()) { + return; + } + + resetEmpty(); + + try { + getCollection().remove(); + fail("Deque.remove should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + + resetFull(); + + final int max = getFullElements().length; + for (int i = 0; i < max; i++) { + final E element = getCollection().remove(); + final boolean success = getConfirmed().remove(element); + assertTrue("remove should return correct element", success); + verify(); + } + + try { + getCollection().element(); + fail("Deque.remove should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + } + + /** + * Tests {@link Deque#poll()}. + */ + public void testDequePoll() { + if (!isRemoveSupported()) { + return; + } + + resetEmpty(); + + E element = getCollection().poll(); + assertNull(element); + + resetFull(); + + final int max = getFullElements().length; + for (int i = 0; i < max; i++) { + element = getCollection().poll(); + final boolean success = getConfirmed().remove(element); + assertTrue("poll should return correct element", success); + verify(); + } + + element = getCollection().poll(); + assertNull(element); + } + + /** + * Tests {@link Deque#removeFirstOccurrence(Object o)}. + */ + public void testDequeRemoveFirstOccurrence() { + if (!isRemoveSupported()) { + return; + } + + resetFull(); + + final E[] elements = getFullElements(); + for (final E element : elements) { + final boolean success = getCollection().removeFirstOccurrence(element); + assertTrue("element should be removed successfully", success); + getConfirmed().remove(element); + verify(); + } + + try { + getCollection().element(); + fail("Deque.remove should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + } + + /** + * Tests {@link Deque#removeLastOccurrence(Object o)}. + */ + public void testDequeRemoveLastOccurrence() { + if (!isRemoveSupported()) { + return; + } + + resetFull(); + + final E[] elements = getFullElements(); + for (final E element : elements) { + final boolean success = getCollection().removeLastOccurrence(element); + assertTrue("element should be removed successfully", success); + final LinkedList list = (LinkedList) getConfirmed(); + list.removeLastOccurrence(element); + verify(); + } + + try { + getCollection().element(); + fail("Deque.remove should throw NoSuchElementException"); + } catch (final NoSuchElementException e) { + // expected + } + } + + /** + * Tests {@link Deque#descendingIterator()}. + */ + public void testDescendingIterator() { + if (!isAddSupported()) { + return; + } + + resetEmpty(); + final E[] elements = getFullElements(); + getCollection().addAll(Arrays.asList(elements)); + final Iterator iter = getCollection().descendingIterator(); + final int max = getFullElements().length; + for (int i = max-1; i >= 0; i--) { + assertTrue(iter.hasNext()); + final Object o1 = iter.next(); + assertEquals(elements[i],o1); + } + } + + @SuppressWarnings("unchecked") + public void testEmptyDequeSerialization() throws IOException, ClassNotFoundException { + final Deque deque = makeObject(); + if (!(deque instanceof Serializable && isTestSerialization())) { + return; + } + + final byte[] object = writeExternalFormToBytes((Serializable) deque); + final Deque deque2 = (Deque) readExternalFormFromBytes(object); + + assertEquals("Both deques are empty", 0, deque.size()); + assertEquals("Both deques are empty", 0, deque2.size()); + } + + @SuppressWarnings("unchecked") + public void testFullDequeSerialization() throws IOException, ClassNotFoundException { + final Deque deque = makeFullCollection(); + final int size = getFullElements().length; + if (!(deque instanceof Serializable && isTestSerialization())) { + return; + } + + final byte[] object = writeExternalFormToBytes((Serializable) deque); + final Deque deque2 = (Deque) readExternalFormFromBytes(object); + + assertEquals("Both deques are same size", size, deque.size()); + assertEquals("Both deques are same size", size, deque2.size()); + } + + /** + * Compare the current serialized form of the Deque + * against the canonical version in SVN. + */ + @SuppressWarnings("unchecked") + public void testEmptyDequeCompatibility() throws IOException, ClassNotFoundException { + + // test to make sure the canonical form has been preserved + final Deque deque = makeObject(); + if (deque instanceof Serializable && !skipSerializedCanonicalTests() + && isTestSerialization()) { + writeExternalFormToDisk((Serializable) deque, getCanonicalEmptyCollectionName(deque)); + final Deque deque2 = (Deque) readExternalFormFromDisk(getCanonicalEmptyCollectionName(deque)); + assertEquals("Deque is empty", 0, deque2.size()); + } + } + + /** + * Compare the current serialized form of the Deque + * against the canonical version in SVN. + */ + @SuppressWarnings("unchecked") + public void testFullDequeCompatibility() throws IOException, ClassNotFoundException { + + // test to make sure the canonical form has been preserved + final Deque deque = makeFullCollection(); + if(deque instanceof Serializable && !skipSerializedCanonicalTests() && isTestSerialization()) { + writeExternalFormToDisk((Serializable) deque, getCanonicalFullCollectionName(deque)); + final Deque deque2 = (Deque) readExternalFormFromDisk(getCanonicalFullCollectionName(deque)); + assertEquals("Deques are not the right size", deque.size(), deque2.size()); + } + } +} diff --git a/src/test/java/org/apache/commons/collections4/deque/BlockingDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/BlockingDequeTest.java new file mode 100644 index 0000000000..36aa8366b4 --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/deque/BlockingDequeTest.java @@ -0,0 +1,140 @@ +/* + * 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.collections4.deque; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.Objects; + +/** + * Extension of {@link AbstractDequeTest} for exercising the + * {@link BlockingDeque} implementation. + * + * @since 4.5 + */ +public class BlockingDequeTest extends AbstractDequeTest { + /** + * JUnit constructor. + * + * @param testName the test class name + */ + public BlockingDequeTest(String testName) { + super(testName); + } + + @Override + public Deque makeObject() { + return new BlockingDeque(new LinkedList()); + } + + public void testPutFirst() { + if (!isAddSupported()) { + return; + } + + BlockingDeque deque = (BlockingDeque) makeObject(); + + final E[] elements = getFullElements(); + int size = 0; + try { + for (final E element : elements) { + deque.putFirst(element); + size++; + assertEquals("Deque size should grow after add", size, deque.size()); + assertTrue("Deque should contain added element", Objects.equals(element, deque.getFirst())); + } + + } catch (InterruptedException ex) { + fail(ex.getMessage()); + } + } + + public void testPutLast() { + if (!isAddSupported()) { + return; + } + + BlockingDeque deque = (BlockingDeque) makeObject(); + + final E[] elements = getFullElements(); + int size = 0; + try { + for (final E element : elements) { + deque.putLast(element); + size++; + assertEquals("Deque size should grow after add", size, deque.size()); + assertTrue("Deque should contain added element", Objects.equals(element, deque.getLast())); + } + + } catch (InterruptedException ex) { + fail(ex.getMessage()); + } + } + + public void testTakeFirst() { + if (!isRemoveSupported()) { + return; + } + + BlockingDeque deque = new BlockingDeque(makeFullCollection()); + int size = deque.size(); + + assertTrue("Deque should contain elements",size > 0); + + final E[] elements = getFullElements(); + try { + for (final E element : elements) { + E e = deque.takeFirst(); + size--; + assertEquals("Deque size should decrease",size, deque.size()); + assertTrue(Objects.equals(element, e)); + } + assertEquals("Deque should be empty", 0, deque.size()); + + } catch (InterruptedException ex) { + fail(ex.getMessage()); + } + } + + public void testTakeLast() { + if (!isRemoveSupported()) { + return; + } + + BlockingDeque deque = new BlockingDeque(makeFullCollection()); + int size = deque.size(); + + assertTrue("Deque should contain elements",size > 0); + + final E[] elements = getFullElements(); + try { + for (int i = size-1; i >= 0; i--) { + E e = deque.takeLast(); + assertEquals("Deque size should decrease", i, deque.size()); + assertTrue(Objects.equals(elements[i], e)); + } + assertEquals("Deque should be empty", 0, deque.size()); + + } catch (InterruptedException ex) { + fail(ex.getMessage()); + } + } + @Override + public String getCompatibilityVersion() { + return "4.5"; + } +} diff --git a/src/test/java/org/apache/commons/collections4/deque/CircularDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/CircularDequeTest.java new file mode 100644 index 0000000000..544d03d225 --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/deque/CircularDequeTest.java @@ -0,0 +1,170 @@ +/* + * 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.collections4.deque; + +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * Extension of {@link AbstractDequeTest} for exercising the + * {@link CircularDeque} implementation. + * + * @since 4.5 + */ +public class CircularDequeTest extends AbstractDequeTest { + /** + * JUnit constructor. + * + * @param testName the test class name + */ + public CircularDequeTest(String testName) { + super(testName); + } + + /** + * Returns an empty CircularDeque that won't overflow. + * + * @return an empty CircularDeque + */ + @Override + public Deque makeObject() { + return new CircularDeque<>(100); + } + + /** + * Tests that the removal operation actually removes the first element. + */ + @SuppressWarnings("unchecked") + public void testCircularDequeCircular() { + final List list = new LinkedList<>(); + list.add((E) "A"); + list.add((E) "B"); + list.add((E) "C"); + final Deque deque = new CircularDeque<>(list); + + assertEquals(true, deque.contains("A")); + assertEquals(true, deque.contains("B")); + assertEquals(true, deque.contains("C")); + + deque.add((E) "D"); + + assertEquals(false, deque.contains("A")); + assertEquals(true, deque.contains("B")); + assertEquals(true, deque.contains("C")); + assertEquals(true, deque.contains("D")); + + assertEquals("B", deque.peek()); + assertEquals("B", deque.remove()); + assertEquals("C", deque.remove()); + assertEquals("D", deque.remove()); + } + + /** + * Tests that the constructor correctly throws an exception. + */ + public void testConstructorException1() { + try { + new CircularDeque(0); + fail(); + } catch (final IllegalArgumentException ex) { + + } + } + + /** + * Tests that the constructor correctly throws an exception. + */ + public void testConstructorException2() { + try { + new CircularDeque(-20); + fail(); + } catch (final IllegalArgumentException ex) { + + } + } + + /** + * Tests that the constructor correctly throws an exception. + */ + public void testConstructorException3() { + try { + new CircularDeque(null); + fail(); + } catch (final NullPointerException ex) { + + } + } + + /** + * Tests that the removal operation using method removeFirstOccurrence() and removeFirstOccurrence(). + */ + public void testRemoveElement() { + final List list = new LinkedList<>(); + list.add((E) "A"); + list.add((E) "B"); + list.add((E) "C"); + list.add((E) "D"); + list.add((E) "E"); + final Deque deque = new CircularDeque<>(list); + assertEquals(5, ((CircularDeque) deque).maxSize()); + + deque.addLast((E) "F"); + deque.addLast((E) "G"); + deque.addLast((E) "H"); + + assertEquals(false, deque.removeFirstOccurrence("A")); + assertEquals(false, deque.removeFirstOccurrence("B")); + assertEquals(false, deque.removeLastOccurrence("C")); + assertEquals("[D, E, F, G, H]", deque.toString()); + + assertEquals(true, deque.removeLastOccurrence("H")); + assertEquals("[D, E, F, G]", deque.toString()); + + assertEquals(true, deque.removeLastOccurrence("E")); + assertEquals("[D, F, G]", deque.toString()); + + assertEquals(true, deque.removeLastOccurrence("F")); + assertEquals("[D, G]", deque.toString()); + + assertEquals("D", deque.removeFirst()); + assertEquals("G", deque.removeLast()); + } + + public void testDescendingIterator() { + final List list = new LinkedList<>(); + list.add((E) "A"); + list.add((E) "B"); + list.add((E) "C"); + list.add((E) "D"); + list.add((E) "E"); + final Deque deque = new CircularDeque<>(list); + + final Iterator iterator = deque.descendingIterator(); + while (iterator.hasNext()) { + iterator.next(); + iterator.remove(); + } + assertTrue(deque.isEmpty()); + } + + @Override + public String getCompatibilityVersion() { + return "4.5"; + } +} diff --git a/src/test/java/org/apache/commons/collections4/deque/PredicatedDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/PredicatedDequeTest.java new file mode 100644 index 0000000000..457746804f --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/deque/PredicatedDequeTest.java @@ -0,0 +1,95 @@ +/* + * 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.collections4.deque; + +import java.util.Arrays; +import java.util.Deque; + +import java.util.LinkedList; + +import org.apache.commons.collections4.Predicate; +import org.apache.commons.collections4.functors.NotNullPredicate; +import org.apache.commons.collections4.functors.TruePredicate; + +/** + * Extension of {@link AbstractDequeTest} for exercising the + * {@link PredicatedDeque} implementation. + * + * @since 4.5 + */ +public class PredicatedDequeTest extends AbstractDequeTest { + /** + * JUnit constructor. + * + * @param testName the test class name + */ + public PredicatedDequeTest(String testName) { + super(testName); + } + + protected Predicate notNullPredicate = NotNullPredicate.notNullPredicate(); + + protected Predicate truePredicate = TruePredicate.truePredicate(); + + + protected Deque decorateDeque(final Deque queue, final Predicate predicate) { + return PredicatedDeque.predicatedDeque(queue, predicate); + } + + @Override + public Deque makeObject() { + return decorateDeque(new LinkedList(), truePredicate); + } + + @Override + public Deque makeFullCollection() { + final Deque deque = new LinkedList<>(); + deque.addAll(Arrays.asList(getFullElements())); + return decorateDeque(deque, truePredicate); + } + + public Deque makeTestDeque() { + return decorateDeque(new LinkedList(), notNullPredicate); + } + + @SuppressWarnings("unchecked") + public void testPredicatedDeque() { + final Deque deque = makeTestDeque(); + + assertNull(deque.peek()); + + try { + deque.addFirst(null); + fail("Deque.addFirst should throw IllegalArgumentException"); + } catch (final IllegalArgumentException e) { + // expected + } + + deque.addFirst((E) "one"); + deque.addFirst((E) "two"); + + assertEquals("Deque get", "two", deque.pollFirst()); + assertEquals("Deque get", "one", deque.pollFirst()); + assertNull(deque.pollFirst()); + } + + @Override + public String getCompatibilityVersion() { + return "4.5"; + } + +} diff --git a/src/test/java/org/apache/commons/collections4/deque/SynchronizedDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/SynchronizedDequeTest.java new file mode 100644 index 0000000000..d5af329d6d --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/deque/SynchronizedDequeTest.java @@ -0,0 +1,63 @@ +/* + * 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.collections4.deque; + +import junit.framework.Test; +import org.apache.commons.collections4.BulkTest; +import org.junit.Ignore; + +import java.util.Deque; +import java.util.LinkedList; + +/** + * Extension of {@link AbstractDequeTest} for exercising the + * {@link SynchronizedDeque} implementation. + * + * @since 4.5 + */ +public class SynchronizedDequeTest extends AbstractDequeTest { + /** + * JUnit constructor. + * + * @param testName the test class name + */ + public SynchronizedDequeTest(String testName) { + super(testName); + } + + public static Test suite() { + return BulkTest.makeSuite(SynchronizedDequeTest.class); + } + + @Override + public Deque makeObject() { + return SynchronizedDeque.synchronizedDeque(new LinkedList()); + } + + @Override + public String getCompatibilityVersion() { + return "4.5"; + } + + @Ignore("Run once") + public void testCreate() throws Exception { + Deque deque = makeObject(); + writeExternalFormToDisk((java.io.Serializable) deque, "src/test/resources/data/test/SynchronizedDeque.emptyCollection.version4.5.obj"); + deque = makeFullCollection(); + writeExternalFormToDisk((java.io.Serializable) deque, "src/test/resources/data/test/SynchronizedDeque.fullCollection.version4.5.obj"); + } +} diff --git a/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java b/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java new file mode 100644 index 0000000000..3cd86c8731 --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/deque/TransformedDequeTest.java @@ -0,0 +1,97 @@ +/* + * 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.collections4.deque; + +import org.apache.commons.collections4.Transformer; +import org.apache.commons.collections4.collection.TransformedCollectionTest; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Deque; +import java.util.LinkedList; + +/** + * Extension of {@link AbstractDequeTest} for exercising the + * {@link TransformedDeque} implementation. + * + * @since 4.5 + */ +public class TransformedDequeTest extends AbstractDequeTest { + + /** + * JUnit constructor. + * + * @param testName the test class name + */ + public TransformedDequeTest(final String testName) { + super(testName); + } + + @Override + @SuppressWarnings("unchecked") + public Deque makeObject() { + return TransformedDeque.transformingDeque(new LinkedList(), + (Transformer) TransformedCollectionTest.NOOP_TRANSFORMER); + } + + @Override + @SuppressWarnings("unchecked") + public Deque makeFullCollection() { + final Deque list = new LinkedList<>(); + list.addAll(Arrays.asList(getFullElements())); + return TransformedDeque.transformingDeque(list, (Transformer) TransformedCollectionTest.NOOP_TRANSFORMER); + } + + public void testTransformedDeque() { + final Deque deque = TransformedDeque.transformingDeque(new LinkedList<>(), + TransformedCollectionTest.STRING_TO_INTEGER_TRANSFORMER); + assertEquals(0, deque.size()); + final Object[] elements = new Object[] { "1", "3", "5", "7", "2", "4", "6" }; + for (int i = 0; i < elements.length; i++) { + deque.add(elements[i]); + assertEquals(i + 1, deque.size()); + assertEquals(true, deque.contains(Integer.valueOf((String) elements[i]))); + assertEquals(false, deque.contains(elements[i])); + } + + assertEquals(false, deque.remove(elements[0])); + assertEquals(true, deque.remove(Integer.valueOf((String) elements[0]))); + + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void testTransformedDeque_decorateTransform() { + final Deque originalDeque = new LinkedList(); + final Object[] elements = new Object[] {"1", "3", "5", "7", "2", "4", "6"}; + Collections.addAll(originalDeque, elements); + final Deque deque = TransformedDeque.transformedDeque(originalDeque, + TransformedCollectionTest.STRING_TO_INTEGER_TRANSFORMER); + assertEquals(elements.length, deque.size()); + for (final Object el : elements) { + assertEquals(true, deque.contains(Integer.valueOf((String) el))); + assertEquals(false, deque.contains(el)); + } + + assertEquals(false, deque.remove(elements[0])); + assertEquals(true, deque.remove(Integer.valueOf((String) elements[0]))); + } + + @Override + public String getCompatibilityVersion() { + return "4.5"; + } +}