1 /*
2  * aTunes
3  * Copyright (C) Alex Aranda, Sylvain Gaudard and contributors
4  *
5  * See http://www.atunes.org/wiki/index.php?title=Contributing for information about contributors
6  *
7  * http://www.atunes.org
8  * http://sourceforge.net/projects/atunes
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  */
20 
21 package net.sourceforge.atunes.kernel.modules.process;
22 
23 import java.awt.event.ActionEvent;
24 import java.awt.event.ActionListener;
25 import java.util.List;
26 import java.util.concurrent.CopyOnWriteArrayList;
27 
28 import javax.swing.SwingUtilities;
29 
30 import net.sourceforge.atunes.model.IDialogFactory;
31 import net.sourceforge.atunes.model.IProcess;
32 import net.sourceforge.atunes.model.IProcessListener;
33 import net.sourceforge.atunes.model.IProgressDialog;
34 import net.sourceforge.atunes.utils.I18nUtils;
35 import net.sourceforge.atunes.utils.Logger;
36 
37 /**
38  * A Process represents a task to be done in background. While task is being
39  * executed it updates a progress dialog. Also represents a common and easy way
40  * to implement new background processes extending this class
41  *
42  * @author alex
43  *
44  * @param <T>
45  */
46 public abstract class AbstractProcess<T> implements Runnable, IProcess<T> {
47 
48 	/**
49 	 * List of listeners notified when Process ends or is canceled
50 	 */
51 	private volatile List<IProcessListener<T>> listeners;
52 
53 	/**
54 	 * Flag indicating if process has been canceled
55 	 */
56 	private volatile boolean canceled = false;
57 
58 	/**
59 	 * Size of this process. This can be for example the total number of files
60 	 * to copy, to delete, ...
61 	 */
62 	private volatile long processSize;
63 
64 	/**
65 	 * The dialog used to show the progress of this process
66 	 */
67 	private IProgressDialog progressDialog;
68 
69 	private IDialogFactory dialogFactory;
70 
71 	/**
72 	 * @param dialogFactory
73 	 */
setDialogFactory(IDialogFactory dialogFactory)74 	public void setDialogFactory(IDialogFactory dialogFactory) {
75 		this.dialogFactory = dialogFactory;
76 	}
77 
78 	/**
79 	 * Adds a listener to this process
80 	 *
81 	 * @param listener
82 	 */
83 	@Override
addProcessListener(IProcessListener<T> listener)84 	public final void addProcessListener(IProcessListener<T> listener) {
85 		if (listeners == null) {
86 			listeners = new CopyOnWriteArrayList<IProcessListener<T>>();
87 		}
88 		listeners.add(listener);
89 	}
90 
91 	/**
92 	 * Removes a listener of this process
93 	 *
94 	 * @param listener
95 	 */
96 	@Override
removeProcessListener(IProcessListener<T> listener)97 	public final void removeProcessListener(IProcessListener<T> listener) {
98 		if (listeners != null) {
99 			listeners.remove(listener);
100 		}
101 	}
102 
103 	/**
104 	 * Returns a message to be shown in progress dialog. Processes can override
105 	 * this method to show custom information
106 	 *
107 	 * @return
108 	 */
getProgressDialogInformation()109 	protected String getProgressDialogInformation() {
110 		return I18nUtils.getString("PLEASE_WAIT");
111 	}
112 
113 	/**
114 	 * Used to cancel this process
115 	 */
cancelProcess()116 	protected final void cancelProcess() {
117 		canceled = true;
118 	}
119 
120 	/**
121 	 * Executes this process
122 	 */
123 	@Override
execute()124 	public final void execute() {
125 		// Add a debug log entry
126 		Logger.debug(this.getClass().getName());
127 
128 		// Get size of this process
129 		this.processSize = getProcessSize();
130 
131 		// Create new thread to run process
132 		Thread t = new Thread(this);
133 
134 		// Run...
135 		t.start();
136 	}
137 
138 	/**
139 	 * Returns progress dialog used in this process
140 	 *
141 	 * @return
142 	 */
getProgressDialog()143 	protected IProgressDialog getProgressDialog() {
144 		if (progressDialog == null) {
145 			progressDialog = dialogFactory.newDialog("progressDialog",
146 					IProgressDialog.class);
147 			progressDialog.setTitle(getProgressDialogTitle());
148 			progressDialog.setInfoText(getProgressDialogInformation());
149 			progressDialog.setCurrentProgress(0);
150 			progressDialog.setProgressBarValue(0);
151 			progressDialog.addCancelButtonActionListener(new ActionListener() {
152 				@Override
153 				public void actionPerformed(ActionEvent e) {
154 					// When cancel disable cancel button
155 					cancelProcess();
156 					progressDialog.disableCancelButton();
157 				}
158 			});
159 		}
160 		return progressDialog;
161 	}
162 
163 	/**
164 	 * Shows progress dialog
165 	 */
showProgressDialog()166 	private final void showProgressDialog() {
167 		try {
168 			SwingUtilities.invokeAndWait(new Runnable() {
169 				@Override
170 				public void run() {
171 					// Set process size in dialog
172 					getProgressDialog().setTotalProgress(processSize);
173 
174 					// Show dialog
175 					getProgressDialog().showDialog();
176 				}
177 			});
178 		} catch (Exception e) {
179 			Logger.error(e);
180 		}
181 	}
182 
183 	/**
184 	 * Hide progress dialog
185 	 */
hideProgressDialog()186 	private final void hideProgressDialog() {
187 		try {
188 			SwingUtilities.invokeAndWait(new Runnable() {
189 				public void run() {
190 					getProgressDialog().hideDialog();
191 				}
192 			});
193 		} catch (Exception e) {
194 			Logger.error(e);
195 		}
196 	}
197 
198 	/**
199 	 * Sets the current progress of this process. This method should be called
200 	 * every time process wants to increase progress indicator
201 	 *
202 	 * @param currentProgress
203 	 */
setCurrentProgress(final long currentProgress)204 	protected final void setCurrentProgress(final long currentProgress) {
205 		try {
206 			SwingUtilities.invokeAndWait(new Runnable() {
207 				@Override
208 				public void run() {
209 					getProgressDialog().setCurrentProgress(currentProgress);
210 					getProgressDialog().setProgressBarValue(
211 							(int) ((currentProgress * 100.0) / processSize));
212 				}
213 			});
214 		} catch (Exception e) {
215 			Logger.error(e);
216 		}
217 	}
218 
219 	/**
220 	 * Returns a string for the title of progress dialog
221 	 *
222 	 * @return
223 	 */
getProgressDialogTitle()224 	protected abstract String getProgressDialogTitle();
225 
226 	/**
227 	 * Code of the process
228 	 *
229 	 * @return <code>true</code> if process is executed successfully,
230 	 *         <code>false</code>otherwise
231 	 */
runProcess()232 	protected abstract boolean runProcess();
233 
234 	/**
235 	 * Code to be executed after process cancellation
236 	 */
runCancel()237 	protected abstract void runCancel();
238 
239 	/**
240 	 * @return process result
241 	 */
getProcessResult()242 	protected abstract T getProcessResult();
243 
244 	/**
245 	 * Returns process size
246 	 *
247 	 * @return
248 	 */
getProcessSize()249 	protected abstract long getProcessSize();
250 
251 	/**
252 	 * @return the canceled
253 	 */
isCanceled()254 	protected boolean isCanceled() {
255 		return canceled;
256 	}
257 
258 	@Override
run()259 	public final void run() {
260 		// Show progress dialog
261 		showProgressDialog();
262 
263 		// Run process code
264 		boolean ok = runProcess();
265 
266 		// Process finished: hide progress dialog
267 		hideProgressDialog();
268 
269 		// If process has been canceled then execute cancel code
270 		if (canceled) {
271 			runCancel();
272 		}
273 
274 		// Notify all listeners
275 		if (listeners != null && !listeners.isEmpty()) {
276 			for (IProcessListener<T> listener : listeners) {
277 				if (canceled) {
278 					listener.processCanceled();
279 				} else {
280 					listener.processFinished(ok, getProcessResult());
281 				}
282 			}
283 		}
284 	}
285 
286 	/**
287 	 * @return
288 	 */
getDialogFactory()289 	protected IDialogFactory getDialogFactory() {
290 		return dialogFactory;
291 	}
292 }
293