release 0.9.6
[debian/openrocket] / src / net / sf / openrocket / logging / CyclicBuffer.java
diff --git a/src/net/sf/openrocket/logging/CyclicBuffer.java b/src/net/sf/openrocket/logging/CyclicBuffer.java
new file mode 100644 (file)
index 0000000..31328ba
--- /dev/null
@@ -0,0 +1,176 @@
+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");
+               }
+       }
+}