/* * This program is free software; you can redistribute it and/or modify it under the terms of * the GNU AFFERO GENERAL PUBLIC LICENSE as published by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU AFFERO GENERAL PUBLIC LICENSE for more details. * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE along with this program; * if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package com.meidusa.amoeba.util; /** * A queue implementation that is more efficient than a wrapper around * java.util.Vector. Allows adding and removing elements to/from the beginning, * without the unneccessary System.arraycopy overhead of java.util.Vector. */ public class Queue<T> { public Queue(int suggestedSize) { _size = _suggestedSize = suggestedSize; _items = newArray(_size); } public Queue() { this(4); } public synchronized void clear() { _count = _start = _end = 0; _size = _suggestedSize; _items = newArray(_size); } public synchronized boolean hasElements() { return (_count != 0); } public synchronized int size() { return _count; } public synchronized void prepend(T item) { if (_count == _size) { makeMoreRoom(); } if (_start == 0) { _start = _size - 1; } else { _start--; } _items[_start] = item; _count++; if (_count == 1) { notify(); } } /** * Appends the supplied item to the end of the queue, and notify a consumer * if and only if the queue was previously empty. */ public synchronized void append(T item) { // only notify if the queue was previously empty append0(item, _count == 0); } /** * Appends an item to the queue without notifying anyone. Useful for * appending a bunch of items and then waking up the listener. */ public synchronized void appendSilent(T item) { append0(item, false); } /** * Appends an item to the queue and notify a listener regardless of how many * items are on the queue. Use this for the last item you append to a queue * in a batch via <code>appendSilent</code> because the regular * <code>append</code> will think it doesn't need to notify anyone because * the queue size isn't zero prior to this add. You should also use this * method if you have mutiple consumers listening waiting on the queue, to * guarantee that one will be woken for every element added. */ public synchronized void appendLoud(T item) { append0(item, true); } /** * Internal append method. If subclassing queue, be sure to call this method * from inside a synchronized block. */ protected void append0(T item, boolean notify) { if (_count == _size) { makeMoreRoom(); } _items[_end] = item; _end = (_end + 1) % _size; _count++; if (notify) { notify(); } } /** * Returns the next item on the queue or null if the queue is empty. This * method will not block waiting for an item to be added to the queue. */ public synchronized T getNonBlocking() { if (_count == 0) { return null; } // pull the object off, and clear our reference to it T retval = _items[_start]; _items[_start] = null; _start = (_start + 1) % _size; _count--; return retval; } /** * Blocks the current thread waiting for an item to be added to the queue. * If the queue is currently non-empty, this function will return * immediately. */ public synchronized void waitForItem() { while (_count == 0) { try { wait(); } catch (InterruptedException e) { } } } /** * Gets the next item from the queue blocking for no longer than * <code>maxwait</code> milliseconds waiting for an item to be added to the * queue if it is empty at the time of invocation. */ public synchronized T get(long maxwait) { if (_count == 0) { try { wait(maxwait); } catch (InterruptedException e) { } // if count's still null when we pull out, we waited // ourmaxwait time. if (_count == 0) { return null; } } return get(); } /** * Gets the next item from the queue, blocking until an item is added to the * queue if the queue is empty at time of invocation. */ public synchronized T get() { while (_count == 0) { try { wait(); } catch (InterruptedException e) { } } // pull the object off, and clear our reference to it T retval = _items[_start]; _items[_start] = null; _start = (_start + 1) % _size; _count--; // if we are only filling 1/8th of the space, shrink by half if ((_size > MIN_SHRINK_SIZE) && (_size > _suggestedSize) && (_count < (_size >> 3))) shrink(); return retval; } private void makeMoreRoom() { T[] items = newArray(_size * 2); System.arraycopy(_items, _start, items, 0, _size - _start); System.arraycopy(_items, 0, items, _size - _start, _end); _start = 0; _end = _size; _size *= 2; _items = items; } // shrink by half private void shrink() { T[] items = newArray(_size / 2); if (_start > _end) { // the data wraps around System.arraycopy(_items, _start, items, 0, _size - _start); System.arraycopy(_items, 0, items, _size - _start, _end + 1); } else { // the data does not wrap around System.arraycopy(_items, _start, items, 0, _end - _start + 1); } _size = _size / 2; _start = 0; _end = _count; _items = items; } @SuppressWarnings("unchecked") private T[] newArray(int size) { return (T[]) new Object[size]; } public String toString() { StringBuilder buf = new StringBuilder(); buf.append("[count=").append(_count); buf.append(", size=").append(_size); buf.append(", start=").append(_start); buf.append(", end=").append(_end); buf.append(", elements={"); for (int i = 0; i < _count; i++) { int pos = (i + _start) % _size; if (i > 0) buf.append(", "); buf.append(_items[pos]); } return buf.append("}]").toString(); } protected final static int MIN_SHRINK_SIZE = 1024; protected T[] _items; protected int _count = 0; protected int _start = 0, _end = 0; protected int _suggestedSize, _size = 0; }