blob: ea697c28efc2e62e70c64ee100561af22ed3ae12 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.codehaus.groovy.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* A queue that stores values wrapped in a Reference, the type of which is
* determined by the provided {@link ReferenceBundle}. References stored
* in this queue will be removed when reference processing occurs.
* <p>
* This queue is backed by a {@link ConcurrentLinkedQueue} and is thread safe.
* The iterator will only return non-null values (reachable) and is based on
* the "weakly consistent" iterator of the underlying {@link ConcurrentLinkedQueue}.
*
* @param <T> the type of values to store
*/
public class ManagedConcurrentLinkedQueue<T> implements Iterable<T> {
private final ReferenceBundle bundle;
private final ConcurrentLinkedQueue<Element<T>> queue;
/**
* Creates an empty ManagedConcurrentLinkedQueue that will use the provided
* {@code ReferenceBundle} to store values as the given Reference
* type.
*
* @param bundle used to create the appropriate Reference type
* for the values stored
*/
public ManagedConcurrentLinkedQueue(ReferenceBundle bundle) {
this.bundle = bundle;
this.queue = new ConcurrentLinkedQueue<Element<T>>();
}
/**
* Adds the specified value to the queue.
*
* @param value the value to add
*/
public void add(T value) {
Element<T> e = new Element<T>(value);
queue.offer(e);
}
/**
* Returns {@code true} if this queue contains no elements.
* <p>
* This method does not check the elements to verify they contain
* non-null reference values.
*/
public boolean isEmpty() {
return queue.isEmpty();
}
/**
* Returns an array containing all values from this queue in the sequence they
* were added.
*
* @param tArray the array to populate if big enough, else a new array with
* the same runtime type
* @return an array containing all non-null values in this queue
*/
public T[] toArray(T[] tArray) {
return values().toArray(tArray);
}
/**
* Returns a list containing all values from this queue in the
* sequence they were added.
*/
public List<T> values() {
List<T> result = new ArrayList<T>();
for (T t : this) {
result.add(t);
}
return result;
}
/**
* Returns an iterator over all non-null values in this queue. The values should be
* returned in the order they were added.
*/
@Override
public Iterator<T> iterator() {
return new Itr(queue.iterator());
}
private class Element<V> extends ManagedReference<V> {
Element(V value) {
super(bundle, value);
}
@Override
public void finalizeReference() {
queue.remove(this);
super.finalizeReference();
}
}
private class Itr implements Iterator<T> {
final Iterator<Element<T>> wrapped;
T value;
Element<T> current;
boolean exhausted;
Itr(Iterator<Element<T>> wrapped) {
this.wrapped = wrapped;
}
@Override
public boolean hasNext() {
if (!exhausted && value == null) {
advance();
}
return value != null;
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
T next = value;
value = null;
return next;
}
@Override
public void remove() {
if (current == null || value != null) {
throw new IllegalStateException("Next method has not been called");
}
wrapped.remove();
current = null;
}
private void advance() {
while (wrapped.hasNext()) {
Element<T> e = wrapped.next();
T v = e.get();
if (v != null) {
current = e;
value = v;
return;
}
wrapped.remove();
}
value = null;
current = null;
exhausted = true;
}
}
}