001    package com.sptci.echo2;
002    
003    import java.io.Serializable;
004    import java.util.ArrayList;
005    import java.util.Comparator;
006    import java.util.List;
007    
008    import java.util.logging.Level;
009    import java.util.logging.Logger;
010    
011    import nextapp.echo2.app.Button;
012    import nextapp.echo2.app.Component;
013    import nextapp.echo2.app.Label;
014    import nextapp.echo2.app.Table;
015    import nextapp.echo2.app.TaskQueueHandle;
016    
017    import nextapp.echo2.app.event.ActionListener;
018    import nextapp.echo2.app.table.TableCellRenderer;
019    
020    import nextapp.echo2.webcontainer.ContainerContext;
021    
022    import com.sptci.ReflectionUtility;
023    
024    /**
025     * An abstract base class for all controllers.
026     *
027     * <p><b>Note:</b> It is imperative that sub-classes set
028     * {@link #application} to a valid instance.</p>
029     *
030     * <p>Copyright 2006 Sans Pareil Technologies, Inc.</p>
031     * @author Rakesh Vidyadharan 2006-05-16
032     * @version $Id: Controller.java 3099 2007-04-22 13:39:37Z rakesh $
033     */
034    public abstract class Controller<V> implements Serializable
035    {
036      /**
037       * The logger used to log errors to.
038       */
039      protected static final Logger logger =
040        Logger.getLogger( Controller.class.getName() );
041    
042      /**
043       * The global application instance for each session.
044       */
045      protected Application application = (Application) Application.getActive();
046    
047      /**
048       * The view that is controlled by this instance.
049       */
050      protected final V view;
051    
052      /**
053       * Crete a new instance of the controller for the specified view.
054       */
055      public Controller( V view )
056      {
057        this.view = view;
058      }
059      
060      /**
061       * Check the specified string to ensure that it is not <code>null</code>
062       * and not empty.  Strings that are composed entirely of spaces are also 
063       * treated as empty.
064       *
065       * @param text The text that is to be checked.
066       * @return Return <code>true</code> if the string specified is not <code>null
067       *   </code> or empty.
068       */
069      public boolean checkText( String text )
070      {
071        boolean result = true;
072        
073        if ( text == null || text.length() == 0 )
074        {
075          result = false;
076        }
077        else if ( text.replaceAll( "\\s", "" ).length() == 0 )
078        {
079          result = false;
080        }
081        
082        return result;
083      }
084      
085      /**
086       * Returns {@link #application}.
087       *
088       * @return Application The value/reference of/to application.
089       */
090      public Application getApplication()
091      {
092        return application;
093      }
094      
095      /**
096       * Set {@link #application}.
097       *
098       * @param application The value to set.
099       */
100      protected void setApplication( Application application )
101      {
102        this.application = application;
103      }
104      
105      /**
106       * Returns {@link #view}.
107       *
108       * @return V The value/reference of/to view.
109       */
110      public V getView()
111      {
112        return view;
113      }
114      
115      /**
116       * Return the logger associated with this controller.
117       */
118      public Logger getLogger()
119      {
120        return logger;
121      }
122      
123      /**
124       * A global handler for exceptions encountered while updating the
125       * display.
126       *
127       * @see Application#processFatalException
128       * @param t The fatal exception
129       */
130      public void processFatalException( Throwable t )
131      {
132        application.processFatalException( t );
133      }
134    
135      /**
136       * A custom table cell renderer used to present the cell data as
137       * hyper linked.
138       */
139      public class LinkedCellRenderer implements TableCellRenderer
140      {
141        /**
142         * The action listener to use for the cell data.
143         */
144        private ActionListener listener;
145    
146        /**
147         * Create a new instance of the rendered using the specified
148         * action listener
149         *
150         * @param listener The action listener to use for the
151         *   cell data.
152         */
153        public LinkedCellRenderer( ActionListener listener )
154        {
155          this.listener = listener;
156        }
157    
158        /**
159         * Return the hyperlinked button with appropriate action listener
160         */
161        public Component getTableCellRendererComponent( Table table, 
162            Object value, int column, int row )
163        {
164          ArrayList list = (ArrayList) value;
165          Button button = new Button( (String) list.get( 1 ) );
166          button.setStyleName( "Table.Button" );
167          button.addActionListener( listener );
168          button.setActionCommand( column + "_" + (String) list.get( 0 ) );
169          return button;
170        }
171      }
172    
173      /**
174       * An abstract worker thread used to execute tasks in the background to avoid locking up
175       * the UI.
176       */
177      protected abstract class Worker<S> extends Thread
178      {
179        /**
180         * The polling interval at which client must poll server for results of task execution.
181         */
182        protected int pollingInterval = 2000;
183    
184        /**
185         * The source object on which the specified {@link #method} is to be invoked.
186         */
187        protected S source;
188    
189        /**
190         * The name of the <code>Method</code> that is to be invoked on {@link 
191         * #source}.
192         */
193        protected String method;
194    
195        /**
196         * The message to display after completing the task.
197         */
198        protected String message;
199    
200        /**
201         * Default constructor.
202         */
203        protected Worker() {}
204    
205        /**
206         * Create a new instance of the worker with the specified values.
207         *
208         * @param pollingInterval The {@link #pollingInterval} to set.
209         * @param source The {@link #source} to set.
210         * @param method The {@link #method} to set.
211         * @param message The {@link #message} to set.
212         */
213        protected Worker( int pollingInterval, S source, String method, 
214            String message )
215        {
216          setPollingInterval( pollingInterval );
217          setSource( source );
218          setMethod( method );
219          setMessage( message );
220        }
221    
222        /**
223         * Implementation of the Runnable interface.  Delegates to
224         * {@link #execute}.
225         */
226        @Override
227        public void run()
228        {
229          execute();
230        }
231    
232        /**
233         * Execute the {@link #method} on {@link #source}.
234         */
235        protected abstract void execute();
236    
237        /**
238         * Display the contents of {@link #message} in an instance of {@link ErrorPane}.
239         */
240        void displayMessage()
241        {
242          if ( message != null )
243          {
244            TaskQueueHandle queue = application.createTaskQueue();
245            ContainerContext containerContext = (ContainerContext) 
246              application.getContextProperty( ContainerContext.CONTEXT_PROPERTY_NAME );
247            containerContext.setTaskQueueCallbackInterval( queue, pollingInterval );
248            application.enqueueTask( queue, new Executor() );
249          }
250        }
251        
252        /**
253         * Set {@link #pollingInterval}.
254         *
255         * @param pollingInterval The value to set.
256         */
257        protected void setPollingInterval( int pollingInterval )
258        {
259          this.pollingInterval = pollingInterval;
260        }
261        
262        /**
263         * Set {@link #source}.
264         *
265         * @param source The value to set.
266         */
267        protected void setSource( S source )
268        {
269          this.source = source;
270        }
271        
272        /**
273         * Set {@link #method}.
274         *
275         * @param method The value to set.
276         */
277        protected void setMethod( String method )
278        {
279          this.method = method;
280        }
281        
282        /**
283         * Set {@link #message}.
284         *
285         * @param message The value to set.
286         */
287        protected void setMessage( String message )
288        {
289          this.message = message;
290        }
291    
292        /**
293         * The <code>Runnable</code> that is used to execute the task from the
294         * Echo2 <code>TaskQueue</code>.
295         */
296        class Executor implements Runnable
297        {
298          /**
299           * Execute {@link #method}.
300           */
301          public void run()
302          {
303            ErrorPane pane = new ErrorPane( "Task Finished", message );
304            application.getWindow().getContent().add( pane );
305          }
306        }
307      }
308    
309      /**
310       * An abstract updater task used to execute timed tasks in the 
311       * background and <code>push</code> updates to the UI.
312       */
313      public class Updater<S> implements TaskQueueHandle
314      {
315        /**
316         * The polling interval at which client must poll server for updates.
317         */
318        protected int pollingInterval;
319    
320        /**
321         * The source object which contains the method that is to be invoked
322         * each time this task is executed.
323         */
324        protected S source;
325    
326        /**
327         * The name of the <code>Method</code> that is to be invoked on {@link 
328         * #source}.
329         */
330        protected String method;
331    
332        /**
333         * Create a new instance of the worker with the specified values.
334         *
335         * @param pollingInterval The {@link #pollingInterval} to set.
336         * @param source The {@link #source} to set.
337         * @param method The {@link #method} to set.
338         */
339        public Updater( int pollingInterval, S source, String method )
340        {
341          setPollingInterval( pollingInterval );
342          setSource( source );
343          setMethod( method );
344          application.addTask( this, new Executor() );
345        }
346        
347        /**
348         * Set {@link #pollingInterval}.
349         *
350         * @param pollingInterval The value to set.
351         */
352        protected void setPollingInterval( int pollingInterval )
353        {
354          this.pollingInterval = pollingInterval;
355        }
356        
357        /**
358         * Set {@link #source}.
359         *
360         * @param source The value to set.
361         */
362        protected void setSource( S source )
363        {
364          this.source = source;
365        }
366        
367        /**
368         * Set {@link #method}.
369         *
370         * @param method The value to set.
371         */
372        protected void setMethod( String method )
373        {
374          this.method = method;
375        }
376    
377        /**
378         * The runnable that is used to execute {@link #method} on {@link
379         * #source} when executed the application service.
380         */
381        class Executor implements Runnable
382        {
383          /**
384           * Execute the method on the source object.
385           */
386          public void run()
387          {
388            try
389            {
390              ReflectionUtility.fetchMethod(
391                  source.getClass(), method, new Class[]{} ).invoke(
392                  source, new Object[]{} );
393            }
394            catch ( Throwable t )
395            {
396              logger.log( Level.SEVERE, "Error executing method", t );
397            }
398          }
399        }
400      }
401    }