/*******************************************************************************
* Copyright (c) 2010 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
******************************************************************************/
package org.eclipse.nebula.visualization.xygraph.dataprovider;
import java.util.AbstractCollection;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* A particular circular buffer. New arrived data will be appended to the tail
* of the buffer. When buffer is full, the oldest data will be deleted when new
* data arrived.
*
* @author Xihui Chen
*/
public class CircularBuffer<T> extends AbstractCollection<T> {
private int bufferSize = 0;
private T[] buffer;
private int head;
private int tail;
private int count;
public CircularBuffer(int bufferSize) {
if (bufferSize <= 0)
throw new IllegalArgumentException("Buffer size must be greater than zero.");
this.setBufferSize(bufferSize, true);
}
/**
* Add an element.
*
* @param element
*/
public synchronized boolean add(T element) {
if (tail == head && count == bufferSize) { // buffer is full
buffer[tail] = element;
head = (head + 1) % bufferSize;
tail = (tail + 1) % bufferSize;
return true;
} else {// buffer is not full
buffer[tail] = element;
tail = (tail + 1) % bufferSize;
count++;
return true;
}
}
/**
* Get element
*
* @param index
* the index of the element in the buffer.
* @return the element. null if the data at the index doesn't exist.
*/
public synchronized T getElement(int index) {
if (index < count)
return buffer[(head + index) % bufferSize];
else
return null;
}
/**
* Get head element
*
* @return the head element. null if the buffer is empty.
*/
public synchronized T getHead() {
if (count > 0)
return buffer[head];
else
return null;
}
/**
* Get tail element
*
* @return the tail element. null if the buffer is empty.
*/
public synchronized T getTail() {
if (count > 0)
return buffer[(head + count - 1) % bufferSize];
else
return null;
}
/**
* clear the buffer;
*/
public synchronized void clear() {
for (int i = 0; i < count; i++) {
buffer[(head + i) % bufferSize] = null;
}
head = 0;
tail = 0;
count = 0;
}
/**
* Set the buffer size.
*
* @param bufferSize
* the bufferSize to set
* @param clear
* clear the buffer if true. Otherwise keep the exist data;
* Oldest data will be omitted if the new bufferSize is less than
* the exist data count.
*/
@SuppressWarnings("unchecked")
public synchronized void setBufferSize(int bufferSize, boolean clear) {
assert bufferSize > 0;
if (this.bufferSize != bufferSize) {
if (clear) {// clear
buffer = (T[]) new Object[bufferSize];
clear();
} else {// keep the exist data
T[] tempBuffer = (T[]) toArray();
buffer = (T[]) new Object[bufferSize];
if (bufferSize < count) {
System.arraycopy(tempBuffer, count - bufferSize, buffer, 0, bufferSize);
} else {
System.arraycopy(tempBuffer, 0, buffer, 0, count);
}
count = Math.min(bufferSize, count);
head = 0;
tail = count % bufferSize;
}
this.bufferSize = bufferSize;
}
}
/**
* @return the bufferSize
*/
public synchronized int getBufferSize() {
return bufferSize;
}
public Iterator<T> iterator() {
return new Iterator<T>() {
private int index = 0;
public boolean hasNext() {
return index < count;
}
public T next() {
if (!hasNext())
throw new NoSuchElementException();
return buffer[(head + index++) % bufferSize];
}
public void remove() {
throw new UnsupportedOperationException("Remove is not supported.");
}
};
}
@Override
public int size() {
return count;
}
}