001: /*
002: * DefaultCompletionHandler.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.gui.completion;
013:
014: import java.awt.Color;
015: import java.awt.Toolkit;
016: import java.util.ArrayList;
017: import java.util.Collections;
018: import java.util.List;
019: import javax.swing.BorderFactory;
020: import javax.swing.JLabel;
021: import javax.swing.ListModel;
022: import javax.swing.event.ListDataEvent;
023: import javax.swing.event.ListDataListener;
024: import workbench.db.WbConnection;
025: import workbench.gui.editor.JEditTextArea;
026: import workbench.interfaces.StatusBar;
027: import workbench.log.LogMgr;
028: import workbench.resource.ResourceMgr;
029: import workbench.resource.Settings;
030: import workbench.sql.ScriptParser;
031: import workbench.util.SqlUtil;
032: import workbench.util.WbThread;
033:
034: /**
035: * Handle the auto completion for tables and columns
036: * @author support@sql-workbench.net
037: */
038: public class DefaultCompletionHandler implements ListModel,
039: CompletionHandler {
040: private JEditTextArea editor;
041: protected List elements = Collections.EMPTY_LIST;
042: protected WbConnection dbConnection;
043: private JLabel header;
044: private List listeners;
045: private CompletionPopup window;
046: protected StatusBar statusBar;
047: private String currentWord;
048:
049: public DefaultCompletionHandler() {
050: header = new JLabel("Tables");
051: header.setForeground(Color.BLUE);
052: header.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 2));
053: }
054:
055: public void setStatusBar(StatusBar bar) {
056: this .statusBar = bar;
057: }
058:
059: public void setEditor(JEditTextArea ed) {
060: this .editor = ed;
061: }
062:
063: public void setConnection(WbConnection conn) {
064: this .dbConnection = conn;
065: if (this .window != null) {
066: this .window
067: .setDbStoresMixedCase(dbConnection != null ? dbConnection
068: .getMetadata().storesMixedCaseIdentifiers()
069: : false);
070: }
071: }
072:
073: protected void showPopup() {
074: try {
075: statusBar.setStatusMessage(ResourceMgr
076: .getString("MsgCompletionRetrievingObjects"));
077: if (this .updateSelectionList()) {
078: this .window.showPopup(currentWord);
079: }
080: } catch (Throwable th) {
081: LogMgr.logError("CompletionHandler.showPopup()",
082: "Error retrieving completion objects", th);
083: statusBar.clearStatusMessage();
084: }
085: }
086:
087: public void cancelPopup() {
088: if (this .window != null)
089: this .window.cancelPopup();
090: }
091:
092: public void showCompletionPopup() {
093: if (this .window == null) {
094: this .window = new CompletionPopup(editor, header, this );
095: this .window
096: .setDbStoresMixedCase(dbConnection != null ? dbConnection
097: .getMetadata().storesMixedCaseIdentifiers()
098: : false);
099: }
100:
101: // if this is not done in a separate thread
102: // the status bar will not be updated...
103: WbThread t = new WbThread("Completion") {
104: public void run() {
105: showPopup();
106: }
107: };
108: t.start();
109: }
110:
111: private boolean updateSelectionList() {
112: boolean result = false;
113: String script = this .editor.getText();
114: ScriptParser parser = new ScriptParser(script);
115: parser.setCheckEscapedQuotes(Settings.getInstance()
116: .getCheckEscapedQuotes());
117: parser.allowEmptyLineAsSeparator(Settings.getInstance()
118: .getAutoCompletionEmptyLineIsSeparator());
119: parser.setAlternateLineComment(dbConnection == null ? null
120: : dbConnection.getDbSettings().getLineComment());
121: int cursorPos = this .editor.getCaretPosition();
122:
123: int index = parser.getCommandIndexAtCursorPos(cursorPos);
124: int commandCursorPos = parser.getIndexInCommand(index,
125: cursorPos);
126: String sql = parser.getCommand(index, false);
127: if (sql == null) {
128: showNoObjectsFoundMessage();
129: return false;
130: }
131:
132: this .currentWord = editor
133: .getWordAtCursor(BaseAnalyzer.SELECT_WORD_DELIM);
134:
135: try {
136: StatementContext ctx = new StatementContext(
137: this .dbConnection, sql, commandCursorPos);
138: if (ctx.isStatementSupported()) {
139: boolean selectWord = (ctx.getAnalyzer()
140: .getOverwriteCurrentWord() && currentWord != null);
141: BaseAnalyzer analyzer = ctx.getAnalyzer();
142: if (analyzer != null && currentWord != null
143: && analyzer.isWbParam()
144: && currentWord.startsWith("-")) {
145: currentWord = currentWord.substring(1);
146: }
147: window.selectCurrentWordInEditor(selectWord);
148: this .elements = ctx.getData();
149: this .header.setText(ctx.getTitle());
150: this .window.setContext(ctx);
151:
152: result = (this .elements != null && this .elements.size() > 0);
153: if (result) {
154: statusBar.clearStatusMessage();
155: fireDataChanged();
156: } else {
157: showNoObjectsFoundMessage();
158: }
159: } else {
160: Toolkit.getDefaultToolkit().beep();
161: showFailedMessage(sql);
162: result = false;
163: }
164: } catch (Exception e) {
165: LogMgr.logError("CompletionHandler.updateSelectionList()",
166: "Error retrieving objects", e);
167: result = false;
168: showNoObjectsFoundMessage();
169: }
170: return result;
171: }
172:
173: private void showNoObjectsFoundMessage() {
174: WbThread t = new WbThread("Notification") {
175: public void run() {
176: String msg = ResourceMgr
177: .getString("MsgCompletionNothingFound");
178: statusBar.setStatusMessage(msg);
179: try {
180: Thread.sleep(2500);
181: } catch (Throwable th) {
182: }
183: String m = statusBar.getText();
184: if (msg.equals(m))
185: statusBar.clearStatusMessage();
186: }
187: };
188: t.start();
189: }
190:
191: private void showFailedMessage(String sql) {
192: final String verb = SqlUtil.getSqlVerb(sql);
193: WbThread t = new WbThread("Notification") {
194: public void run() {
195: String msg = "'"
196: + verb
197: + "' "
198: + ResourceMgr
199: .getString("MsgCompletionNotSupported");
200: statusBar.setStatusMessage(msg);
201: try {
202: Thread.sleep(2500);
203: } catch (Throwable th) {
204: }
205: String m = statusBar.getText();
206: if (msg.equals(m))
207: statusBar.clearStatusMessage();
208: }
209: };
210: t.start();
211: }
212:
213: private void fireDataChanged() {
214: if (this .listeners == null)
215: return;
216: ListDataEvent evt = new ListDataEvent(this ,
217: ListDataEvent.CONTENTS_CHANGED, 0,
218: this .elements.size() - 1);
219: for (int i = 0; i < this .listeners.size(); i++) {
220: ListDataListener l = (ListDataListener) this .listeners
221: .get(i);
222: l.contentsChanged(evt);
223: }
224: }
225:
226: /**
227: * Implementation of the ListModel interface
228: */
229: public Object getElementAt(int index) {
230: if (this .elements == null)
231: return null;
232: return this .elements.get(index);
233: }
234:
235: /**
236: * Implementation of the ListModel interface
237: */
238: public int getSize() {
239: if (this .elements == null)
240: return 0;
241: return this .elements.size();
242: }
243:
244: /**
245: * Implementation of the ListModel interface
246: */
247: public void addListDataListener(ListDataListener listDataListener) {
248: if (this .listeners == null)
249: this .listeners = new ArrayList();
250: this .listeners.add(listDataListener);
251: }
252:
253: /**
254: * Implementation of the ListModel interface
255: */
256: public void removeListDataListener(ListDataListener listDataListener) {
257: if (this.listeners == null)
258: return;
259: this.listeners.remove(listDataListener);
260: }
261:
262: }
|