/*
* Bitronix Transaction Manager
*
* Copyright (c) 2010, Bitronix Software.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package bitronix.tm.utils;
import java.util.*;
/**
* Positional object container. Objects can be added to a scheduler at a certain position (or priority) and can be
* retrieved later on in their position + added order. All the objects of a scheduler can be iterated in order or
* objects of a cetain position can be retrieved for iteration.
*
* @author lorban
*/
public class Scheduler<T> implements Iterable<T> {
public static final Integer DEFAULT_POSITION = 0;
public static final Integer ALWAYS_FIRST_POSITION = Integer.MIN_VALUE;
public static final Integer ALWAYS_LAST_POSITION = Integer.MAX_VALUE;
private List<Integer> keys = new ArrayList<Integer>();
private Map<Integer, List<T>> objects = new TreeMap<Integer, List<T>>();
private int size = 0;
public Scheduler() {
}
public synchronized void add(T obj, Integer position) {
List<T> list = objects.get(position);
if (list == null) {
if (!keys.contains(position)) {
keys.add(position);
Collections.sort(keys);
}
list = new ArrayList<T>();
objects.put(position, list);
}
list.add(obj);
size++;
}
public synchronized void remove(T obj) {
Iterator<T> it = iterator();
while (it.hasNext()) {
T o = it.next();
if (o == obj) {
it.remove();
return;
}
}
throw new NoSuchElementException("no such element: " + obj);
}
public synchronized SortedSet<Integer> getNaturalOrderPositions() {
return new TreeSet<Integer>(objects.keySet());
}
public synchronized SortedSet<Integer> getReverseOrderPositions() {
TreeSet<Integer> result = new TreeSet<Integer>(Collections.reverseOrder());
result.addAll(getNaturalOrderPositions());
return result;
}
public synchronized List<T> getByNaturalOrderForPosition(Integer position) {
return objects.get(position);
}
public synchronized List<T> getByReverseOrderForPosition(Integer position) {
List<T> result = new ArrayList<T>(getByNaturalOrderForPosition(position));
Collections.reverse(result);
return result;
}
public synchronized int size() {
return size;
}
public Iterator<T> iterator() {
return new SchedulerNaturalOrderIterator();
}
public Iterator<T> reverseIterator() {
return new SchedulerReverseOrderIterator();
}
public String toString() {
return "a Scheduler with " + size() + " object(s) in " + getNaturalOrderPositions().size() + " position(s)";
}
/**
* This iterator supports in-flight updates of the iterated object.
*/
private final class SchedulerNaturalOrderIterator implements Iterator<T> {
private int nextKeyIndex;
private List<T> objectsOfCurrentKey;
private int objectsOfCurrentKeyIndex;
private SchedulerNaturalOrderIterator() {
this.nextKeyIndex = 0;
}
public void remove() {
synchronized (Scheduler.this) {
if (objectsOfCurrentKey == null)
throw new NoSuchElementException("iterator not yet placed on an element");
objectsOfCurrentKeyIndex--;
objectsOfCurrentKey.remove(objectsOfCurrentKeyIndex);
if (objectsOfCurrentKey.size() == 0) {
// there are no more objects in the current position's list -> remove it
nextKeyIndex--;
Integer key = Scheduler.this.keys.get(nextKeyIndex);
Scheduler.this.keys.remove(nextKeyIndex);
Scheduler.this.objects.remove(key);
objectsOfCurrentKey = null;
}
Scheduler.this.size--;
}
}
public boolean hasNext() {
synchronized (Scheduler.this) {
if (objectsOfCurrentKey == null || objectsOfCurrentKeyIndex >= objectsOfCurrentKey.size()) {
// we reached the end of the current position's list
if (nextKeyIndex < Scheduler.this.keys.size()) {
// there is another position after this one
Integer currentKey = Scheduler.this.keys.get(nextKeyIndex++);
objectsOfCurrentKey = Scheduler.this.objects.get(currentKey);
objectsOfCurrentKeyIndex = 0;
return true;
} else {
// there is no other position after this one
return false;
}
}
// there are still objects in the current position's list
return true;
}
}
public T next() {
synchronized (Scheduler.this) {
if (!hasNext())
throw new NoSuchElementException("iterator bounds reached");
return objectsOfCurrentKey.get(objectsOfCurrentKeyIndex++);
}
}
}
/**
* This iterator supports in-flight updates of the iterated object.
*/
private final class SchedulerReverseOrderIterator implements Iterator<T> {
private int nextKeyIndex;
private List<T> objectsOfCurrentKey;
private int objectsOfCurrentKeyIndex;
private SchedulerReverseOrderIterator() {
synchronized (Scheduler.this) {
this.nextKeyIndex = Scheduler.this.keys.size() -1;
}
}
public void remove() {
synchronized (Scheduler.this) {
if (objectsOfCurrentKey == null)
throw new NoSuchElementException("iterator not yet placed on an element");
objectsOfCurrentKeyIndex--;
objectsOfCurrentKey.remove(objectsOfCurrentKeyIndex);
if (objectsOfCurrentKey.size() == 0) {
// there are no more objects in the current position's list -> remove it
Integer key = Scheduler.this.keys.get(nextKeyIndex+1);
Scheduler.this.keys.remove(nextKeyIndex+1);
Scheduler.this.objects.remove(key);
objectsOfCurrentKey = null;
}
Scheduler.this.size--;
}
}
public boolean hasNext() {
synchronized (Scheduler.this) {
if (objectsOfCurrentKey == null || objectsOfCurrentKeyIndex >= objectsOfCurrentKey.size()) {
// we reached the end of the current position's list
if (nextKeyIndex >= 0) {
// there is another position after this one
Integer currentKey = Scheduler.this.keys.get(nextKeyIndex--);
objectsOfCurrentKey = Scheduler.this.objects.get(currentKey);
objectsOfCurrentKeyIndex = 0;
return true;
} else {
// there is no other position after this one
return false;
}
}
// there are still objects in the current position's list
return true;
}
}
public T next() {
synchronized (Scheduler.this) {
if (!hasNext())
throw new NoSuchElementException("iterator bounds reached");
return objectsOfCurrentKey.get(objectsOfCurrentKeyIndex++);
}
}
}
}