diff options
Diffstat (limited to 'jpa/plugins/org.eclipse.jpt.utility')
18 files changed, 1460 insertions, 194 deletions
diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/AsynchronousCommandExecutor.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/AsynchronousCommandExecutor.java new file mode 100644 index 0000000000..00baa2efd0 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/AsynchronousCommandExecutor.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal; + +import org.eclipse.jpt.utility.Command; + +/** + * This command executor will dispatch commands to be executed in a separate + * thread, allowing calls to {@link CommandExecutor#execute(Command)} to return + * immediately. + * <p> + * <strong>NB:</strong> The client-supplied commands should handle any + * exception appropriately (e.g. log the exception and return gracefully) so + * the command execution thread can continue executing. + */ +public class AsynchronousCommandExecutor + implements CallbackStatefulCommandExecutor +{ + /** + * This command queue is shared with the command execution/consumer thread. + * Adding a command to it will trigger the command to be executed by the + * command execution thread or, if another command is currently executing, + * to execute the new command once the currently executing command has + * finished executing. + */ + final SynchronizedQueue<Command> commands = new SynchronizedQueue<Command>(); + + /** + * Most of the thread-related behavior is delegated to this coordinator. + */ + private final ConsumerThreadCoordinator consumerThreadCoordinator; + + private final ListenerList<Listener> listenerList = new ListenerList<Listener>(Listener.class); + + + // ********** construction ********** + + /** + * Construct an asynchronous command executor. + * Allow the command execution thread(s) to be assigned JDK-generated names. + */ + public AsynchronousCommandExecutor() { + this(null); + } + + /** + * Construct an asynchronous command executor. + * Assign the command execution thread(s) the specified name. + */ + public AsynchronousCommandExecutor(String threadName) { + super(); + this.consumerThreadCoordinator = new ConsumerThreadCoordinator(this.buildConsumer(), threadName); + } + + private ConsumerThreadCoordinator.Consumer buildConsumer() { + return new Consumer(); + } + + + // ********** CallbackStatefulCommandExecutor implementation ********** + + /** + * Build and start the command execution/consumer thread. + * <p> + * Note: We don't clear the command queue here; so if a command has been + * added to the queue <em>before</em> getting here, the first command will + * be executed promptly (albeit, asynchronously). + * The command queue will be non-empty if:<ul> + * <li>{@link #execute(Command)} was called after the command executor was + * constructed but before {@link #start()} was called; or + * <li>{@link #execute(Command)} was called after {@link #stop()} was called + * but before {@link #start()} was called (to restart the command executor); or + * <li>{@link #stop()} was called when there were still outstanding commands + * remaining in the command queue + * </ul> + */ + public void start() { + this.consumerThreadCoordinator.start(); + } + + /** + * Put the specified command on the command queue, to be consumed by the + * command execution thread. + */ + public void execute(Command command) { + this.commands.enqueue(command); + } + + /** + * Interrupt the command execution thread so that it stops executing at the + * end of the current command. Suspend the current thread until + * the command execution thread is finished executing. If any uncaught + * exceptions were thrown while the execution thread was executing, + * wrap them in a composite exception and throw the composite exception. + */ + public void stop() { + this.consumerThreadCoordinator.stop(); + } + + public void addListener(Listener listener) { + this.listenerList.add(listener); + } + + public void removeListener(Listener listener) { + this.listenerList.remove(listener); + } + + /** + * Notify our listeners. + */ + /* private */ void commandExecuted(Command command) { + for (Listener listener : this.listenerList.getListeners()) { + listener.commandExecuted(command); + } + } + + + // ********** consumer ********** + + /** + * This implementation of {@link ConsumerThreadCoordinator.Consumer} + * will execute the commands enqueued by the asynchronous command executor. + * It will wait until the shared command queue is non-empty to begin executing the + * commands in the queue. Once a comand is executed, the thread will quiesce until + * another command is placed in the command queue. If a new command is + * enqueued during the execution of another command (either recursively by + * the command itself or by another thread), + * the new command will be executed immediately after the currently + * executing command is finished. + * Stop the thread by calling {@link Thread#interrupt()}. + */ + class Consumer + implements ConsumerThreadCoordinator.Consumer + { + Consumer() { + super(); + } + + /** + * Wait until a command has been placed in the queue. + */ + public void waitForProducer() throws InterruptedException { + AsynchronousCommandExecutor.this.commands.waitUntilNotEmpty(); + } + + /** + * Execute the first command in the queue and notify our listeners. + */ + public void execute() { + Command command = AsynchronousCommandExecutor.this.commands.dequeue(); + command.execute(); + AsynchronousCommandExecutor.this.commandExecuted(command); + } + + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/CallbackStatefulCommandExecutor.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/CallbackStatefulCommandExecutor.java new file mode 100644 index 0000000000..d9fb9a1c6b --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/CallbackStatefulCommandExecutor.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal; + +import java.util.EventListener; + +import org.eclipse.jpt.utility.Command; + +/** + * This command executor notifies clients commands are executed. + */ +public interface CallbackStatefulCommandExecutor + extends StatefulCommandExecutor +{ + /** + * Add the specified listener to be notified whenever a command has been + * executed. + * @see #removeListener(Listener) + */ + void addListener(Listener listener); + + /** + * Remove the specified listener. + * @see #addListener(Listener) + */ + void removeListener(Listener listener); + + + // ********** listener ********** + + /** + * Interface implemented by listeners to be notified whenever a + * command is executed. + */ + public interface Listener + extends EventListener + { + /** + * The specified command was executed. + */ + void commandExecuted(Command command); + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/CompositeCommand.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/CompositeCommand.java new file mode 100644 index 0000000000..ff603eb0b7 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/CompositeCommand.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal; + +import org.eclipse.jpt.utility.Command; + +/** + * <code>CompositeCommand</code> provides support for treating a collection of + * {@link Command}s as a single command. + */ +public class CompositeCommand + implements Command +{ + private final Iterable<Command> commands; + + public CompositeCommand(Iterable<Command> commands) { + super(); + this.commands = commands; + } + + public void execute() { + for (Command command : this.commands) { + command.execute(); + } + } + + @Override + public String toString() { + return StringTools.buildToStringFor(this, this.commands); + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/ConsumerThreadCoordinator.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/ConsumerThreadCoordinator.java new file mode 100644 index 0000000000..abc9e71456 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/ConsumerThreadCoordinator.java @@ -0,0 +1,225 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal; + +import java.util.Vector; + +/** + * A <code>ConsumerThreadCoordinator</code> controls the creation, + * starting, and stopping of a general purpose "consumer" thread. Construct + * the coordinator with a {@link Consumer} that both waits for the producer + * to "produce" something to "consume" and, once the wait is over, + * "consumes" whatever is available. + * <p> + * <strong>NB:</strong> The client-supplied consumer should handle any + * exception appropriately (e.g. log the exception and return gracefully) so + * the thread can continue executing. + */ +public class ConsumerThreadCoordinator { + /** + * The runnable passed to the consumer thread each time it is built. + */ + private final Runnable runnable; + + /** + * Optional, client-supplied name for the consumer thread. + * If null, the JDK assigns a name. + */ + private final String threadName; + + /** + * The consumer is executed on this thread. A new thread is built + * for every start/stop cycle (since a thread cannot be started more than + * once). + */ + private volatile Thread thread; + + /** + * A list of the uncaught exceptions thrown by the consumer + * during the current start/stop cycle. + */ + final Vector<Throwable> exceptions = new Vector<Throwable>(); + + + // ********** construction ********** + + /** + * Construct a consumer thread coordinator for the specified consumer. + * Allow the consumer thread(s) to be assigned JDK-generated names. + */ + public ConsumerThreadCoordinator(Consumer consumer) { + this(consumer, null); + } + + /** + * Construct a consumer thread coordinator for the specified consumer. + * Assign the consumer thread(s) the specified name. + */ + public ConsumerThreadCoordinator(Consumer consumer, String threadName) { + super(); + this.runnable = this.buildRunnable(consumer); + this.threadName = threadName; + } + + private Runnable buildRunnable(Consumer consumer) { + return new RunnableConsumer(consumer); + } + + + // ********** Lifecycle support ********** + + /** + * Build and start the consumer thread. + */ + public synchronized void start() { + if (this.thread != null) { + throw new IllegalStateException("Not stopped."); //$NON-NLS-1$ + } + this.thread = this.buildThread(); + this.thread.start(); + } + + private Thread buildThread() { + Thread t = new Thread(this.runnable); + if (this.threadName != null) { + t.setName(this.threadName); + } + return t; + } + + /** + * Interrupt the consumer thread so that it stops executing at the + * end of its current iteration. Suspend the current thread until + * the consumer thread is finished executing. If any uncaught + * exceptions were thrown while the consumer thread was executing, + * wrap them in a composite exception and throw the composite exception. + */ + public synchronized void stop() { + if (this.thread == null) { + throw new IllegalStateException("Not started."); //$NON-NLS-1$ + } + this.thread.interrupt(); + try { + this.thread.join(); + } catch (InterruptedException ex) { + // the thread that called #stop() was interrupted while waiting to + // join the consumer thread - ignore; + // 'thread' is still "interrupted", so its #run() loop will still stop + // after its current execution - we just won't wait around for it... + } + this.thread = null; + + if (this.exceptions.size() > 0) { + Throwable[] temp = this.exceptions.toArray(new Throwable[this.exceptions.size()]); + this.exceptions.clear(); + throw new CompositeException(temp); + } + } + + @Override + public String toString() { + return StringTools.buildToStringFor(this, this.thread); + } + + + // ********** consumer thread runnable ********** + + /** + * This implementation of {@link Runnable} is a long-running consumer that + * will repeatedly execute the consumer {@link Consumer#execute()} method. + * With each iteration, the consumer thread will wait + * until the other consumer method, {@link Consumer#waitForProducer()}, allows the + * consumer thread to proceed (i.e. there is something for the consumer to + * consume). Once {@link Consumer#execute()} is finished, the thread will quiesce + * until {@link Consumer#waitForProducer()} returns again. + * Stop the thread by calling {@link Thread#interrupt()}. + */ + private class RunnableConsumer + implements Runnable + { + /** + * Client-supplied consumer that controls waiting for something to consume + * and the consuming itself. + */ + private final Consumer consumer; + + RunnableConsumer(Consumer consumer) { + super(); + this.consumer = consumer; + } + + /** + * Loop while this thread has not been interrupted by another thread. + * In each loop: Pause execution until {@link Consumer#waitForProducer()} + * allows us to proceed. + * <p> + * If this thread is interrupted <em>during</em> {@link Consumer#execute()}, + * the call to {@link Thread#interrupted()} will stop the loop. If this thread is + * interrupted during the call to {@link Consumer#waitForProducer()}, + * we will catch the {@link InterruptedException} and stop the loop also. + */ + public void run() { + while ( ! Thread.interrupted()) { + try { + this.consumer.waitForProducer(); + } catch (InterruptedException ex) { + // we were interrupted while waiting, must be Quittin' Time + return; + } + this.execute(); + } + } + + /** + * Execute the consumer {@link Consumer#execute()} method. + * Do not allow any unhandled exceptions to kill the thread. + * Store them up for later pain. + * @see ConsumerThreadCoordinator#stop() + */ + private void execute() { + try { + this.execute_(); + } catch (Throwable ex) { + ConsumerThreadCoordinator.this.exceptions.add(ex); + } + } + + /** + * Subclass-implemented behavior: consume stuff. + */ + private void execute_() { + this.consumer.execute(); + } + + } + + + // ********** consumer interface ********** + + /** + * Interface implemented by clients that controls:<ul> + * <li>when the consumer thread suspends, waiting for something to consume + * <li>the consuming of whatever is being produced + * </ul> + */ + public interface Consumer { + /** + * Wait for something to consume. + * Throw an {@link InterruptedException} if the thread is interrupted. + */ + void waitForProducer() throws InterruptedException; + + /** + * Consume whatever is currently available. + */ + void execute(); + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/Queue.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/Queue.java new file mode 100644 index 0000000000..cf9b1f8b25 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/Queue.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal; + +import java.io.Serializable; +import java.util.NoSuchElementException; + +/** + * Interface defining the classic queue behavior, + * without the backdoors allowed by {@link java.util.Queue}. + * + * @param <E> the type of elements contained by the queue + */ +public interface Queue<E> { + + /** + * "Enqueue" the specified item to the tail of the queue. + */ + void enqueue(E o); + + /** + * "Dequeue" an item from the head of the queue. + */ + E dequeue(); + + /** + * Return the item on the head of the queue + * without removing it from the queue. + */ + E peek(); + + /** + * Return whether the queue is empty. + */ + boolean isEmpty(); + + + final class Empty<E> implements Queue<E>, Serializable { + @SuppressWarnings("unchecked") + public static final Queue INSTANCE = new Empty(); + @SuppressWarnings("unchecked") + public static <T> Queue<T> instance() { + return INSTANCE; + } + // ensure single instance + private Empty() { + super(); + } + public void enqueue(E o) { + throw new UnsupportedOperationException(); + } + public E dequeue() { + throw new NoSuchElementException(); + } + public E peek() { + throw new NoSuchElementException(); + } + public boolean isEmpty() { + return true; + } + private static final long serialVersionUID = 1L; + private Object readResolve() { + // replace this object with the singleton + return INSTANCE; + } + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SimpleCommandExecutor.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SimpleCommandExecutor.java new file mode 100644 index 0000000000..de883b3e8b --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SimpleCommandExecutor.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal; + +import org.eclipse.jpt.utility.Command; + +/** + * Straightforward implementation of {@link CallbackStatefulCommandExecutor}. + */ +public class SimpleCommandExecutor + implements CallbackStatefulCommandExecutor +{ + private boolean active = false; + private final ListenerList<Listener> listenerList = new ListenerList<Listener>(Listener.class); + + public SimpleCommandExecutor() { + super(); + } + + public void start() { + if (this.active) { + throw new IllegalStateException("Not stopped."); //$NON-NLS-1$ + } + this.active = true; + } + + public void execute(Command command) { + if (this.active) { + command.execute(); + this.commandExecuted(command); + } + } + + public void stop() { + if ( ! this.active) { + throw new IllegalStateException("Not started."); //$NON-NLS-1$ + } + this.active = false; + } + + public void addListener(Listener listener) { + this.listenerList.add(listener); + } + + public void removeListener(Listener listener) { + this.listenerList.remove(listener); + } + + /** + * Notify our listeners. + */ + private void commandExecuted(Command command) { + for (Listener listener : this.listenerList.getListeners()) { + listener.commandExecuted(command); + } + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SimpleQueue.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SimpleQueue.java new file mode 100644 index 0000000000..89a73e1454 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SimpleQueue.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal; + +import java.io.Serializable; +import java.util.Collection; +import java.util.LinkedList; + +/** + * Straightforward implementation of the {@link Queue} interface. + */ +public class SimpleQueue<E> + implements Queue<E>, Cloneable, Serializable +{ + private LinkedList<E> elements; + + private static final long serialVersionUID = 1L; + + + // ********** constructors ********** + + /** + * Construct an empty queue. + */ + public SimpleQueue() { + super(); + this.elements = new LinkedList<E>(); + } + + /** + * Construct a queue containing the elements of the specified + * collection. The queue will dequeue its elements in the same + * order they are returned by the collection's iterator (i.e. the + * first element returned by the collection's iterator will be the + * first element returned by {@link #dequeue()}). + */ + public SimpleQueue(Collection<? extends E> c) { + super(); + this.elements = new LinkedList<E>(c); + } + + + // ********** Queue implementation ********** + + public void enqueue(E o) { + this.elements.addLast(o); + } + + public E dequeue() { + return this.elements.removeFirst(); + } + + public E peek() { + return this.elements.getFirst(); + } + + public boolean isEmpty() { + return this.elements.isEmpty(); + } + + + // ********** Cloneable implementation ********** + + @Override + public SimpleQueue<E> clone() { + try { + @SuppressWarnings("unchecked") + SimpleQueue<E> clone = (SimpleQueue<E>) super.clone(); + @SuppressWarnings("unchecked") + LinkedList<E> ll = (LinkedList<E>) this.elements.clone(); + clone.elements = ll; + return clone; + } catch (CloneNotSupportedException ex) { + throw new InternalError(); + } + } + + @Override + public String toString() { + return StringTools.buildToStringFor(this, this.peek()); + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SimpleStack.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SimpleStack.java index 5df2f99e40..ffdea0c519 100644 --- a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SimpleStack.java +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SimpleStack.java @@ -43,16 +43,16 @@ public class SimpleStack<E> * last element returned by the collection's iterator will be the * first element returned by {@link #pop()}). */ - public SimpleStack(Collection<? extends E> c) { + public SimpleStack(Collection<? extends E> collection) { super(); - this.elements = new LinkedList<E>(c); + this.elements = new LinkedList<E>(collection); } // ********** Stack implementation ********** - public void push(E o) { - this.elements.addLast(o); + public void push(E element) { + this.elements.addLast(element); } public E pop() { @@ -76,7 +76,7 @@ public class SimpleStack<E> } - // ********** Cloneable implementation ********** + // ********** standard methods ********** @Override public SimpleStack<E> clone() { @@ -92,4 +92,9 @@ public class SimpleStack<E> } } + @Override + public String toString() { + return StringTools.buildToStringFor(this, this.peek()); + } + } diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/Stack.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/Stack.java index 4c3ac1bca6..50ba19877a 100644 --- a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/Stack.java +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/Stack.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2008 Oracle. All rights reserved. + * Copyright (c) 2007, 2009 Oracle. 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. @@ -14,15 +14,16 @@ import java.util.EmptyStackException; /** * Interface defining the classic stack behavior, - * without the backdoors allowed by java.util.Stack. - * E is the type of elements contained by the Stack. + * without the backdoors allowed by {@link java.util.Stack}. + * + * @param <E> the type of elements contained by the stack */ public interface Stack<E> { /** * "Push" the specified item on to the top of the stack. */ - void push(E o); + void push(E element); /** * "Pop" an item from the top of the stack. @@ -52,7 +53,7 @@ public interface Stack<E> { private Empty() { super(); } - public void push(E o) { + public void push(E element) { throw new UnsupportedOperationException(); } public E pop() { diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/StatefulCommandExecutor.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/StatefulCommandExecutor.java new file mode 100644 index 0000000000..d4fd37a110 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/StatefulCommandExecutor.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal; + +import org.eclipse.jpt.utility.CommandExecutor; + +/** + * This interface allows clients to control how a command is executed. + * This is useful when the server provides the command but the client provides + * the context (e.g. the client would like to dispatch the command to the UI + * thread). + */ +public interface StatefulCommandExecutor + extends CommandExecutor +{ + /** + * Start the command executor. + */ + void start(); + + /** + * Stop the command executor. + */ + void stop(); + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SynchronizedQueue.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SynchronizedQueue.java new file mode 100644 index 0000000000..c35a4aa133 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SynchronizedQueue.java @@ -0,0 +1,348 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal; + +import java.io.Serializable; +import java.util.NoSuchElementException; + +import org.eclipse.jpt.utility.Command; + +/** + * Thread-safe implementation of the {@link Queue} interface. + * This also provides protocol for suspending a thread until the + * queue is empty or not empty, with optional time-outs. + */ +public class SynchronizedQueue<E> + implements Queue<E>, Serializable +{ + /** Backing queue. */ + private final Queue<E> queue; + + /** Object to synchronize on. */ + private final Object mutex; + + private static final long serialVersionUID = 1L; + + + // ********** constructors ********** + + /** + * Construct a synchronized queue that wraps the + * specified queue and locks on the specified mutex. + */ + public SynchronizedQueue(Queue<E> queue, Object mutex) { + super(); + if (queue == null) { + throw new NullPointerException(); + } + this.queue = queue; + this.mutex = mutex; + } + + /** + * Construct a synchronized queue that wraps the + * specified queue and locks on itself. + */ + public SynchronizedQueue(Queue<E> queue) { + super(); + if (queue == null) { + throw new NullPointerException(); + } + this.queue = queue; + this.mutex = this; + } + + /** + * Construct an empty synchronized queue that locks on the specified mutex. + */ + public SynchronizedQueue(Object mutex) { + this(new SimpleQueue<E>(), mutex); + } + + /** + * Construct an empty synchronized queue that locks on itself. + */ + public SynchronizedQueue() { + this(new SimpleQueue<E>()); + } + + + // ********** Queue implementation ********** + + public void enqueue(E element) { + synchronized (this.mutex) { + this.enqueue_(element); + } + } + + /** + * Pre-condition: synchronized + */ + private void enqueue_(E element) { + this.queue.enqueue(element); + this.mutex.notifyAll(); + } + + public E dequeue() { + synchronized (this.mutex) { + return this.dequeue_(); + } + } + + /** + * Pre-condition: synchronized + */ + private E dequeue_() { + E element = this.queue.dequeue(); + this.mutex.notifyAll(); + return element; + } + + public E peek() { + synchronized (this.mutex) { + return this.queue.peek(); + } + } + + public boolean isEmpty() { + synchronized (this.mutex) { + return this.queue.isEmpty(); + } + } + + + // ********** indefinite waits ********** + + /** + * Suspend the current thread until the queue's empty status changes + * to the specified value. + */ + public void waitUntilEmptyIs(boolean empty) throws InterruptedException { + synchronized (this.mutex) { + this.waitUntilEmptyIs_(empty); + } + } + + /** + * Pre-condition: synchronized + */ + private void waitUntilEmptyIs_(boolean empty) throws InterruptedException { + while (this.queue.isEmpty() != empty) { + this.mutex.wait(); + } + } + + /** + * Suspend the current thread until the queue is empty. + */ + public void waitUntilEmpty() throws InterruptedException { + this.waitUntilEmptyIs(true); + } + + /** + * Suspend the current thread until the queue has something on it. + */ + public void waitUntilNotEmpty() throws InterruptedException { + this.waitUntilEmptyIs(false); + } + + /** + * Suspend the current thread until the queue is empty, + * then "enqueue" the specified item to the tail of the queue + * and continue executing. + */ + public void waitToEnqueue(E element) throws InterruptedException { + synchronized (this.mutex) { + this.waitUntilEmptyIs_(true); + this.enqueue_(element); + } + } + + /** + * Suspend the current thread until the queue has something on it, + * then "dequeue" an item from the head of the queue and return it. + */ + public Object waitToDequeue() throws InterruptedException { + synchronized (this.mutex) { + this.waitUntilEmptyIs_(false); + return this.dequeue_(); + } + } + + + // ********** timed waits ********** + + /** + * Suspend the current thread until the queue's empty status changes + * to the specified value or the specified time-out occurs. + * The time-out is specified in milliseconds. Return <code>true</code> if the specified + * empty status was achieved; return <code>false</code> if a time-out occurred. + * If the queue's empty status is already the specified value, + * return <code>true</code> immediately. + * If the time-out is zero, wait indefinitely. + */ + public boolean waitUntilEmptyIs(boolean empty, long timeout) throws InterruptedException { + synchronized (this.mutex) { + return this.waitUntilEmptyIs_(empty, timeout); + } + } + + /** + * Pre-condition: synchronized + */ + private boolean waitUntilEmptyIs_(boolean empty, long timeout) throws InterruptedException { + if (timeout == 0L) { + this.waitUntilEmptyIs_(empty); // wait indefinitely until notified + return true; // if it ever comes back, the condition was met + } + + long stop = System.currentTimeMillis() + timeout; + long remaining = timeout; + while ((this.queue.isEmpty() != empty) && (remaining > 0L)) { + this.mutex.wait(remaining); + remaining = stop - System.currentTimeMillis(); + } + return (this.queue.isEmpty() == empty); + } + + /** + * Suspend the current thread until the queue is empty + * or the specified time-out occurs. + * The time-out is specified in milliseconds. Return <code>true</code> if + * the queue is empty; return <code>false</code> if a time-out occurred. + * If the queue is already empty, return <code>true</code> immediately. + * If the time-out is zero, wait indefinitely. + */ + public boolean waitUntilEmpty(long timeout) throws InterruptedException { + return this.waitUntilEmptyIs(true, timeout); + } + + /** + * Suspend the current thread until the queue has something on it. + * or the specified time-out occurs. + * The time-out is specified in milliseconds. Return <code>true</code> if + * the queue is not empty; return <code>false</code> if a time-out occurred. + * If the queue already has something on it, return <code>true</code> immediately. + * If the time-out is zero, wait indefinitely. + */ + public boolean waitUntilNotEmpty(long timeout) throws InterruptedException { + return this.waitUntilEmptyIs(false, timeout); + } + + /** + * Suspend the current thread until the queue is empty, + * then "enqueue" the specified item to the tail of the queue + * and continue executing. If the queue is not emptied out + * before the time-out, simply continue executing without + * "enqueueing" the item. + * The time-out is specified in milliseconds. Return <code>true</code> if the + * item was enqueued; return <code>false</code> if a time-out occurred. + * If the queue is already empty, "enqueue" the specified item and + * return <code>true</code> immediately. + * If the time-out is zero, wait indefinitely. + */ + public boolean waitToEnqueue(E element, long timeout) throws InterruptedException { + synchronized (this.mutex) { + boolean success = this.waitUntilEmptyIs_(true, timeout); + if (success) { + this.enqueue_(element); + } + return success; + } + } + + /** + * Suspend the current thread until the queue has something on it, + * then "dequeue" an item from the head of the queue and return it. + * If the queue is empty and nothing is "enqueued" on to it before the + * time-out, throw a no such element exception. + * The time-out is specified in milliseconds. + * If the queue is not empty, "dequeue" an item and + * return it immediately. + * If the time-out is zero, wait indefinitely. + */ + public Object waitToDequeue(long timeout) throws InterruptedException { + synchronized (this.mutex) { + boolean success = this.waitUntilEmptyIs_(false, timeout); + if (success) { + return this.dequeue_(); + } + throw new NoSuchElementException(); + } + } + + + // ********** synchronized behavior ********** + + /** + * If the current thread is not interrupted, execute the specified command + * with the mutex locked. This is useful for initializing the queue in another + * thread. + */ + public void execute(Command command) throws InterruptedException { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + synchronized (this.mutex) { + command.execute(); + } + } + + + // ********** additional public protocol ********** + + /** + * "Drain" all the current items from the queue into specified queue. + */ + public void drainTo(Queue<E> q) { + synchronized (this.mutex) { + this.drainTo_(q); + } + } + + /** + * Pre-condition: synchronized + */ + private void drainTo_(Queue<E> q) { + boolean changed = false; + while ( ! this.queue.isEmpty()) { + q.enqueue(this.queue.dequeue()); + changed = true; + } + if (changed) { + this.mutex.notifyAll(); + } + } + + /** + * Return the object the queue locks on while performing + * its operations. + */ + public Object getMutex() { + return this.mutex; + } + + + // ********** standard methods ********** + + @Override + public String toString() { + synchronized (this.mutex) { + return '[' + this.queue.toString() + ']'; + } + } + + private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { + synchronized (this.mutex) { + s.defaultWriteObject(); + } + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SynchronizedStack.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SynchronizedStack.java index 1b0b9f33d9..dc0a4dce1b 100644 --- a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SynchronizedStack.java +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/SynchronizedStack.java @@ -11,6 +11,7 @@ package org.eclipse.jpt.utility.internal; import java.io.Serializable; import java.util.EmptyStackException; + import org.eclipse.jpt.utility.Command; /** @@ -59,14 +60,14 @@ public class SynchronizedStack<E> } /** - * Construct a synchronized stack that locks on the specified mutex. + * Construct an empty synchronized stack that locks on the specified mutex. */ public SynchronizedStack(Object mutex) { this(new SimpleStack<E>(), mutex); } /** - * Construct a synchronized stack that locks on itself. + * Construct an empty synchronized stack that locks on itself. */ public SynchronizedStack() { this(new SimpleStack<E>()); @@ -75,21 +76,35 @@ public class SynchronizedStack<E> // ********** Stack implementation ********** - public void push(E o) { + public void push(E element) { synchronized (this.mutex) { - this.stack.push(o); - this.mutex.notifyAll(); + this.push_(element); } } + /** + * Pre-condition: synchronized + */ + private void push_(E element) { + this.stack.push(element); + this.mutex.notifyAll(); + } + public E pop() { synchronized (this.mutex) { - E o = this.stack.pop(); - this.mutex.notifyAll(); - return o; + return this.pop_(); } } + /** + * Pre-condition: synchronized + */ + private E pop_() { + E o = this.stack.pop(); + this.mutex.notifyAll(); + return o; + } + public E peek() { synchronized (this.mutex) { return this.stack.peek(); @@ -111,9 +126,16 @@ public class SynchronizedStack<E> */ public void waitUntilEmptyIs(boolean empty) throws InterruptedException { synchronized (this.mutex) { - while (this.isEmpty() != empty) { - this.mutex.wait(); - } + this.waitUntilEmptyIs_(empty); + } + } + + /** + * Pre-condition: synchronized + */ + private void waitUntilEmptyIs_(boolean empty) throws InterruptedException { + while (this.stack.isEmpty() != empty) { + this.mutex.wait(); } } @@ -121,18 +143,14 @@ public class SynchronizedStack<E> * Suspend the current thread until the stack is empty. */ public void waitUntilEmpty() throws InterruptedException { - synchronized (this.mutex) { - this.waitUntilEmptyIs(true); - } + this.waitUntilEmptyIs(true); } /** * Suspend the current thread until the stack has something on it. */ public void waitUntilNotEmpty() throws InterruptedException { - synchronized (this.mutex) { - this.waitUntilEmptyIs(false); - } + this.waitUntilEmptyIs(false); } /** @@ -140,10 +158,10 @@ public class SynchronizedStack<E> * then "push" the specified item on to the top of the stack * and continue executing. */ - public void waitToPush(E o) throws InterruptedException { + public void waitToPush(E element) throws InterruptedException { synchronized (this.mutex) { - this.waitUntilEmpty(); - this.push(o); + this.waitUntilEmptyIs_(true); + this.push_(element); } } @@ -153,8 +171,8 @@ public class SynchronizedStack<E> */ public Object waitToPop() throws InterruptedException { synchronized (this.mutex) { - this.waitUntilNotEmpty(); - return this.pop(); + this.waitUntilEmptyIs_(false); + return this.pop_(); } } @@ -164,48 +182,58 @@ public class SynchronizedStack<E> /** * Suspend the current thread until the stack's empty status changes * to the specified value or the specified time-out occurs. - * The time-out is specified in milliseconds. Return true if the specified - * value was achieved; return false if a time-out occurred. + * The time-out is specified in milliseconds. Return <code>true</code> if the specified + * empty status was achieved; return <code>false</code> if a time-out occurred. + * If the stack's empty status is already the specified value, + * return <code>true</code> immediately. + * If the time-out is zero, wait indefinitely. */ public boolean waitUntilEmptyIs(boolean empty, long timeout) throws InterruptedException { synchronized (this.mutex) { - if (timeout == 0L) { - this.waitUntilEmptyIs(empty); // wait indefinitely until notified - return true; // if it ever comes back, the condition was met - } + return this.waitUntilEmptyIs_(empty, timeout); + } + } - long stop = System.currentTimeMillis() + timeout; - long remaining = timeout; - while ((this.isEmpty() != empty) && (remaining > 0L)) { - this.mutex.wait(remaining); - remaining = stop - System.currentTimeMillis(); - } - return (this.isEmpty() == empty); + /** + * Pre-condition: synchronized + */ + private boolean waitUntilEmptyIs_(boolean empty, long timeout) throws InterruptedException { + if (timeout == 0L) { + this.waitUntilEmptyIs_(empty); // wait indefinitely until notified + return true; // if it ever comes back, the condition was met + } + + long stop = System.currentTimeMillis() + timeout; + long remaining = timeout; + while ((this.stack.isEmpty() != empty) && (remaining > 0L)) { + this.mutex.wait(remaining); + remaining = stop - System.currentTimeMillis(); } + return (this.stack.isEmpty() == empty); } /** * Suspend the current thread until the stack is empty * or the specified time-out occurs. - * The time-out is specified in milliseconds. Return true if - * the stack is empty; return false if a time-out occurred. + * The time-out is specified in milliseconds. Return <code>true</code> if + * the stack is empty; return <code>false</code> if a time-out occurred. + * If the stack is already empty, return <code>true</code> immediately. + * If the time-out is zero, wait indefinitely. */ public boolean waitUntilEmpty(long timeout) throws InterruptedException { - synchronized (this.mutex) { - return this.waitUntilEmptyIs(true, timeout); - } + return this.waitUntilEmptyIs(true, timeout); } /** * Suspend the current thread until the stack has something on it. * or the specified time-out occurs. - * The time-out is specified in milliseconds. Return true if - * the stack has something on it; return false if a time-out occurred. + * The time-out is specified in milliseconds. Return <code>true</code> if + * the stack is not empty; return <code>false</code> if a time-out occurred. + * If the stack already has something on it, return <code>true</code> immediately. + * If the time-out is zero, wait indefinitely. */ public boolean waitUntilNotEmpty(long timeout) throws InterruptedException { - synchronized (this.mutex) { - return this.waitUntilEmptyIs(false, timeout); - } + return this.waitUntilEmptyIs(false, timeout); } /** @@ -214,14 +242,17 @@ public class SynchronizedStack<E> * and continue executing. If the stack is not emptied out * before the time-out, simply continue executing without * "pushing" the item. - * The time-out is specified in milliseconds. Return true if the - * item was pushed; return false if a time-out occurred. + * The time-out is specified in milliseconds. Return <code>true</code> if the + * item was pushed; return <code>false</code> if a time-out occurred. + * If the stack is already empty, "push" the specified item and + * return <code>true</code> immediately. + * If the time-out is zero, wait indefinitely. */ - public boolean waitToPush(E o, long timeout) throws InterruptedException { + public boolean waitToPush(E element, long timeout) throws InterruptedException { synchronized (this.mutex) { - boolean success = this.waitUntilEmpty(timeout); + boolean success = this.waitUntilEmptyIs_(true, timeout); if (success) { - this.push(o); + this.push_(element); } return success; } @@ -233,12 +264,15 @@ public class SynchronizedStack<E> * If the stack is empty and nothing is "pushed" on to it before the * time-out, throw an empty stack exception. * The time-out is specified in milliseconds. + * If the stack is not empty, "pop" an item and + * return it immediately. + * If the time-out is zero, wait indefinitely. */ public Object waitToPop(long timeout) throws InterruptedException { synchronized (this.mutex) { - boolean success = this.waitUntilNotEmpty(timeout); + boolean success = this.waitUntilEmptyIs_(false, timeout); if (success) { - return this.pop(); + return this.pop_(); } throw new EmptyStackException(); } @@ -273,12 +307,12 @@ public class SynchronizedStack<E> } - // ********** Object overrides ********** + // ********** standard methods ********** @Override public String toString() { synchronized (this.mutex) { - return this.stack.toString(); + return '[' + this.stack.toString() + ']'; } } diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterables/QueueIterable.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterables/QueueIterable.java new file mode 100644 index 0000000000..50819f3f9c --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterables/QueueIterable.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal.iterables; + +import java.util.Iterator; + +import org.eclipse.jpt.utility.internal.Queue; +import org.eclipse.jpt.utility.internal.StringTools; +import org.eclipse.jpt.utility.internal.iterators.QueueIterator; + +/** + * A <code>QueueIterable</code> provides an {@link Iterable} + * for a {@link Queue} of objects of type <code>E</code>. The queue's elements + * are {@link Queue#dequeue() dequeue}d" as the iterable's iterator returns + * them with calls to {@link Iterator#next()}. + * + * @param <E> the type of elements returned by the iterable's iterator + * + * @see Queue + * @see QueueIterator + */ +public class QueueIterable<E> + implements Iterable<E> +{ + private final Queue<E> queue; + + /** + * Construct an iterable for the specified queue. + */ + public QueueIterable(Queue<E> queue) { + super(); + this.queue = queue; + } + + public Iterator<E> iterator() { + return new QueueIterator<E>(this.queue); + } + + @Override + public String toString() { + return StringTools.buildToStringFor(this, this.queue); + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterables/StackIterable.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterables/StackIterable.java new file mode 100644 index 0000000000..a1e18318ed --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterables/StackIterable.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal.iterables; + +import java.util.Iterator; + +import org.eclipse.jpt.utility.internal.Stack; +import org.eclipse.jpt.utility.internal.StringTools; +import org.eclipse.jpt.utility.internal.iterators.StackIterator; + +/** + * A <code>StackIterable</code> provides an {@link Iterable} + * for a {@link Stack} of objects of type <code>E</code>. The stack's elements + * are {@link Stack#pop() pop}ped" as the iterable's iterator returns + * them with calls to {@link Iterator#next()}. + * + * @param <E> the type of elements returned by the iterable's iterator + * + * @see Stack + * @see StackIterator + */ +public class StackIterable<E> + implements Iterable<E> +{ + private final Stack<E> stack; + + /** + * Construct an iterable for the specified stack. + */ + public StackIterable(Stack<E> stack) { + super(); + this.stack = stack; + } + + public Iterator<E> iterator() { + return new StackIterator<E>(this.stack); + } + + @Override + public String toString() { + return StringTools.buildToStringFor(this, this.stack); + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/QueueIterator.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/QueueIterator.java new file mode 100644 index 0000000000..d7a1ff5cec --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/QueueIterator.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal.iterators; + +import java.util.Iterator; + +import org.eclipse.jpt.utility.internal.Queue; +import org.eclipse.jpt.utility.internal.StringTools; + +/** + * A <code>QueueIterator</code> provides an {@link Iterator} + * for a {@link Queue} of objects of type <code>E</code>. The queue's elements + * are {@link Queue#dequeue() dequeue}d" as the iterator returns them with + * calls to {@link #next()}. + * + * @param <E> the type of elements returned by the iterator + * + * @see Queue + */ +public class QueueIterator<E> + implements Iterator<E> +{ + private final Queue<E> queue; + + + /** + * Construct an iterator for the specified queue. + */ + public QueueIterator(Queue<E> queue) { + super(); + this.queue = queue; + } + + public boolean hasNext() { + return ! this.queue.isEmpty(); + } + + public E next() { + return this.queue.dequeue(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return StringTools.buildToStringFor(this, this.queue); + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/StackIterator.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/StackIterator.java new file mode 100644 index 0000000000..af510c8232 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/iterators/StackIterator.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2009 Oracle. 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: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal.iterators; + +import java.util.Iterator; + +import org.eclipse.jpt.utility.internal.Stack; +import org.eclipse.jpt.utility.internal.StringTools; + +/** + * A <code>StackIterator</code> provides an {@link Iterator} + * for a {@link Stack} of objects of type <code>E</code>. The stack's elements + * are {@link Stack#pop() pop}ped" as the iterator returns them with + * calls to {@link #next()}. + * + * @param <E> the type of elements returned by the iterator + * + * @see Stack + */ +public class StackIterator<E> + implements Iterator<E> +{ + private final Stack<E> stack; + + + /** + * Construct an iterator for the specified stack. + */ + public StackIterator(Stack<E> stack) { + super(); + this.stack = stack; + } + + public boolean hasNext() { + return ! this.stack.isEmpty(); + } + + public E next() { + return this.stack.pop(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return StringTools.buildToStringFor(this, this.stack); + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/synchronizers/AsynchronousSynchronizer.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/synchronizers/AsynchronousSynchronizer.java index 75d6fea7fa..970e94c738 100644 --- a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/synchronizers/AsynchronousSynchronizer.java +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/synchronizers/AsynchronousSynchronizer.java @@ -9,11 +9,8 @@ ******************************************************************************/ package org.eclipse.jpt.utility.internal.synchronizers; -import java.util.Vector; - import org.eclipse.jpt.utility.Command; -import org.eclipse.jpt.utility.internal.CompositeException; -import org.eclipse.jpt.utility.internal.StringTools; +import org.eclipse.jpt.utility.internal.ConsumerThreadCoordinator; import org.eclipse.jpt.utility.internal.SynchronizedBoolean; /** @@ -28,7 +25,7 @@ public class AsynchronousSynchronizer implements Synchronizer { /** - * This flag is shared with the synchronization thread. Setting it to true + * This flag is shared with the synchronization/consumer thread. Setting it to true * will trigger the synchronization to begin or, if the synchronization is * currently executing, to execute again, once the current execution is * complete. @@ -36,34 +33,16 @@ public class AsynchronousSynchronizer final SynchronizedBoolean synchronizeFlag = new SynchronizedBoolean(false); /** - * The runnable passed to the synchronization thread each time it is built. - */ - private final Runnable runnable; - - /** - * Optional, client-supplied name for the synchronization thread. - * If null, allow the JDK to assign a name. - */ - private final String threadName; - - /** - * The synchronization is performed on this thread. A new thread is built - * for every start/stop cycle (since a thread cannot be started more than - * once). + * Most of the thread-related behavior is delegated to this coordinator. */ - private Thread thread; - - /** - * A list of the uncaught exceptions thrown by the command. - */ - final Vector<Throwable> exceptions = new Vector<Throwable>(); + private final ConsumerThreadCoordinator consumerThreadCoordinator; // ********** construction ********** /** * Construct an asynchronous synchronizer that uses the specified command to - * perform the synchronization. Allow the generated thread(s) to be assigned + * perform the synchronization. Allow the synchronization thread(s) to be assigned * JDK-generated names. */ public AsynchronousSynchronizer(Command command) { @@ -72,17 +51,19 @@ public class AsynchronousSynchronizer /** * Construct an asynchronous synchronizer that uses the specified command to - * perform the synchronization. Assign the generated thread(s) the specified + * perform the synchronization. Assign the synchronization thread(s) the specified * name. */ public AsynchronousSynchronizer(Command command, String threadName) { super(); - this.runnable = this.buildRunnable(command); - this.threadName = threadName; + if (command == null) { + throw new NullPointerException(); + } + this.consumerThreadCoordinator = new ConsumerThreadCoordinator(this.buildConsumer(command), threadName); } - Runnable buildRunnable(Command command) { - return new RunnableSynchronization(command); + ConsumerThreadCoordinator.Consumer buildConsumer(Command command) { + return new Consumer(command); } @@ -106,20 +87,8 @@ public class AsynchronousSynchronizer * the time {@link #stop()} was called) * </ul> */ - public synchronized void start() { - if (this.thread != null) { - throw new IllegalStateException("The Synchronizer was not stopped."); //$NON-NLS-1$ - } - this.thread = this.buildThread(); - this.thread.start(); - } - - private Thread buildThread() { - Thread t = new Thread(this.runnable); - if (this.threadName != null) { - t.setName(this.threadName); - } - return t; + public void start() { + this.consumerThreadCoordinator.start(); } /** @@ -140,101 +109,49 @@ public class AsynchronousSynchronizer * exceptions were thrown while the synchronization thread was executing, * wrap them in a composite exception and throw the composite exception. */ - public synchronized void stop() { - if (this.thread == null) { - throw new IllegalStateException("The Synchronizer was not started."); //$NON-NLS-1$ - } - this.thread.interrupt(); - try { - this.thread.join(); - } catch (InterruptedException ex) { - // the thread that called #stop() was interrupted while waiting to - // join the synchronization thread - ignore; - // 'thread' is still "interrupted", so its #run() loop will still stop - // after its current execution - we just won't wait around for it... - } - this.thread = null; - - if (this.exceptions.size() > 0) { - Throwable[] temp = this.exceptions.toArray(new Throwable[this.exceptions.size()]); - this.exceptions.clear(); - throw new CompositeException(temp); - } - } - - @Override - public String toString() { - return StringTools.buildToStringFor(this, this.thread); + public void stop() { + this.consumerThreadCoordinator.stop(); } - // ********** synchronization thread runnable ********** + // ********** consumer ********** /** - * This implementation of {@link Runnable} will execute a client-supplied command. - * It will wait until a shared "synchronize" flag is set to execute the - * command. Once the the comand is executed, the thread will quiesce until + * This implementation of {@link ConsumerThreadCoordinator.Consumer} + * will execute the client-supplied "synchronize" command. + * It will wait until the shared "synchronize" flag is set to execute the + * command. Once the comand is executed, the thread will quiesce until * the flag is set again. If the flag was set during the execution of the * command (either recursively by the command itself or by another thread), * the command will be re-executed immediately. Stop the thread by calling * {@link Thread#interrupt()}. */ - class RunnableSynchronization - implements Runnable + class Consumer + implements ConsumerThreadCoordinator.Consumer { - /** The client-supplied command that executes on this thread. */ + /** + * The client-supplied command that executes on the + * synchronization/consumer thread. + */ private final Command command; - - RunnableSynchronization(Command command) { + Consumer(Command command) { super(); - if (command == null) { - throw new NullPointerException(); - } this.command = command; } /** - * Loop while this thread has not been interrupted by another thread. - * In each loop: Wait until the "synchronize" flag is set, - * then clear it and execute the command. If the - * "synchronize" flag was set <em>during</em> the synchronization, - * there will be no "wait" before beginning the next synchronization - * (thus the call to {@link Thread#isInterrupted()} before each cycle). - * <p> - * If this thread is interrupted <em>during</em> the synchronization, the - * call to {@link Thread#interrupted()} will stop the loop. If this thread is - * interrupted during the call to {@link SynchronizedBoolean#waitToSetFalse()}, - * we will catch the {@link InterruptedException} and stop the loop. - */ - public void run() { - while ( ! Thread.interrupted()) { - try { - AsynchronousSynchronizer.this.synchronizeFlag.waitToSetFalse(); - } catch (InterruptedException ex) { - // we were interrupted while waiting, must be Quittin' Time - return; - } - this.execute(); - } - } - - /** - * Execute the client-supplied command. Do not allow any unhandled - * exceptions to kill the thread. Store them up for later pain. + * Wait until the "synchronize" flag is set, + * then clear it and allow the "synchronize" command to execute. */ - private void execute() { - try { - this.execute_(); - } catch (Throwable ex) { - AsynchronousSynchronizer.this.exceptions.add(ex); - } + public void waitForProducer() throws InterruptedException { + AsynchronousSynchronizer.this.synchronizeFlag.waitToSetFalse(); } /** * Execute the client-supplied command. */ - void execute_() { + public void execute() { this.command.execute(); } diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/synchronizers/CallbackAsynchronousSynchronizer.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/synchronizers/CallbackAsynchronousSynchronizer.java index 28c610d642..2c30a3241f 100644 --- a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/synchronizers/CallbackAsynchronousSynchronizer.java +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/synchronizers/CallbackAsynchronousSynchronizer.java @@ -10,15 +10,16 @@ package org.eclipse.jpt.utility.internal.synchronizers; import org.eclipse.jpt.utility.Command; +import org.eclipse.jpt.utility.internal.ConsumerThreadCoordinator; import org.eclipse.jpt.utility.internal.ListenerList; /** * Extend the asynchronous synchronizer to notify listeners * when a synchronization "cycle" is complete; i.e. the synchronization has, - * for the moment, handled every "synchronize" request and quiesced. + * for the moment, handled every outstanding "synchronize" request and quiesced. * This notification is <em>not</em> guaranteed to occur with <em>every</em> - * synchronization "cycle"; - * since other, unrelated, synchronizations can be triggered concurrently. + * synchronization "cycle"; since other, unrelated, synchronizations can be + * triggered concurrently. * <p> * <strong>NB:</strong> Listeners should handle any exceptions * appropriately (e.g. log the exception and return gracefully so the thread @@ -35,7 +36,7 @@ public class CallbackAsynchronousSynchronizer /** * Construct a callback asynchronous synchronizer that uses the specified - * command to perform the synchronization. Allow the generated thread(s) + * command to perform the synchronization. Allow the synchronization thread(s) * to be assigned JDK-generated names. */ public CallbackAsynchronousSynchronizer(Command command) { @@ -44,7 +45,7 @@ public class CallbackAsynchronousSynchronizer /** * Construct a callback asynchronous synchronizer that uses the specified - * command to perform the synchronization. Assign the generated thread(s) + * command to perform the synchronization. Assign the synchronization thread(s) * the specified name. */ public CallbackAsynchronousSynchronizer(Command command, String threadName) { @@ -52,12 +53,12 @@ public class CallbackAsynchronousSynchronizer } /** - * Build a runnable that will let us know when the synchronization has + * Build a consumer that will let us know when the synchronization has * quiesced. */ @Override - Runnable buildRunnable(Command command) { - return new RunnableCallbackSynchronization(command); + ConsumerThreadCoordinator.Consumer buildConsumer(Command command) { + return new CallbackConsumer(command); } @@ -84,7 +85,7 @@ public class CallbackAsynchronousSynchronizer // ********** synchronization thread runnable ********** /** - * Extend {@link AsynchronousSynchronizer.RunnableSynchronization} + * Extend {@link AsynchronousSynchronizer.Consumer} * to notify the synchronizer when the synchronization has quiesced * (i.e. the command has finished executing and there are no further * requests for synchronization). @@ -96,16 +97,16 @@ public class CallbackAsynchronousSynchronizer * but this synchronization will not occur until <em>after</em> all the * listeners have been notified. */ - class RunnableCallbackSynchronization - extends RunnableSynchronization + class CallbackConsumer + extends Consumer { - RunnableCallbackSynchronization(Command command) { + CallbackConsumer(Command command) { super(command); } @Override - void execute_() { - super.execute_(); + public void execute() { + super.execute(); // hmmm - we will notify listeners even when we our thread is "interrupted"; // that seems ok... ~bjv if (CallbackAsynchronousSynchronizer.this.synchronizeFlag.isFalse()) { |