/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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. */ package org.geotools.utils.progress; import java.util.ArrayList; import java.util.List; import javax.swing.SwingUtilities; /** * * @author Simone Giannecchini, GeoSolutions. * * * @source $URL$ */ public abstract class ProgressManager { /** * Private Class which simply fires the events using a copy of the listeners * list in order to avoid problems with listeners that remove themselves or * are removed by someone else */ protected final static class ProgressEventDispatchThreadEventLauncher implements Runnable { /** * The event we want to fire away. */ private ProcessingEvent event; /** * The list of listeners. */ private Object[] listeners; /** * Default constructor. * */ ProgressEventDispatchThreadEventLauncher() { } /** * Used to send an event to an array of listeners. * * @param evt * is the {@link ProcessingEvent} to send. * @param listeners * is the array of {@link ProcessingEventListener}s to * notify. */ synchronized void setEvent(final ProcessingEvent evt, final Object[] listeners) { if (listeners == null || evt == null) throw new NullPointerException("Input argumentBuilder cannot be null"); this.listeners = listeners; this.event = evt; } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ public void run() { final int numListeners = listeners.length; if (event instanceof ExceptionEvent) for (int i = 0; i < numListeners; i++) ((ProcessingEventListener) listeners[i]) .exceptionOccurred((ExceptionEvent) this.event); else for (int i = 0; i < numListeners; i++) ((ProcessingEventListener) listeners[i]) .getNotification(this.event); } } /** * Set this to false for command line UIs where the delayed event sending * may prevent some messages to be seen before the tool exits, to true for * real GUI where you don't want the processing to be blocked too long, or * when you have slow listeners in general. */ private boolean sendDelayedMessages = false; /** * Proper way to stop a thread is not by calling Thread.stop() but by using * a shared variable that can be checked in order to notify a terminating * condition. */ private volatile boolean stopThread = false; /** * List containing all the objects that want to be notified during * processing. */ private List<ProcessingEventListener> notificationListeners = new ArrayList<ProcessingEventListener>(); public ProgressManager(boolean sendDelayedMessages) { this.sendDelayedMessages = sendDelayedMessages; } /** * Default constructor. * */ public ProgressManager() { } /** * Adding a listener to the {@link ProcessingEventListener}s' list. * * @param listener * to add to the list of listeners. */ public final synchronized void addProcessingEventListener( final ProcessingEventListener listener) { notificationListeners.add(listener); } /** * Removing a {@link ProcessingEventListener} from the listeners' list. * * @param listener * {@link ProcessingEventListener} to remove from the list of * listeners. */ public final synchronized void removeProcessingEventListener( final ProcessingEventListener listener) { notificationListeners.remove(listener); } /** * Removing all the listeners. * */ public final synchronized void removeAllProcessingEventListeners() { notificationListeners.clear(); } /** * Firing an event to listeners in order to inform them about what we are * doing and about the percentage of work already carried out. * * @param string * The message to show. * @param percentage * The percentage for the process. */ public synchronized void fireEvent(final String string, final double percentage) { final String newLine = System.getProperty("line.separator"); final StringBuffer message = new StringBuffer("Thread Name "); message.append(Thread.currentThread().getName()).append(newLine); message.append(this.getClass().toString()).append(newLine).append( string); final ProcessingEvent evt = new ProcessingEvent(this, string, percentage); ProgressEventDispatchThreadEventLauncher eventLauncher = new ProgressEventDispatchThreadEventLauncher(); eventLauncher.setEvent(evt, this.notificationListeners.toArray()); sendEvent(eventLauncher); } private void sendEvent( ProgressEventDispatchThreadEventLauncher eventLauncher) { if (sendDelayedMessages) SwingUtilities.invokeLater(eventLauncher); else eventLauncher.run(); } /** * Firing an exception event to listeners in order to inform them that * processing broke and we can no longer proceed * * @param string * The message to show. * @param percentage * The percentage for the process. * @param ex * the actual exception occurred */ public synchronized void fireException(final String string, final double percentage, Exception ex) { final String newLine = System.getProperty("line.separator"); final StringBuffer message = new StringBuffer("Thread Name "); message.append(Thread.currentThread().getName()).append(newLine); message.append(this.getClass().toString()).append(newLine).append( string); final ExceptionEvent evt = new ExceptionEvent(this, string, percentage, ex); ProgressEventDispatchThreadEventLauncher eventLauncher = new ProgressEventDispatchThreadEventLauncher(); eventLauncher.setEvent(evt, this.notificationListeners.toArray()); sendEvent(eventLauncher); } /** * Firing an exception event to listeners in order to inform them that * processing broke and we can no longer proceed. This is a convenience * method, it will call {@link #fireException(String, double, Exception)} * with the exception message and -1 as percentage. * * @param ex * the actual exception occurred */ public synchronized void fireException(Exception ex) { fireException(ExceptionEvent.getMessageFromException(ex), -1, ex); } /** * Should this thread be stopped? * */ public final boolean getStopThread() { return stopThread; } /** * Stop this thread. * * @param stop */ public final void stopThread() { stopThread = true; } /** * Perform proper clean up. * */ public synchronized void dispose() { removeAllProcessingEventListeners(); } /** * This method is responsible for doing the actual processing. * */ public abstract void run() throws Throwable; }