001: /*
002: * Copyright (C) 2003 Joseph Mocker
003: * mock-sf@misfit.dhs.org
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License
007: * as published by the Free Software Foundation; either version 2
008: * of the License, or any later version.
009: *
010: * This program is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013: * GNU General Public License for more details.
014: *
015: * You should have received a copy of the GNU General Public License
016: * along with this program; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
018: */
019:
020: package net.sourceforge.squirrel_sql.plugins.sqlbookmark;
021:
022: import java.awt.BorderLayout;
023: import java.awt.Container;
024: import java.awt.Frame;
025: import java.awt.event.ActionEvent;
026: import java.awt.event.ActionListener;
027: import java.util.ArrayList;
028: import java.util.HashMap;
029: import java.util.StringTokenizer;
030:
031: import javax.swing.JButton;
032: import javax.swing.JDialog;
033: import javax.swing.JLabel;
034: import javax.swing.JPanel;
035: import javax.swing.JTextField;
036: import javax.swing.SwingConstants;
037:
038: import net.sourceforge.squirrel_sql.client.session.ISQLEntryPanel;
039: import net.sourceforge.squirrel_sql.client.session.ISession;
040: import net.sourceforge.squirrel_sql.fw.gui.PropertyPanel;
041: import net.sourceforge.squirrel_sql.fw.util.ICommand;
042: import net.sourceforge.squirrel_sql.fw.util.StringManager;
043: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
044:
045: /**
046: * Runs a bookmark.
047: *
048: * @author Joseph Mocker
049: **/
050: public class RunBookmarkCommand implements ICommand {
051:
052: private static final StringManager s_stringMgr = StringManagerFactory
053: .getStringManager(RunBookmarkCommand.class);
054:
055: /** Parent frame. */
056: private final Frame frame;
057:
058: /** The session that we are saving a script for. */
059: private final ISession session;
060:
061: /** The current plugin. */
062: private SQLBookmarkPlugin plugin;
063:
064: /** The bookmark to run */
065: private Bookmark bookmark;
066: private ISQLEntryPanel _sqlEntryPanel;
067:
068: /**
069: * Ctor.
070: *
071: * @param frame Parent Frame.
072: * @param session The session that we are saving a script for.
073: * @param bookmark The bookmark to run.
074: * @param plugin The current plugin.
075: *
076: * @param sqlEntryPanel
077: * @throws IllegalArgumentException
078: * Thrown if a <TT>null</TT> <TT>ISession</TT> or <TT>IPlugin</TT>
079: * passed.
080: */
081: public RunBookmarkCommand(Frame frame, ISession session,
082: Bookmark bookmark, SQLBookmarkPlugin plugin,
083: ISQLEntryPanel sqlEntryPanel)
084: throws IllegalArgumentException {
085: super ();
086: _sqlEntryPanel = sqlEntryPanel;
087: if (session == null) {
088: throw new IllegalArgumentException("Null ISession passed");
089: }
090: if (plugin == null) {
091: throw new IllegalArgumentException("Null IPlugin passed");
092: }
093: this .frame = frame;
094: this .session = session;
095: this .plugin = plugin;
096: this .bookmark = bookmark;
097:
098: }
099:
100: /**
101: * Load the Bookmark into the SQL Edit buffer.
102: */
103: public void execute() {
104: if (session != null) {
105: String sql = parseAndLoadSql(bookmark.getSql());
106:
107: if (null != sql) {
108:
109: int caretPosition = _sqlEntryPanel.getCaretPosition();
110: _sqlEntryPanel.replaceSelection(sql);
111: _sqlEntryPanel.setCaretPosition(caretPosition
112: + sql.length());
113: }
114: }
115: }
116:
117: /**
118: * Parse the SQL and prompt the user for entry values.
119: *
120: * Bookmarked SQL strings can have replaceable parameters. At the time
121: * the bookmark is loaded, the user is asked to enter values for any
122: * of the parameters. The parameters come in three forms:
123: * ${prompt[, tip]} - simple anonymous parameter
124: * ${id=name, prompt[, tip]} - named parameter, allows it to be reused
125: * ${ref=name} - use the value of an already named parameter.
126: * where
127: * prompt is the string to display in the popup prompt
128: * tip is the optional tooltip to display on the popup prompt
129: * name is the "variable" name of the parameter.
130:
131: * @param sql The SQL to parse and load.
132: * @return the post-processed SQL.
133: **/
134: protected String parseAndLoadSql(String sql) {
135:
136: // TODO: Make Parameter implement SQLItem interface which has a getString
137: // method which can also be implemented by SQLString, or SQLFragment or
138: // some such. We can then eliminate the use of instanceof below and
139: // clean up the code a bit, by making itemsInSql look like:
140: //
141: // ArrayList<SQLItem> itemsInSql = new ArrayList<SQLItem>();
142: //
143: ArrayList itemsInSql = new ArrayList();
144: HashMap<String, Parameter> paramsById = new HashMap<String, Parameter>();
145: ArrayList<Parameter> parameters = new ArrayList<Parameter>();
146:
147: HashMap<String, Parameter> lookup = new HashMap<String, Parameter>();
148:
149: //
150: // First parse the SQL string
151: //
152: int start = 0;
153: int idx = 0;
154: while ((idx = sql.indexOf("${", start)) >= 0) {
155: int ridx = sql.indexOf("}", idx);
156: if (ridx < 0)
157: break;
158:
159: String arg = sql.substring(idx + 2, ridx);
160: itemsInSql.add(sql.substring(start, idx));
161: start = ridx + 1;
162:
163: StringTokenizer st = new StringTokenizer(arg, ",");
164: Parameter parameter = new Parameter();
165: if (arg.startsWith("ref=")) {
166: String ref = st.nextToken();
167: parameter.reference = ref.substring(4);
168: } else if (arg.startsWith("id=")) {
169: String id = st.nextToken();
170: String prompt = st.nextToken();
171: parameter.id = id.substring(3);
172: parameter.prompt = prompt;
173: if (st.countTokens() > 0) {
174: String tip = st.nextToken();
175: parameter.tip = tip;
176: }
177: } else {
178: String prompt = st.nextToken();
179: parameter.prompt = prompt;
180: if (st.countTokens() > 0) {
181: String tip = st.nextToken();
182: parameter.tip = tip;
183: }
184: }
185:
186: if (parameter.reference == null) {
187:
188: // 1646886: If we've already seen the parameter, don't create another
189: // instance as this will force the user to enter the same value twice.
190: // Add the previous instance to itemsInSql though so that the parameters
191: // value gets propagated to the right spot(s) in the SQL statement.
192: if (lookup.containsKey(parameter.prompt)) {
193: parameter = lookup.get(parameter.prompt);
194: } else {
195: lookup.put(parameter.prompt, parameter);
196: parameters.add(parameter);
197: }
198:
199: }
200: if (parameter.id != null) {
201: paramsById.put(parameter.id, parameter);
202: }
203: itemsInSql.add(parameter);
204: }
205: itemsInSql.add(sql.substring(start));
206:
207: DoneAction doneAction = null;
208: //
209: // If there are parameters in the SQL string, then we need
210: // to prompt for some answers.
211: //
212: if (parameters.size() > 0) {
213: // i18n[sqlbookmark.qureyParams=Query Parameters]
214: JDialog dialog = new JDialog(frame, s_stringMgr
215: .getString("sqlbookmark.qureyParams"), true);
216: Container contentPane = dialog.getContentPane();
217: contentPane.setLayout(new BorderLayout());
218:
219: PropertyPanel propPane = new PropertyPanel();
220: contentPane.add(propPane, BorderLayout.CENTER);
221:
222: for (idx = 0; idx < parameters.size(); idx++) {
223: Parameter parameter = parameters.get(idx);
224:
225: JLabel label = new JLabel(parameter.prompt + ":",
226: SwingConstants.RIGHT);
227: if (parameter.tip != null)
228: label.setToolTipText(parameter.tip);
229:
230: JTextField value = new JTextField(20);
231: propPane.add(label, value);
232:
233: parameter.value = value;
234: }
235:
236: JPanel actionPane = new JPanel();
237: contentPane.add(actionPane, BorderLayout.SOUTH);
238:
239: // i18n[sqlbookmark.btnOk=OK]
240: JButton done = new JButton(s_stringMgr
241: .getString("sqlbookmark.btnOk"));
242: actionPane.add(done);
243: doneAction = new DoneAction(dialog);
244: done.addActionListener(doneAction);
245: dialog.getRootPane().setDefaultButton(done);
246: dialog.setLocationRelativeTo(frame);
247: dialog.pack();
248: dialog.setVisible(true);
249: }
250:
251: if (null == doneAction || doneAction.actionExecuted()) {
252: //
253: // No go through the parse SQL and build the final SQL replacing
254: // parameters with values is goes.
255: //
256: StringBuffer sqlbuf = new StringBuffer();
257: for (idx = 0; idx < itemsInSql.size(); idx++) {
258: Object item = itemsInSql.get(idx);
259: if (item instanceof String)
260: sqlbuf.append((String) item);
261: if (item instanceof Parameter) {
262: Parameter parameter = (Parameter) item;
263: if (parameter.reference != null)
264: parameter = paramsById.get(parameter.reference);
265:
266: sqlbuf.append(parameter.value.getText());
267: }
268: }
269:
270: return sqlbuf.toString();
271:
272: } else {
273: return null;
274: }
275:
276: }
277:
278: /**
279: * Internal convenience class for managing attributes of a parameter.
280: */
281: static class Parameter {
282: String reference;
283: String id;
284: String prompt;
285: String tip;
286: JTextField value;
287: }
288:
289: /**
290: * Internal action class called when user clicks the "OK" button.
291: */
292: static class DoneAction implements ActionListener {
293:
294: JDialog dialog = null;
295: private boolean _actionExecuted;
296:
297: public DoneAction(JDialog dialog) {
298: super ();
299: this .dialog = dialog;
300: }
301:
302: public void actionPerformed(ActionEvent e) {
303: _actionExecuted = true;
304: dialog.dispose();
305: }
306:
307: public boolean actionExecuted() {
308: return _actionExecuted;
309: }
310: }
311:
312: }
|