blob: a8a741aba7623313595740ea2c3ea80950de6bf8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2004 IBM Corporation and others.
* 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
*
* Contributors:
* IBM - Initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.jobs;
import java.util.*;
/**
* A Queue of objects.
*/
public class Queue {
protected Object[] elements;
protected int head;
protected boolean reuse;
protected int tail;
public Queue() {
this(20, false);
}
/**
* The parameter reuse indicates what do you want to happen with
* the object reference when you remove it from the queue. If
* reuse is false the queue no longer holds a reference to the
* object when it is removed. If reuse is true you can use the
* method getNextAvailableObject to get an used object, set its
* new values and add it again to the queue.
*/
public Queue(int size, boolean reuse) {
elements = new Object[size];
head = tail = 0;
this.reuse = reuse;
}
/**
* Adds an object to the tail of the queue.
*/
public void enqueue(Object element) {
int newTail = increment(tail);
if (newTail == head) {
grow();
newTail = tail + 1;
}
elements[tail] = element;
tail = newTail;
}
public void clear() {
if (tail >= head) {
for (int i = head; i < tail; i++)
elements[i] = null;
} else {
for (int i = head; i < elements.length; i++)
elements[i] = null;
for (int i = 0; i < tail; i++)
elements[i] = null;
}
tail = head = 0;
}
public boolean contains(Object o) {
return get(o) != null;
}
/**
* This method does not affect the queue itself. It is only a
* helper to decrement an index in the queue.
*/
public int decrement(int index) {
return (index == 0) ? (elements.length - 1) : index - 1;
}
public Object elementAt(int index) {
return elements[index];
}
public Iterator elements() {
/**/
if (isEmpty())
return new ArrayList(0).iterator();
/* if head < tail we can use the same array */
if (head <= tail)
return Arrays.asList(elements).iterator();
/* otherwise we need to create a new array */
Object[] newElements = new Object[size()];
int end = (elements.length - head);
System.arraycopy(elements, head, newElements, 0, end);
System.arraycopy(elements, 0, newElements, end, tail);
return Arrays.asList(newElements).iterator();
}
public Object get(Object o) {
int index = head;
while (index != tail) {
if (elements[index].equals(o))
return elements[index];
index = increment(index);
}
return null;
}
/**
* Removes the given object from the queue. Shifts the underlying array.
*/
public boolean remove(Object o) {
int index = head;
//find the object to remove
while (index != tail) {
if (elements[index].equals(o))
break;
index = increment(index);
}
//if element wasn't found, return
if (index == tail)
return false;
//store a reference to it (needed for reuse of objects)
Object toRemove = elements[index];
int nextIndex = -1;
while (index != tail) {
nextIndex = increment(index);
if (nextIndex != tail)
elements[index] = elements[nextIndex];
index = nextIndex;
}
//decrement tail
tail = decrement(tail);
//if objects are reused, transfer the reference that is removed to the end of the queue
//otherwise set the element after the last one to null (to avoid duplicate references)
elements[tail] = reuse ? toRemove : null;
return true;
}
/**
* Returns an object that has been removed from the queue, if any.
* The intention is to support reuse of objects that have already
* been processed and removed from the queue. Returns null if there
* are no available objects.
*/
public Object getNextAvailableObject() {
int index = tail;
while (index != head) {
if (elements[index] != null) {
Object result = elements[index];
elements[index] = null;
return result;
}
index = increment(index);
}
return null;
}
protected void grow() {
int newSize = (int) (elements.length * 1.5);
Object[] newElements = new Object[newSize];
if (tail >= head)
System.arraycopy(elements, head, newElements, head, size());
else {
int newHead = newSize - (elements.length - head);
System.arraycopy(elements, 0, newElements, 0, tail + 1);
System.arraycopy(elements, head, newElements, newHead, (newSize - newHead));
head = newHead;
}
elements = newElements;
}
/**
* This method does not affect the queue itself. It is only a
* helper to increment an index in the queue.
*/
public int increment(int index) {
return (index == (elements.length - 1)) ? 0 : index + 1;
}
public int indexOf(Object target) {
if (tail >= head) {
for (int i = head; i < tail; i++)
if (target.equals(elements[i]))
return i;
} else {
for (int i = head; i < elements.length; i++)
if (target.equals(elements[i]))
return i;
for (int i = 0; i < tail; i++)
if (target.equals(elements[i]))
return i;
}
return -1;
}
public boolean isEmpty() {
return tail == head;
}
public Object peek() {
return elements[head];
}
public Object peekTail() {
return elements[decrement(tail)];
}
/**
* Removes an returns the item at the head of the queue.
*/
public Object dequeue() {
if (isEmpty())
return null;
Object result = peek();
if (!reuse)
elements[head] = null;
head = increment(head);
return result;
}
public Object removeTail() {
Object result = peekTail();
tail = decrement(tail);
if (!reuse)
elements[tail] = null;
return result;
}
public void reset() {
tail = head = 0;
}
public int size() {
return tail > head ? (tail - head) : ((elements.length - head) + tail);
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("["); //$NON-NLS-1$
if (!isEmpty()) {
Iterator it = elements();
while (true) {
sb.append(it.next());
if (it.hasNext())
sb.append(", "); //$NON-NLS-1$
else
break;
}
}
sb.append("]"); //$NON-NLS-1$
return sb.toString();
}
}