/******************************************************************************* * Copyright (c) 2009 the CHISEL group and contributors. * 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: * Del Myers -- initial API and implementation *******************************************************************************/ package org.eclipse.zest.custom.sequence.internal; import java.util.LinkedList; import java.util.List; import org.eclipse.swt.widgets.Display; /** * A class used to schedule long-running processes that must be run in the UI thread. * This class is available here in order to prevent coupling with the Eclipse runtime * so that the views can be used in stand-alone java applications. * * @author Del Myers * */ public final class UIJobProcessor { private class UIJob { AbstractSimpleProgressRunnable runnable; Display display; IUIProgressService service; boolean cancelable; public UIJob(AbstractSimpleProgressRunnable runnable, Display display, IUIProgressService service, boolean cancelable) { this.display = display; this.runnable = runnable; this.service = service; this.cancelable = cancelable; } } /** * Defines listeners for job states in this processor. * @author Del Myers */ public static interface IUIJobProcessListener { public void jobQueued(AbstractSimpleProgressRunnable runnable); public void jobRemoved(AbstractSimpleProgressRunnable runnable); public void jobStarted(AbstractSimpleProgressRunnable runnable); public void jobFinished(AbstractSimpleProgressRunnable runnable); } /** * The queue of jobs to be run. */ private LinkedList<UIJob> queue; private List<IUIJobProcessListener> listeners; private UIJob currentJob; private Thread thread; private volatile boolean quit; private IUIProgressService progressService; public UIJobProcessor(IUIProgressService progressService) { this.progressService = progressService; queue = new LinkedList<UIJob>(); listeners = new LinkedList<IUIJobProcessListener>(); quit = false; } public final void runInUIThread(AbstractSimpleProgressRunnable runnable, Display display, boolean cancelable) { run(runnable, display, progressService, cancelable); } /** * Searches for queued jobs that have a family equal to the given family. Jobs that are not * currently running, they will be removed from the queue. If the current running job is * part of the family, it will be cancelled. * @param family */ public final void cancelJobsInFamily(Object family) { if (family == null) return; synchronized (queue) { removeJobsInFamily(family); if (currentJob != null && family.equals(currentJob.runnable.getFamily())) { if (currentJob.runnable != null) { currentJob.runnable.cancel(); } } } } public final void quit() { if (quit) { return; } synchronized (queue) { quit = true; if (currentJob != null) { currentJob.runnable.cancel(); } while (queue.size() > 0) { UIJob job = queue.removeFirst(); fireRemoved(job.runnable); fireFinished(job.runnable); } } } /** * Searches through the queued jobs that have a family equal to the given family. If such jobs are found, * they are removed from the queue. Does not cancel a currently running job. * @param family */ public void removeJobsInFamily(Object family) { if (family == null) return; synchronized (queue) { int i = 0; while (i < queue.size()) { UIJob job = queue.get(i); if (family.equals(job.runnable.getFamily())) { queue.remove(i); fireRemoved(job.runnable); fireFinished(job.runnable); } else { i++; } } } } private final void run(AbstractSimpleProgressRunnable runnable, Display display, IUIProgressService service, boolean cancelable) { synchronized (queue) { if (runnable != null && display != null && !display.isDisposed()) { UIJob job = new UIJob(runnable, display, service, cancelable); queue.add(job); fireQueued(job.runnable); if (thread == null) { thread = new Thread(new QueueingThreadRunnable(), "Sequence Diagram UI Job Queue"); thread.start(); } } } } private class QueueingThreadRunnable implements Runnable { /** * Runs until the job queue is empty. After the job queue is empty, the thread * will die, and must be rescheduled. */ public void run() { int queueSize; do { synchronized (queue) { queueSize = queue.size(); if (queueSize > 0) { queueSize--; currentJob = queue.removeFirst(); fireRemoved(currentJob.runnable); } } if (currentJob != null && currentJob.display != null) { try { currentJob.display.syncExec(new Runnable(){ public void run() { fireStarted(currentJob.runnable); if (currentJob.service == null) { currentJob.service = new DelayedProgressMonitorDialog( Display.getCurrent().getActiveShell() ); } try { currentJob.service.runInUIThread(currentJob.runnable, currentJob.cancelable); } catch (Throwable e) { currentJob.service.handleException(e); } finally { fireFinished(currentJob.runnable); currentJob = null; } } }); } catch (Throwable t) { //don't die because of an exception t.printStackTrace(); } } synchronized (queue) { if (queue.size() == 0) { thread = null; break; } } } while (true); } } public void addJobListener(IUIJobProcessListener listener) { synchronized (listeners) { if (!listeners.contains(listener)) { listeners.add(listener); } } } public void removeJobListener(IUIJobProcessListener listener) { synchronized (listeners) { listeners.remove(listener); } } private void fireStarted(AbstractSimpleProgressRunnable runnable) { IUIJobProcessListener[] la; synchronized (listeners) { la = listeners.toArray(new IUIJobProcessListener[listeners.size()]); } for (IUIJobProcessListener listener : la) { listener.jobStarted(runnable); } } private void fireQueued(AbstractSimpleProgressRunnable runnable) { IUIJobProcessListener[] la; synchronized (listeners) { la = listeners.toArray(new IUIJobProcessListener[listeners.size()]); } for (IUIJobProcessListener listener : la) { listener.jobQueued(runnable); } } private void fireRemoved(AbstractSimpleProgressRunnable runnable) { IUIJobProcessListener[] la; synchronized (listeners) { la = listeners.toArray(new IUIJobProcessListener[listeners.size()]); } for (IUIJobProcessListener listener : la) { listener.jobRemoved(runnable); } } private void fireFinished(AbstractSimpleProgressRunnable runnable) { IUIJobProcessListener[] la; synchronized (listeners) { la = listeners.toArray(new IUIJobProcessListener[listeners.size()]); } for (IUIJobProcessListener listener : la) { listener.jobFinished(runnable); } } }