View Javadoc

1   /*
2    * Copyright (c) 1998, 2005 Gargoyle Software Inc. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions are met:
6    *
7    * 1. Redistributions of source code must retain the above copyright notice,
8    *    this list of conditions and the following disclaimer.
9    * 2. Redistributions in binary form must reproduce the above copyright notice,
10   *    this list of conditions and the following disclaimer in the documentation
11   *    and/or other materials provided with the distribution.
12   * 3. The end-user documentation included with the redistribution, if any, must
13   *    include the following acknowledgment:
14   *
15   *       "This product includes software developed by Gargoyle Software Inc.
16   *        (http://www.GargoyleSoftware.com/)."
17   *
18   *    Alternately, this acknowledgment may appear in the software itself, if
19   *    and wherever such third-party acknowledgments normally appear.
20   * 4. The name "Gargoyle Software" must not be used to endorse or promote
21   *    products derived from this software without prior written permission.
22   *    For written permission, please contact info@GargoyleSoftware.com.
23   * 5. Products derived from this software may not be called "GSBase", nor may
24   *    "GSBase" appear in their name, without prior written permission of
25   *    Gargoyle Software Inc.
26   *
27   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
28   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
29   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
30   * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
33   * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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 />&nbsp;</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