/* Nord Modular Midi Protocol 3.03 Library Copyright (C) 2003-2006 Marcus Andersson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU 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 net.sf.nmedit.jnmprotocol2.utils; import java.util.ArrayList; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Queue; import net.sf.nmedit.jnmprotocol2.utils.QueueBuffer; /** * A queue that is optimized for buffering and fast * insert/remove operations of elements. * * The queue uses two different linked lists internally. * The first list contains the elements that have an object * associated with them. The second list to which we refer * with the term cache, contains the elements * that have no object associated with them anymore. * * If elements that contain data are removed from the queue * (for example using poll()) the will be stored in the cache * (the reference to the data is set to null). * Later if a data is offered to the queue, it is not necessary * to create a new list element but instead an element from the * cache can be used. * * The advantages of the cache are: * <ul> * <li>it is faster to reuse an element instead of allocating a new one</li> * <li>less in best case no garbage is created. The garbage collector * has less work to do</li> * <li>memory consumption is constant if the queue is used correctly. * A correct use means that the maximum number of elements is below * or equal a constant boundary. For example the queue contains always * <=1000 elements. A queue that uses no such cache can produce * up to several MB of garbage. * </li> * </ul> * * The queue uses a linked list as internal representation * and recycles unused list items to avoid unecessary garbage. * * @author Christian Schneider */ public class QueueBuffer<E> implements Queue<E> { /** * Element of a linked list. Contains the stored element data and * the next element in the list. */ private static class Element<E> { public E data; public Element<E> next; public Element(E data) { this.data = data; this.next = null; } } // head is the first element in the list (the front element in this queue). // always one of the following contitions is true: // - head == null // if and only if the queue is empty // - head == tail && head!=null // if and only if the number of elements in the queue is 1 // - head != tail // if and only if the queue contains more than one element private Element<E> head; // tail is the last element in the list (the last element in this queue). // always one of the following contitions is true: // - tail == null // if and only if the queue is empty // - head == tail && head!=null // if and only if the number of elements in the queue is 1 // - head != tail // if and only if the queue contains more than one element private Element<E> tail; // the head of the list of cached elements. This list contains the elements // that were previously used and removed from the queue. We recycle // them if new data is offered to the queue. // elements in the cache contain no data, thus the Element.data field is always null // always one of the following contitions is true: // - cacheHead == null // if and only if the cache is empty // - cacheHead == cacheTail && cacheHead!=null // if and only if the number of elements in the cache is 1 // - cacheHead != cacheTail // if and only if the cache contains more than one element private Element<E> cacheHead; // the tail of the list of cached elements // elements in the cache contain no data, thus the Element.data field is always null // always one of the following contitions is true: // - cacheTail == null // if and only if the cache is empty // - cacheHead == cacheTail && cacheHead!=null // if and only if the number of elements in the cache is 1 // - cacheHead != cacheTail // if and only if the cache contains more than one element private Element<E> cacheTail; // variable is changed if the collection is modified private transient int modcount; /** * Removes all elements from this queue and adds them to a new queue. * @return a new queue */ public QueueBuffer<E> release() { QueueBuffer<E> q = new QueueBuffer<E>(); q.head = head; q.tail = tail; head = null; tail = null; modcount++; return q; } /** * Inserts the specified element into this queue. * * If possible the queue will not create new element * container but recycle one that is not used anymore. * * @param o the element to insert. * @return <tt>true</tt> if o is not null, otherwise false */ public final boolean offer( E o ) { if (o == null) return false; // the new element Element<E> e ; // we check if we can recycle the new element // or if we have to create a new instance if (cacheHead != null) { // recycle the head of the cache e = cacheHead; // remove head element from cache cacheHead = cacheHead.next; if (cacheHead==null) cacheTail = null; // initialize element fields e.next = null; e.data = o; } else { // create a new instance e = new Element<E>(o); } // no we add the element it to the queue // we first have to check if the queue is empty // or if it already contains some elements if (tail != null) { // the queue is not empty // the last element will be e tail.next = e; // and e will become the new tail tail = e; } else { // the queue is empty, thus e is both // head and tail head = tail = e; } // we have changed the queue modcount++; // always return true return true; } /** * Retrieves and removes the head of this queue, or <tt>null</tt> * if this queue is empty. * * The element container of the removed element will be * added to a cache and recycled if further elements are added * to the qeue. * * @return the head of this queue, or <tt>null</tt> if this * queue is empty. */ public final E poll() { // first see if the queue is empty and return null if so if (head == null) return null; // the queue is not empty // the result element / data Element<E> e = head; E result = e.data; // if the head is removed then // - the next element in the queue will become the new head head = head.next; // - if the next element is null then tail will become null, too if (head == null) tail = null; // now we can cache the unused element container // first disconnect it e.next = null; // then add it to the cache (e.data will be automatically set to null) cache(e); // we have changed the queue modcount++; // we are done return result; } /** * Retrieves and removes the head of this queue. This method * differs from the <tt>poll</tt> method in that it throws an * exception if this queue is empty. * * @return the head of this queue. * @throws NoSuchElementException if this queue is empty. */ public E remove() { E e = poll(); if (e == null) throw new NoSuchElementException(); return e; } /** * Retrieves, but does not remove, the head of this queue, * returning <tt>null</tt> if this queue is empty. * * @return the head of this queue, or <tt>null</tt> if this queue * is empty. */ public final E peek() { return head != null ? head.data : null; } /** * Retrieves, but does not remove, the head of this queue. This method * differs from the <tt>peek</tt> method only in that it throws an * exception if this queue is empty. * * @return the head of this queue. * @throws NoSuchElementException if this queue is empty. */ public final E element() { E e = peek(); if (e == null) throw new NoSuchElementException(); return e; } /** * Returns the number of elements in this collection. If this collection * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns * <tt>Integer.MAX_VALUE</tt>. * * Do not use this method if not absolutely necessary because it * has complexity O(n). * * @return the number of elements in this collection */ public int size() { int size = 0; Element<E> pos = head; while (pos!=null && size!=Integer.MAX_VALUE) { size ++; pos = pos.next; } return size; } /** * Returns <tt>true</tt> if this collection contains no elements. * * @return <tt>true</tt> if this collection contains no elements */ public final boolean isEmpty() { return head == null; } /** * Returns <tt>true</tt> if this collection contains the specified * element. More formally, returns <tt>true</tt> if and only if this * collection contains at least one element <tt>e</tt> such that * <tt>(o==null ? e==null : o.equals(e))</tt>. * * @param o element whose presence in this collection is to be tested. * @return <tt>true</tt> if this collection contains the specified * element * @throws NullPointerException if the specified element is null and this * collection does not support null elements (optional). */ public boolean contains( Object o ) { if (o==null) throw new NullPointerException(); Element<E> pos = head; while (pos!=null) { if (pos.data==o || pos.data.equals(o)) return true; pos = pos.next; } return false; } /** * Returns an iterator over the elements in this collection. * The elements are returned in the same order like they are * stored in this queue. * * @return an <tt>Iterator</tt> over the elements in this collection */ public Iterator<E> iterator() { return new Iterator<E> () { // the data of the element returned by next() // or null if next() was not called or remove() was called E data = null; // current position, starting at the head of the queue // if pos == null then the iteration is completed Element<E> pos = head; // remember modification counter so we can see if // concurrent modifications were performed int knownMod = modcount; public boolean hasNext() { return pos != null; } /** * Checks if the modification counter has changed and * throws a ConcurrentModificationException if this is * the case. */ private void checkMod() { if (knownMod != modcount) throw new ConcurrentModificationException(); } public E next() { // check for modifications checkMod(); // see if element exists if (!hasNext()) throw new NoSuchElementException(); // remember data data = pos.data; // remember position pos = pos.next; return data; } public void remove() { // check for modifications checkMod(); // check if data!=null what means that next() has been called if (data == null) throw new IllegalStateException(); // remove data QueueBuffer.this.remove(data); // set data to null since we also use it as state variable data = null; // update the known modification value knownMod = modcount; } }; } public Object[] toArray() { List<Object> list = new ArrayList<Object>(size()); list.addAll(this); return list.toArray(); } public <T> T[] toArray( T[] a ) { List<E> list = new ArrayList<E>(size()); list.addAll(this); return list.toArray(a); } /** * Adds the specified element to the queue. * * @param o element whose presence in this collection is to be ensured. * @return <tt>true</tt> * @throws NullPointerException if the specified element is null. */ public boolean add( E o ) { if (o == null) throw new NullPointerException(); return offer(o); } /** * Removes a single instance of the specified element from this * collection, if it is present. More formally, * removes an element <tt>e</tt> such that <tt>(o==null ? e==null : * o.equals(e))</tt>, if this collection contains one or more such * elements. Returns true if this collection contained the specified * element. * * @param o element to be removed from this collection, if present. * @return <tt>true</tt> if this collection contained the specified element * @throws NullPointerException if the specified element is null. */ public boolean remove( Object o ) { // null-elements are not allowed if (o==null) throw new NullPointerException(); // the element is not in the queue if (isEmpty()) return false; // prev is the previous element of pos Element<E> prev = null; // the current position in the queue Element<E> pos = head; while (pos!=null) { // see if we have found the specified element if (pos.data == o || pos.data.equals(o)) { // remove element // if we remove the head then we have to set head:=head.next if (head == pos) head = head.next; // if we remove tail then we have to set tail:=previous of tail if (tail == pos) tail = prev; // if either head or tail is null then the queue is empty and we have to // set both to null if (head == null ^ tail == null) head = tail = null; // if the removed element (pos) has a previous element // than we have to link the previous element with the next element // to be sure that pos is not inside the list anymore if (prev != null) prev.next = pos.next; // now we can cache the unused element container // first disconnect it pos.next = null; // then add it to the cache (e.data will be automatically set to null) cache(pos); // update modification counter modcount++; // the element has been removed return true; } // we have not found it yet // ensure that prev is previous element of pos prev = pos; pos = prev.next; } // the element could not be found, thus it was not removed return false; } /** * Returns <tt>true</tt> if this collection contains all of the elements * in the specified collection. * * @param c collection to be checked for containment in this collection. * @return <tt>true</tt> if this collection contains all of the elements * in the specified collection * @throws NullPointerException if the specified collection is <tt>null</tt>. * @see #contains(Object) */ public boolean containsAll( Collection<?> c ) { if (c == this || c.isEmpty()) return true; for (Object o : c) { // we do not support null-elements if (o == null) throw new NullPointerException(); if (!contains(o)) return false; } return true; } /** * Offers each element of the specified queue to this queue. The specified * queue does not contain any elements after this operation. * * To balance the transaction of elements, this queue will provide * all cached (unused) elements to the specified queue. * * Note: The operation has complexity O(1) and not O(n). * * @param queue the queue to offer to this queue * @return true if this collection changed as result of the * operation. * @throws NullPointerException if the specified queue is null */ public boolean offerAll( QueueBuffer<E> queue ) { // first see if the collection will be changed if (queue.isEmpty()) return false; // first see if this collection is empty if (tail == null) { // this collection is empty and the head of // the specified queue will become the head of this queue head = queue.head; } else { // this collection is not empty, we add // the specified queue to the tail tail.next = queue.head; } // the tail of the specified queue will become the tail of this queue tail = queue.tail; // the queue has no element after this operation queue.head = queue.tail = null; // now we give the specified queue our cached elements as exchange if (cacheHead != null) { // see if the specified queue has no cached elements if (queue.cacheTail == null) { // no it hasn't, thus we can copy them 1:1 queue.cacheHead = cacheHead; queue.cacheTail = cacheTail; } else { // the specified queue has already elements in it's cache queue.cacheTail.next = cacheHead; queue.cacheTail = cacheTail; } // this cache is empty as result of this operation cacheHead = cacheTail = null; } // update the modification counters of both queues queue.modcount ++; modcount ++; // the collection has changed as result of this operation return true; } /** * The operation is not supported. * @throws UnsupportedOperationException */ public boolean addAll( Collection<? extends E> c ) { throw new UnsupportedOperationException(); } /** * The operation is not supported. * @throws UnsupportedOperationException */ public boolean removeAll( Collection<?> c ) { throw new UnsupportedOperationException(); } /** * The operation is not supported. * @throws UnsupportedOperationException */ public boolean retainAll( Collection<?> c ) { throw new UnsupportedOperationException(); } /** * Clears the cache of unused elements. */ public void clearCache() { cacheHead = cacheTail = null; } /** * Removes all of the elements from this collection. * This collection will be empty after this method. * * If the collection was not empty then the element * containeres will be recycled. */ public void clear() { if (isEmpty()) return; // we do not set head.next to null here // so that each element (head,...,tail) // will be added to the cache cache(head); head = tail = null; modcount++; } /** * Adds the specified element and each of it's * successors to the cache. Set e.next=null if * only e should be added to the cache. * * The operation will set the Element.data field * of e and it's successors to null. * * @param e the element(s) that should be recycled */ private final void cache(Element<E> e) { // set Element.data fields to null Element<E> newTail = e; while (newTail.next!=null) { newTail.data = null; newTail = newTail.next; } newTail.data = null; // see if the cache is empty or not if (cacheTail != null) cacheTail.next = e; else cacheHead = e; cacheTail = newTail; } }