001package org.javasimon.callback.lastsplits;
002
003import java.util.AbstractList;
004import java.util.Arrays;
005import java.util.Collection;
006import java.util.Iterator;
007
008/**
009 * Ring/circular buffer, fixed size FIFO list. Stores values as a fixed size
010 * array looping insertion point when array is full. Warnings: <ul> <li>Only
011 * needed methods of the List interface have been implemented.</li> <li>This
012 * class is not thread safe</li> </ul>
013 *
014 * @author gquintana
015 * @since 3.2
016 */
017public class CircularList<T> extends AbstractList<T> {
018
019        /** Elements. */
020        private final T[] elements;
021        /**
022         * Index + 1 of the last element.
023         * Or said differently index where will be added the next element.
024         */
025        private int lastIndex;
026        /**
027         * Index of the the first element.
028         * Or said differently index which will be removed when list capacity is achieved.
029         */
030        private int firstIndex = -1;
031        /** Number of element stored in this list. */
032        private int size;
033
034        /**
035         * Constructor.
036         *
037         * @param capacity Size of the ring buffer
038         */
039        public CircularList(int capacity) {
040                elements = (T[]) new Object[capacity];
041        }
042
043        /**
044         * Get capacity (buffer size).
045         *
046         * @return capacity
047         */
048        public int getCapacity() {
049                return elements.length;
050        }
051
052        /**
053         * Converts an index starting from 0 into an index starting from first
054         *
055         * @param index Index
056         * @return Index converted
057         */
058        private int convertIndex(int index) {
059                return (lastIndex + index) % elements.length;
060        }
061
062        /**
063         * Get element by index
064         *
065         * @param index Index
066         * @return Element
067         */
068        public T get(int index) {
069                return elements[convertIndex(index)];
070        }
071
072        /**
073         * Return the number of elements in the list
074         *
075         * @return Size
076         */
077        public int size() {
078                return size;
079        }
080
081        /**
082         * Tells whether the list is empty
083         *
084         * @return true if empty
085         */
086        @Override
087        public boolean isEmpty() {
088                return size == 0;
089        }
090
091        /**
092         * Increment an index
093         *
094         * @param index Input index
095         * @param maxIndex Assigned value when capacity is reached
096         * @return Output index
097         */
098        private int incrementIndex(int index, int maxIndex) {
099                int newIndex = index + 1;
100                if (newIndex >= elements.length) {
101                        newIndex = maxIndex;
102                }
103                return newIndex;
104        }
105
106        /**
107         * Add an element to the list, overwriting last added element when
108         * list's capacity is reached
109         *
110         * @param newElement Element to add
111         * @return Always true
112         */
113        @Override
114        public boolean add(T newElement) {
115                elements[lastIndex] = newElement;
116                lastIndex = incrementIndex(lastIndex, 0);
117                if (isEmpty()) {
118                        // First element
119                        firstIndex = 0;
120                        size = 1;
121                } else if (isFull()) {
122                        // Reuse space
123                        firstIndex = lastIndex;
124                } else {
125                        size = incrementIndex(size, elements.length);
126                }
127                return true;
128        }
129
130        private boolean isFull() {
131                return size == elements.length;
132        }
133
134        /**
135         * Inserts a collection of elements, looping on them.
136         *
137         * @param newElements Collection of elements to add
138         * @return Always true
139         */
140        @Override
141        public boolean addAll(Collection<? extends T> newElements) {
142                for (T newElement : newElements) {
143                        add(newElement);
144                }
145                return true;
146        }
147
148        /**
149         * Removes the first (inserted) element of the collection.
150         *
151         * @return Removed element or null if any
152         */
153        public T removeFirst() {
154                if (isEmpty()) {
155                        return null;
156                }
157                T firstElement = elements[firstIndex];
158                elements[firstIndex] = null;
159                firstIndex = incrementIndex(firstIndex, 0);
160                return firstElement;
161        }
162
163        /**
164         * Returns the first (inserted) element.
165         *
166         * @return First element or null if any
167         */
168        public T first() {
169                return isEmpty() ? null : elements[lastIndex];
170        }
171
172        /**
173         * Returns the last (inserted) element.
174         *
175         * @return Last element or null if any
176         */
177        public T last() {
178                return isEmpty() ? null : elements[lastIndex];
179        }
180
181        /** Removes all elements from the list. */
182        @Override
183        public void clear() {
184                Arrays.fill(elements, null);
185                lastIndex = 0;
186                firstIndex = -1;
187                size = 0;
188        }
189
190        /**
191         * Creates a new iterator to browse elements.
192         *
193         * @return Iterator
194         */
195        @Override
196        public java.util.Iterator<T> iterator() {
197                if (isEmpty()) {
198                        return new EmptyIterator();
199                } else {
200                        return new MainIterator();
201                }
202        }
203
204        /** Empty iterator used when the list is empty. */
205        private class EmptyIterator implements Iterator<T> {
206
207                public boolean hasNext() {
208                        return false;
209                }
210
211                public T next() {
212                        throw new UnsupportedOperationException("Not supported yet.");
213                }
214
215                public void remove() {
216                        throw new UnsupportedOperationException("Not supported yet.");
217                }
218        }
219
220        /** Main iterator user when the list contains at least one element. */
221        private class MainIterator implements Iterator<T> {
222
223                /** Index of the element. */
224                private int nextIndex = firstIndex;
225                /**
226                 * Is it first element. This flag is required because when
227                 * firstIndex=lastIndex (ring buffer is completely used) we
228                 * don't whether it's the first iteration or the last one.
229                 */
230                private boolean begin = true;
231
232                /** Is there another element in the list. */
233                public boolean hasNext() {
234                        return begin || nextIndex != lastIndex;
235                }
236
237                /** Returns an element and compute the next one. */
238                public T next() {
239                        T nextElement = elements[nextIndex];
240                        begin = false;
241                        nextIndex = incrementIndex(nextIndex, 0);
242                        return nextElement;
243                }
244
245                public void remove() {
246                        throw new UnsupportedOperationException("Not supported");
247                }
248        }
249
250        /**
251         * Copy elements in a target array.
252         *
253         * @param elementsCopy Target array
254         */
255        private <X> void copy(X[] elementsCopy) {
256                if (!isEmpty()) {
257                        if (firstIndex < lastIndex) {
258                                System.arraycopy(elements, firstIndex, elementsCopy, 0, lastIndex - firstIndex);
259                        } else {
260                                int firstSize = elements.length - firstIndex;
261                                System.arraycopy(elements, firstIndex, elementsCopy, 0, firstSize);
262                                System.arraycopy(elements, 0, elementsCopy, firstSize, lastIndex);
263                        }
264                }
265        }
266
267        @Override
268        public Object[] toArray() {
269                Object[] elementsCopy = new Object[size];
270                copy(elementsCopy);
271                return elementsCopy;
272        }
273
274        @Override
275        public <T> T[] toArray(T[] elementsCopy) {
276                if (elementsCopy.length >= size) {
277                        copy(elementsCopy);
278                        return elementsCopy;
279                } else {
280                        return (T[]) toArray();
281                }
282        }
283}