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 }