--- /dev/null
+package net.sf.openrocket.logging;
+
+import java.util.AbstractQueue;
+import java.util.ArrayList;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * A cyclic buffer with a fixed size. When more data is inserted, the newest
+ * data will overwrite the oldest data.
+ * <p>
+ * Though this class implements the Queue interface, it specifically breaks the
+ * contract by overwriting (removing) data without specific removal. It also
+ * currently does not support removing arbitrary elements from the set.
+ * <p>
+ * The methods in this class are synchronized for concurrent modification.
+ * However, iterating over the set is not thread-safe. To obtain a snapshot
+ * of the state of the buffer, use {@link #asList()}.
+ *
+ * @param <E> the object type that is stored.
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class CyclicBuffer<E> extends AbstractQueue<E> {
+
+ private final ArrayList<E> buffer;
+ private final int maxSize;
+
+ private int startPosition = 0;
+ private int size = 0;
+ private int overwriteCount = 0;
+
+ private int modCount = 0;
+
+
+ /**
+ * Create a cyclic buffer of the specified size.
+ *
+ * @param size the size of the cyclic buffer.
+ */
+ public CyclicBuffer(int size) {
+ this.buffer = new ArrayList<E>(size);
+ for (int i=0; i<size; i++) {
+ this.buffer.add(null);
+ }
+ this.maxSize = size;
+ }
+
+
+
+ @Override
+ public synchronized boolean offer(E e) {
+ buffer.set((startPosition + size) % maxSize, e);
+ if (size < maxSize) {
+ size++;
+ } else {
+ startPosition = next(startPosition);
+ overwriteCount++;
+ }
+
+ modCount++;
+ return true;
+ }
+
+
+ @Override
+ public synchronized E peek() {
+ if (size == 0)
+ return null;
+ return buffer.get(startPosition);
+ }
+
+
+ @Override
+ public synchronized E poll() {
+ if (size == 0)
+ return null;
+
+ E element = buffer.get(startPosition);
+ startPosition = next(startPosition);
+ size--;
+
+ modCount++;
+ return element;
+ }
+
+
+ @Override
+ public synchronized int size() {
+ return size;
+ }
+
+
+ @Override
+ public synchronized Iterator<E> iterator() {
+ return new CyclicBufferIterator();
+ }
+
+
+ /**
+ * Return a snapshot of the current buffered objects in the order they
+ * were placed in the buffer. The list is independent of the buffer.
+ *
+ * @return a list of the buffered objects.
+ */
+ public synchronized List<E> asList() {
+ ArrayList<E> list = new ArrayList<E>(size);
+ if (startPosition + size > maxSize) {
+ list.addAll(buffer.subList(startPosition, maxSize));
+ list.addAll(buffer.subList(0, startPosition + size - maxSize));
+ } else {
+ list.addAll(buffer.subList(startPosition, startPosition+size));
+ }
+ return list;
+ }
+
+
+ /**
+ * Return the number of elements that have been overwritten in the buffer.
+ * The overwritten elements are the elements that have been added to the
+ * buffer, have not been explicitly removed but are not present in the list.
+ *
+ * @return the number of overwritten elements this far.
+ */
+ public synchronized int getOverwriteCount() {
+ return overwriteCount;
+ }
+
+
+ private int next(int n) {
+ return (n+1) % maxSize;
+ }
+
+
+ private class CyclicBufferIterator implements Iterator<E> {
+
+ private int expectedModCount;
+ private int n = 0;
+
+ public CyclicBufferIterator() {
+ this.expectedModCount = modCount;
+ }
+
+ @Override
+ public boolean hasNext() {
+ synchronized (CyclicBuffer.this) {
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException("expectedModCount="+
+ expectedModCount+" modCount=" + modCount);
+ }
+ return (n < size);
+ }
+ }
+
+ @Override
+ public E next() {
+ synchronized (CyclicBuffer.this) {
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException("expectedModCount="+
+ expectedModCount+" modCount=" + modCount);
+ }
+ if (n >= size) {
+ throw new NoSuchElementException("n="+n+" size="+size);
+ }
+ n++;
+ return buffer.get((startPosition + n-1) % maxSize);
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("random remove not supported");
+ }
+ }
+}