release 0.9.6
[debian/openrocket] / src / net / sf / openrocket / logging / CyclicBuffer.java
1 package net.sf.openrocket.logging;
2
3 import java.util.AbstractQueue;
4 import java.util.ArrayList;
5 import java.util.ConcurrentModificationException;
6 import java.util.Iterator;
7 import java.util.List;
8 import java.util.NoSuchElementException;
9
10 /**
11  * A cyclic buffer with a fixed size.  When more data is inserted, the newest
12  * data will overwrite the oldest data.
13  * <p>
14  * Though this class implements the Queue interface, it specifically breaks the
15  * contract by overwriting (removing) data without specific removal.  It also
16  * currently does not support removing arbitrary elements from the set.
17  * <p>
18  * The methods in this class are synchronized for concurrent modification.
19  * However, iterating over the set is not thread-safe.  To obtain a snapshot
20  * of the state of the buffer, use {@link #asList()}.
21  * 
22  * @param <E>   the object type that is stored.
23  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
24  */
25 public class CyclicBuffer<E> extends AbstractQueue<E> {
26
27         private final ArrayList<E> buffer;
28         private final int maxSize;
29         
30         private int startPosition = 0;
31         private int size = 0;
32         private int overwriteCount = 0;
33         
34         private int modCount = 0;
35         
36
37         /**
38          * Create a cyclic buffer of the specified size.
39          * 
40          * @param size  the size of the cyclic buffer.
41          */
42         public CyclicBuffer(int size) {
43                 this.buffer = new ArrayList<E>(size);
44                 for (int i=0; i<size; i++) {
45                         this.buffer.add(null);
46                 }
47                 this.maxSize = size;
48         }
49
50         
51
52         @Override
53         public synchronized boolean offer(E e) {
54                 buffer.set((startPosition + size) % maxSize, e);
55                 if (size < maxSize) {
56                         size++;
57                 } else {
58                         startPosition = next(startPosition);
59                         overwriteCount++;
60                 }
61                 
62                 modCount++;
63                 return true;
64         }
65
66         
67         @Override
68         public synchronized E peek() {
69                 if (size == 0)
70                         return null;
71                 return buffer.get(startPosition);
72         }
73
74         
75         @Override
76         public synchronized E poll() {
77                 if (size == 0)
78                         return null;
79                 
80                 E element = buffer.get(startPosition);
81                 startPosition = next(startPosition);
82                 size--;
83                 
84                 modCount++;
85                 return element;
86         }
87
88         
89         @Override
90         public synchronized int size() {
91                 return size;
92         }
93         
94
95         @Override
96         public synchronized Iterator<E> iterator() {
97                 return new CyclicBufferIterator();
98         }
99
100
101         /**
102          * Return a snapshot of the current buffered objects in the order they
103          * were placed in the buffer.  The list is independent of the buffer.
104          * 
105          * @return      a list of the buffered objects.
106          */
107         public synchronized List<E> asList() {
108                 ArrayList<E> list = new ArrayList<E>(size);
109                 if (startPosition + size > maxSize) {
110                         list.addAll(buffer.subList(startPosition, maxSize));
111                         list.addAll(buffer.subList(0, startPosition + size - maxSize));
112                 } else {
113                         list.addAll(buffer.subList(startPosition, startPosition+size));
114                 }
115                 return list;
116         }
117
118         
119         /**
120          * Return the number of elements that have been overwritten in the buffer.
121          * The overwritten elements are the elements that have been added to the
122          * buffer, have not been explicitly removed but are not present in the list.
123          * 
124          * @return      the number of overwritten elements this far.
125          */
126         public synchronized int getOverwriteCount() {
127                 return overwriteCount;
128         }
129         
130         
131         private int next(int n) {
132                 return (n+1) % maxSize;
133         }
134         
135         
136         private class CyclicBufferIterator implements Iterator<E> {
137
138                 private int expectedModCount;
139                 private int n = 0;
140
141                 public CyclicBufferIterator() {
142                         this.expectedModCount = modCount;
143                 }
144
145                 @Override
146                 public boolean hasNext() {
147                         synchronized (CyclicBuffer.this) {
148                                 if (expectedModCount != modCount) {
149                                         throw new ConcurrentModificationException("expectedModCount="+
150                                                         expectedModCount+" modCount=" + modCount);
151                                 }
152                                 return (n < size);
153                         }
154                 }
155
156                 @Override
157                 public E next() {
158                         synchronized (CyclicBuffer.this) {
159                                 if (expectedModCount != modCount) {
160                                         throw new ConcurrentModificationException("expectedModCount="+
161                                                         expectedModCount+" modCount=" + modCount);
162                                 }
163                                 if (n >= size) {
164                                         throw new NoSuchElementException("n="+n+" size="+size);
165                                 }
166                                 n++;
167                                 return buffer.get((startPosition + n-1) % maxSize);
168                         }
169                 }
170
171                 @Override
172                 public void remove() {
173                         throw new UnsupportedOperationException("random remove not supported");
174                 }
175         }
176 }