001 package com.sptci.rwt.webui;
002
003 import java.util.Collections;
004 import java.util.Map;
005 import java.util.LinkedHashMap;
006
007 import nextapp.echo2.app.Column;
008 import nextapp.echo2.app.Component;
009 import nextapp.echo2.app.Row;
010 import nextapp.echo2.app.SplitPane;
011 import nextapp.echo2.app.TextArea;
012 import nextapp.echo2.app.TextField;
013 import nextapp.echo2.app.event.ActionListener;
014
015 import consultas.echo2consultas.LiveTextField;
016
017 import echopointng.PopUp;
018
019 import com.sptci.ReflectionUtility;
020 import com.sptci.echo2.Configuration;
021 import com.sptci.echo2.Dimensions;
022 import com.sptci.echo2.Utilities;
023 import com.sptci.echo2.WindowPane;
024 import com.sptci.echo2.style.Extent;
025 import com.sptci.rwt.ConnectionManager;
026
027 /**
028 * An abstract base class for query executor view components used to
029 * interact with the {@link com.sptci.rwt.AbstractQueryExecutor} class.
030 *
031 * <p>© Copyright 2007 <a href='http://sptci.com/' target='_new'>Sans Pareil Technologies, Inc.</a></p>
032 * @author Rakesh Vidyadharan 2007-10-12
033 * @version $Id: ExecutorView.java 4123 2008-05-25 21:49:01Z rakesh $
034 */
035 public abstract class ExecutorView extends WindowPane
036 {
037 /** The component used to enter the SQL statement to execute. */
038 protected TextArea query;
039
040 /**
041 * The component used to specify the maximum number of rows of data
042 * to retrieve for <code>select</code> statements.
043 */
044 protected LiveTextField maxResults;
045
046 /**
047 * The component used to specify the maximum number of characters to
048 * display in a table column.
049 *
050 * @since Version 1.3
051 */
052 protected LiveTextField maxColumnLength;
053
054 /**
055 * The component used to display the results after executing a statement.
056 */
057 protected Column results;
058
059 /** Parent Controller. */
060 protected final MainController controller;
061
062 /** The connection manager to use to interact with the database. */
063 protected final ConnectionManager connectionManager;
064
065 /** The cache of previously executed statements in this view. */
066 protected final Map<String,String> history =
067 new LinkedHashMap<String,String>();
068
069 /**
070 * Create instance of the pane using the specified controller.
071 *
072 * @param controller The controller to use to interact with the rest of the
073 * application.
074 */
075 protected ExecutorView( final MainController controller )
076 {
077 this.controller = controller;
078 this.connectionManager = controller.getConnectionManager();
079 setTitle( getTitle() + " : " + connectionManager.getTitle() );
080 }
081
082 /**
083 * Create the UI components that are necessary for the pane.
084 *
085 * @see #createQuery
086 * @see #createControls
087 */
088 @Override
089 public void init()
090 {
091 removeAll();
092 SplitPane pane = new SplitPane();
093 pane.setStyleName( getClass().getName() + ".splitPane" );
094
095 Column column = new Column();
096
097 column.add( createQuery() );
098 column.add( createControls() );
099 pane.add( column );
100
101 results = new Column();
102 pane.add( results );
103
104 add( pane );
105 }
106
107 /**
108 * Initialise the {@link #query} component.
109 *
110 * @return The properly initialised component.
111 */
112 protected Component createQuery()
113 {
114 query = new TextArea();
115 query.setHeight( Extent.getInstance(
116 Dimensions.getInt( this, "query.height" ) ) );
117 query.setStyleName( "Query.TextComponent" );
118 controller.setFocused( query );
119 return query;
120 }
121
122 /**
123 * Initialise the {@link #maxResults} component.
124 *
125 * @see #createLiveTextField
126 * @param row The component to which the {@link #maxResults} component
127 * and its associated label is to be added.
128 */
129 protected void createMaxResults( final Component row )
130 {
131 maxResults = createLiveTextField( "maxResults", row );
132 }
133
134 /**
135 * Initialise the {@link #maxColumnLength} component.
136 *
137 * @since Version 1.3
138 * @see #createLiveTextField
139 * @param row The component to which the {@link #maxColumnLength} component
140 * and its associated label is to be added.
141 */
142 protected void createMaxColumnLength( final Component row )
143 {
144 maxColumnLength = createLiveTextField( "maxColumnLength", row );
145 maxColumnLength.setText( String.valueOf(
146 Dimensions.getInt( this, "maxColumnLength" ) ) );
147 }
148
149 /**
150 * Create a numeric text field with the specified name.
151 *
152 * @since Version 1.3
153 * @param name The name of the text field. Used to look up localised
154 * information.
155 * @param row The parent component to which the text field is to be added.
156 * @return The properly initialised text field.
157 */
158 protected LiveTextField createLiveTextField( final String name,
159 final Component row )
160 {
161 row.add( Utilities.createLabel(
162 getClass().getName(), name, "General.Label" ) );
163
164 LiveTextField field = new LiveTextField();
165 field.setRegexp( "^\\d*$" );
166 field.setWidth( Extent.getInstance(
167 Dimensions.getInt( this, name + ".width" ) ) );
168 field.setMaximumLength(
169 Dimensions.getInt( this, name + ".maxlength" ) );
170
171 row.add( field );
172 return field;
173 }
174
175 /**
176 * Create the {@link nextapp.echo2.app.Button} used to trigger execution
177 * of the user entered SQL statement.
178 *
179 * @see #getListenerName
180 * @return The button component.
181 */
182 protected Component createExecute()
183 {
184 final String className = getListenerName();
185 ActionListener listener = null;
186
187 try
188 {
189 listener = (ActionListener)
190 ReflectionUtility.newInstance( className, controller );
191 }
192 catch ( Throwable t )
193 {
194 String message = Configuration.getString( ExecutorView.class, "error" );
195 message = message.replaceAll( "\\$class\\$", className );
196 controller.processFatalException( message, t );
197 }
198
199 return Utilities.createButton( getClass().getName(), "execute",
200 "General.Button", listener );
201 }
202
203 /**
204 * Create the {@link nextapp.echo2.app.Button} used to trigger export
205 * of the results of the query to Excel.
206 *
207 * @return The component used to trigger the export action.
208 */
209 protected Component createExport()
210 {
211 return Utilities.createButton( getClass().getName(), "export",
212 "General.Button", new ExportListener( controller ) );
213 }
214
215 /**
216 * Create the {@link echopointng.PopUp} component used to display the
217 * component used to capture user input on the category and name to
218 * assign to a saved SQL statement.
219 *
220 * @return The component used to capture user input.
221 */
222 protected Component createSave()
223 {
224 PopUp popup = new PopUp();
225 popup.setTarget( Utilities.createLabel( getClass().getName(),
226 "saveQuery", "Save.Label" ) );
227 popup.setPopUp( new SaveQueryComponent( controller ) );
228
229 return popup;
230 }
231
232 /**
233 * Create the {@link nextapp.echo2.app.Button} used to display the
234 * history of statements executed in the current view.
235 *
236 * @return The button component.
237 */
238 protected Component createHistory()
239 {
240 return Utilities.createButton( getClass().getName(), "history",
241 "General.Button", new HistoryListener( controller ) );
242 }
243
244 /**
245 * Get the fully qualified class name of the action listener used to
246 * execute the user entered statement.
247 *
248 * @return The fully qualified class name of the listener.
249 */
250 protected String getListenerName()
251 {
252 final String name = getClass().getName();
253 String cls = name.substring( 0, name.lastIndexOf( "." ) );
254 cls += ".Execute";
255 cls += name.substring( name.lastIndexOf( "." ) + 1 );
256 cls = cls.substring( 0, cls.lastIndexOf( "ExecutorView" ) );
257 cls += "Listener";
258
259 return cls;
260 }
261
262 /**
263 * Return the SQL statement that was entered into {@link #query}.
264 *
265 * @return The sql statement that was entered.
266 */
267 public String getQuery()
268 {
269 return query.getText();
270 }
271
272 /**
273 * Sets the text displayed in {@link #query} to the specified value.
274 * Re-initialises {@link #query} to get around the client not getting
275 * updated if client state has been modified.
276 *
277 * @param text The query value to set.
278 */
279 public void setQuery( final String text )
280 {
281 final Component parent = query.getParent();
282 final int index = parent.indexOf( query );
283 parent.remove( index );
284 parent.add( createQuery(), index );
285 query.setText( text );
286 }
287
288 /**
289 * Return the value entered in {@link #maxResults}.
290 */
291 public int getMaxResults()
292 {
293 return ( ( maxResults.getText().length() == 0 ) ? 0 :
294 Integer.parseInt( maxResults.getText() ) );
295 }
296
297 /**
298 * Return the value entered in {@link #maxColumnLength}.
299 */
300 public int getMaxColumnLength()
301 {
302 return ( ( maxColumnLength.getText().length() == 0 ) ? 0 :
303 Integer.parseInt( maxColumnLength.getText() ) );
304 }
305
306 /**
307 * Removes the results of a previous query execution. Removes all the
308 * child components of {@link #results}.
309 */
310 public void reset()
311 {
312 results.removeAll();
313 }
314
315 /**
316 * Create the layout component to use to display the {@link #maxResults}
317 * component and the other controls used to execute the statement.
318 *
319 * @return The layout component.
320 */
321 protected abstract Component createControls();
322
323 /**
324 * Returns {@link #history}.
325 *
326 * @return The value/reference of/to history.
327 */
328 public Map<String,String> getHistory()
329 {
330 return Collections.unmodifiableMap( history );
331 }
332
333 /**
334 * Add the specified statement to {@link #history}. Removes and re-adds
335 * duplicate statements to maintain execution order.
336 *
337 * @param statement The statement that is to be added.
338 */
339 public void addToHistory( final String statement )
340 {
341 final String text = ( ( statement.length() > 100 ) ?
342 statement.substring( 0, 100 ) + "..." : statement );
343
344 if ( history.containsKey( text ) )
345 {
346 history.remove( text );
347 }
348
349 history.put( text, statement );
350 }
351
352 /**
353 * Sets the text displayed in {@link #query} to the specified value
354 * from {@link #history}. The value specified is the <code>key</code>
355 * in {@link #history}. The value set will be <code>value</code>.
356 *
357 * @see #setQuery
358 * @param key The key to use to set the statement.
359 */
360 public void setQueryFromHistory( final String key )
361 {
362 final String value = history.get( key );
363 if ( value != null ) setQuery( value );
364 }
365
366 /**
367 * Returns {@link #connectionManager}.
368 *
369 * @return The value/reference of/to connectionManager.
370 */
371 public ConnectionManager getConnectionManager()
372 {
373 return connectionManager;
374 }
375 }