/*
* Copyright (c) 2013, MGrossmann
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the jo-widgets.org nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL jo-widgets.org BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.jowidgets.util.collection;
import org.jowidgets.util.Assert;
final class CircularBufferImpl<ELEMENT_TYPE> implements ICircularBuffer<ELEMENT_TYPE> {
private final int capacity;
private ELEMENT_TYPE[] buffer;
private int insertIndex;
private int size;
CircularBufferImpl(final int capacity) {
if (capacity < 1) {
throw new IllegalArgumentException("Buffer must have at least capacity of one.");
}
this.capacity = capacity;
clear();
}
@Override
public int capacity() {
return capacity;
}
@Override
public int size() {
return size;
}
@Override
public void setSize(final int size) {
setSize(size, true);
}
@Override
public void setSize(final int size, final boolean clear) {
Assert.paramInBounds(capacity, size, "size");
if (this.size < size) {
setIcreasedSize(size);
}
else if (this.size > size) {
setDecreasedSize(size, clear);
}
//else this.size == size, nothing to do
}
private void setIcreasedSize(final int size) {
final int increasment = size - this.size;
if (increasment <= 0) {
throw new IllegalArgumentException("The new size must be creater than the current size");
}
insertIndex = insertIndex + increasment;
if (insertIndex >= capacity) {
insertIndex = insertIndex - capacity;
}
this.size = size;
}
private void setDecreasedSize(final int size, final boolean clear) {
if (clear) {
setDecreasedSizeWithClear(size);
}
else {
setDecreasedSizeNoClear(size);
}
}
private void setDecreasedSizeNoClear(final int size) {
final int decreasement = this.size - size;
if (decreasement <= 0) {
throw new IllegalArgumentException("The new size must be less than the current size");
}
insertIndex = insertIndex - decreasement;
if (insertIndex < 0) {
insertIndex = insertIndex + capacity;
}
this.size = size;
}
private void setDecreasedSizeWithClear(final int size) {
while (this.size > size) {
removeLast();
}
}
@Override
public void add(final ELEMENT_TYPE element) {
buffer[insertIndex] = element;
insertIndex++;
if (insertIndex >= capacity) {
insertIndex = 0;
}
if (size < capacity) {
size++;
}
}
@Override
public boolean removeLast() {
if (size <= 0) {
return false;
}
insertIndex--;
size--;
if (insertIndex < 0) {
insertIndex = capacity - 1;
}
buffer[insertIndex] = null;
return true;
}
@Override
public void set(final int index, final ELEMENT_TYPE element) {
Assert.paramInBounds(size - 1, index, "index");
buffer[getTransferedIndex(index)] = element;
}
@Override
public void fill(final ELEMENT_TYPE element) {
final int elementsToFill = capacity - size();
if (elementsToFill <= 0) {
return;
}
else if (element == null) {
insertIndex = getTransferedIndex(0);
size = capacity;
}
else {
for (int i = 0; i < elementsToFill; i++) {
add(element);
}
}
}
@Override
public void fill() {
fill(null);
}
@Override
public ELEMENT_TYPE get(final int index) {
Assert.paramInBounds(capacity - 1, index, "index");
if (index >= size) {
return null;
}
else {
return buffer[getTransferedIndex(index)];
}
}
private int getTransferedIndex(final int index) {
final int residualCapacity = capacity - size;
int shiftedIndex = insertIndex + residualCapacity + index;
if (shiftedIndex >= capacity) {
shiftedIndex = shiftedIndex - capacity;
}
else if (shiftedIndex < 0) {
shiftedIndex = shiftedIndex + capacity - 1;
}
return shiftedIndex;
}
@SuppressWarnings("unchecked")
@Override
public void clear() {
this.buffer = (ELEMENT_TYPE[]) new Object[capacity];
insertIndex = 0;
size = 0;
}
@Override
public String toString() {
return "CircularBufferImpl [capacity=" + capacity + ", insertIndex=" + insertIndex + ", size" + size + "]";
}
}