1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package com.gargoylesoftware.base.gui;
39
40 import com.gargoylesoftware.base.util.DetailedNullPointerException;
41 import java.util.Locale;
42 import javax.swing.SwingUtilities;
43
44 /***
45 * An abstract superclass for GUI controller classes.<p>
46 *
47 * Swing uses an architecture similar to Model-View-Controller (MVC) in which the model, view
48 * and controller components are kept seperate from each other. This class provides
49 * some common behaviour that is useful for controller objects. This includes support for:
50 * <ul>
51 *
52 * <li>Starting and managing worker threads. See {@link #startTask(WorkerTask)},
53 * {@link #taskComplete(WorkerTask)}, {@link #taskSuccessful(WorkerTask)},
54 * {@link #taskExceptionThrown(WorkerTask,Exception)},
55 * {@link #taskErrorThrown(WorkerTask,Throwable)}
56 * <br /> </li>
57 *
58 * <li>Simple support for localization. See {@link #setLocale(Locale)},
59 * {@link #getLocale()}, {@link #localeChanged(Locale)}</li>
60 *
61 * </ul>
62 *
63 * @version $Revision: 1.7 $
64 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
65 */
66 public abstract class AbstractUIController {
67 private Locale locale_ = Locale.getDefault();
68
69
70 private class TaskRunnable implements Runnable {
71 private Throwable throwable_;
72 private final WorkerTask task_;
73
74 /***
75 * Create an instance
76 * @param task The task that will be run
77 */
78 public TaskRunnable( final WorkerTask task ) {
79 task_ = task;
80 }
81
82 /*** Run the task */
83 public void run() {
84
85 try {
86 task_.runOnWorkerThread();
87 }
88 catch( final Throwable t ) {
89 throwable_ = t;
90 }
91 SwingUtilities.invokeLater(
92 new Runnable() {
93 public void run() {
94 try {
95 run2();
96 }
97 catch( final Throwable t ) {
98 t.printStackTrace();
99 }
100 }
101 public void run2() {
102 if( throwable_ != null ) {
103 if( throwable_ instanceof Exception ) {
104 taskExceptionThrown( task_,
105 (Exception)throwable_ );
106 }
107 else {
108 taskErrorThrown( task_, throwable_ );
109 }
110 }
111 else {
112 try {
113 task_.runOnUIThread();
114 taskSuccessful( task_ );
115 }
116 catch( final Exception e ) {
117 taskExceptionThrown( task_, e );
118 }
119 catch( final Throwable t ) {
120 taskErrorThrown( task_, t );
121 }
122 }
123 taskComplete( task_ );
124 }
125 } );
126 }
127 }
128
129
130 /***
131 * Create a new controller.
132 */
133 public AbstractUIController() {
134 }
135
136
137 /***
138 * Set the current locale
139 *
140 * @param locale the new locale
141 */
142 public final void setLocale( final Locale locale ) {
143 assertNotNull("locale", locale);
144 locale_ = locale;
145 localeChanged( locale_ );
146 }
147
148
149 /***
150 * Return the current locale.
151 *
152 * @return The current locale
153 */
154 public final Locale getLocale() {
155 return locale_;
156 }
157
158
159 /***
160 * The main entry point into this controller
161 */
162 public final void run() {
163 localeChanged( getLocale() );
164 runImpl();
165 }
166
167
168 /***
169 * Subclasses will override this to provide the run logic
170 */
171 protected abstract void runImpl();
172
173
174 /***
175 * Start a WorkerTask. This involves running some code on a background
176 * thread and then some more on the ui thread. See WorkerTask for more
177 * details. <p>
178 *
179 * When the task has completed, one of the following callbacks will be
180 * called based on the success of the task.
181 * <ul>
182 * <li> If the task completed without error then {@link
183 * #taskSuccessful(com.gargoylesoftware.base.gui.WorkerTask)
184 * taskSuccessful()} will be called
185 * <li> If an exception was thrown during processing of the task then
186 * {@link #taskExceptionThrown(com.gargoylesoftware.base.gui.WorkerTask,Exception)
187 * taskExceptionThrown()} will be called
188 * <li> If an error was thrown during processing of the task then {@link
189 * #taskErrorThrown(com.gargoylesoftware.base.gui.WorkerTask,Throwable)
190 * taskErrorThrown()} will be called
191 * </ul>
192 * <br />
193 * Finally, the method {@link #taskComplete(com.gargoylesoftware.base.gui.WorkerTask)
194 * taskSuccessful()} will be called will be called to signal the completion
195 * of the task
196 *
197 * @param task The WorkerTask that is about to execute.
198 */
199 protected final void startTask( final WorkerTask task ) {
200 new Thread( new TaskRunnable(task) ).start();
201 }
202
203
204 /***
205 * A callback that will be invoked when an exception is thrown during the
206 * processing of a WorkerTask. Note that errors and other throwables will
207 * processed by the method {@link #taskErrorThrown(WorkerTask,Throwable)}.<p>
208 *
209 * The default behaviour is to print the stack trace of the caught exception
210 * to System.out. Override this method to provide custom error handling.
211 *
212 * @param task The task that failed
213 * @param exception The exception that was caught.
214 */
215 protected void taskExceptionThrown( final WorkerTask task, final Exception exception ) {
216 exception.printStackTrace();
217 }
218
219
220 /***
221 * A callback that will be invoked when a system error is thrown during the
222 * processing of a WorkerTask. System errors are any throwable objects not
223 * descended from Exception. Typically, only system level code will be
224 * concerned by the errors handled by this method. Application code should
225 * only be concerned with the errors handled by
226 * {@link #taskExceptionThrown(WorkerTask,Exception)}.
227 *
228 * The default behaviour is to print the stack trace of the caught error
229 * to System.out. Override this method to provide custom error handling.
230 *
231 * @param task The task that failed
232 * @param throwable The throwable object that was caught
233 */
234 protected void taskErrorThrown( final WorkerTask task, final Throwable throwable ) {
235 throwable.printStackTrace();
236 }
237
238
239 /***
240 * A callback that will be invoked when a task completed successfully.
241 * Override this method to provide custom handling on completion
242 * of a task.<p>
243 *
244 * The default behaviour is to do nothing. Override this to provide custom
245 * behaviour.
246 *
247 * @param task The task that just finished.
248 */
249 protected void taskSuccessful( final WorkerTask task ) {
250 }
251
252
253 /***
254 * A callback that will be invoked when a task has completed whether it was
255 * successful or not. Override this method to provide custom handling on
256 * completion of a task.
257 *
258 * The default behaviour is to do nothing. Override this to provide custom
259 * behaviour.
260 *
261 * @param task The task that just finished.
262 */
263 protected void taskComplete( final WorkerTask task ) {
264 }
265
266
267 /***
268 * The current locale has changed - update all locale specific information.
269 * All logic that sets locale sensitive information should be executed in
270 * this method.
271 *
272 * @param locale The new locale
273 */
274 protected abstract void localeChanged( final Locale locale );
275
276
277 /***
278 * Throw an exception if the specified object is null
279 * @param fieldName The name of the paremeter we are checking
280 * @param object The value of the parameter we are checking
281 */
282 protected final void assertNotNull( final String fieldName, final Object object ) {
283 if( object == null ) {
284 throw new DetailedNullPointerException("fieldName");
285 }
286 }
287 }
288