/* * 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 { public static final int DEFAULT_POSITION = 0; public static final int ALWAYS_FIRST_POSITION = Integer.MIN_VALUE; public static final int ALWAYS_LAST_POSITION = Integer.MAX_VALUE; public static final Object DEFAULT_POSITION_KEY = new Integer(DEFAULT_POSITION); public static final Object ALWAYS_FIRST_POSITION_KEY = new Integer(ALWAYS_FIRST_POSITION); public static final Object ALWAYS_LAST_POSITION_KEY = new Integer(ALWAYS_LAST_POSITION); private List keys = new ArrayList(); private Map objects = new TreeMap(); private int size = 0; public Scheduler() { } public synchronized void add(Object obj, int position) { Integer key = new Integer(position); List synchronizationsList = (List) objects.get(key); if (synchronizationsList == null) { if (!keys.contains(key)) { keys.add(key); Collections.sort(keys); } synchronizationsList = new ArrayList(); objects.put(key, synchronizationsList); } synchronizationsList.add(obj); size++; } public synchronized void remove(Object obj) { Iterator it = iterator(); while (it.hasNext()) { Object o = it.next(); if (o == obj) { it.remove(); return; } } throw new NoSuchElementException("no such element: " + obj); } public synchronized SortedSet getNaturalOrderPositions() { return new TreeSet(objects.keySet()); } public synchronized SortedSet getReverseOrderPositions() { TreeSet result = new TreeSet(Collections.reverseOrder()); result.addAll(getNaturalOrderPositions()); return result; } public synchronized List getByNaturalOrderForPosition(Object positionKey) { return (List) objects.get(positionKey); } public synchronized List getByReverseOrderForPosition(Object positionKey) { List result = new ArrayList(getByNaturalOrderForPosition(positionKey)); Collections.reverse(result); return result; } public synchronized int size() { return size; } public Iterator iterator() { return new SchedulerNaturalOrderIterator(); } public Iterator 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 { private int nextKeyIndex; private List 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--; Object 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 = (Integer) Scheduler.this.keys.get(nextKeyIndex++); objectsOfCurrentKey = (List) 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 Object 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 { private int nextKeyIndex; private List objectsOfCurrentKey; private int objectsOfCurrentKeyIndex; private SchedulerReverseOrderIterator() { 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 Object 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 = (Integer) Scheduler.this.keys.get(nextKeyIndex--); objectsOfCurrentKey = (List) 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 Object next() { synchronized (Scheduler.this) { if (!hasNext()) throw new NoSuchElementException("iterator bounds reached"); return objectsOfCurrentKey.get(objectsOfCurrentKeyIndex++); } } } }