KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > editor > completion > CompletionImpl


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.editor.completion;
21
22 import java.awt.*;
23 import java.awt.event.*;
24 import java.lang.ref.WeakReference JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.Collections JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.List JavaDoc;
31 import javax.swing.*;
32 import javax.swing.event.ListSelectionListener JavaDoc;
33 import javax.swing.event.CaretListener JavaDoc;
34 import javax.swing.event.ChangeListener JavaDoc;
35 import javax.swing.event.DocumentListener JavaDoc;
36 import javax.swing.plaf.TextUI JavaDoc;
37 import javax.swing.text.*;
38
39 import org.netbeans.api.editor.mimelookup.MimeLookup;
40 import org.netbeans.api.editor.mimelookup.MimePath;
41 import org.netbeans.editor.BaseDocument;
42 import org.netbeans.editor.BaseKit;
43 import org.netbeans.editor.Settings;
44 import org.netbeans.editor.SettingsChangeListener;
45 import org.netbeans.editor.SettingsNames;
46 import org.netbeans.lib.editor.util.swing.DocumentUtilities;
47 import org.netbeans.lib.editor.util.swing.DocumentListenerPriority;
48 import org.netbeans.editor.Registry;
49 import org.netbeans.editor.Utilities;
50 import org.netbeans.editor.ext.ExtKit;
51 import org.netbeans.spi.editor.completion.*;
52 import org.openide.ErrorManager;
53 import org.openide.util.Lookup;
54 import org.openide.util.NbBundle;
55
56 /**
57  * Implementation of the completion processing.
58  * The visual related processing is done in AWT thread together
59  * with completion providers invocation and result set sorting.
60  * <br>
61  * The only thing that can be done outside of the AWT
62  * is hiding of the completion/documentation/tooltip.
63  *
64  * <p>
65  * The completion providers typically reschedule computation intensive
66  * collecting of their result set into an extra thread to keep the GUI responsive.
67  *
68  * @author Dusan Balek, Miloslav Metelka
69  */

70
71 public class CompletionImpl extends MouseAdapter implements DocumentListener JavaDoc,
72 CaretListener JavaDoc, KeyListener, FocusListener, ListSelectionListener JavaDoc, ChangeListener JavaDoc, SettingsChangeListener {
73     
74     private static final boolean debug = Boolean.getBoolean("org.netbeans.modules.editor.completion.debug");
75     private static final boolean allowFallbacks = !Boolean.getBoolean("org.netbeans.modules.editor.completion.noFallbacks");
76     private static final boolean alphaSort = Boolean.getBoolean("org.netbeans.modules.editor.completion.alphabeticalSort"); // [TODO] create an option
77

78     private static CompletionImpl singleton = null;
79
80     private static final String JavaDoc NO_SUGGESTIONS = NbBundle.getMessage(CompletionImpl.class, "completion-no-suggestions");
81     private static final String JavaDoc PLEASE_WAIT = NbBundle.getMessage(CompletionImpl.class, "completion-please-wait");
82
83     private static final String JavaDoc COMPLETION_SHOW = "completion-show"; //NOI18N
84
private static final String JavaDoc COMPLETION_ALL_SHOW = "completion-all-show"; //NOI18N
85
private static final String JavaDoc DOC_SHOW = "doc-show"; //NOI18N
86
private static final String JavaDoc TOOLTIP_SHOW = "tooltip-show"; //NOI18N
87

88     private static final int PLEASE_WAIT_TIMEOUT = 750;
89     private static final int PRESCAN = 50;
90     
91     public static CompletionImpl get() {
92         if (singleton == null)
93             singleton = new CompletionImpl();
94         return singleton;
95     }
96
97     static LazyListModel.Filter filter = new LazyListModel.Filter() {
98         public boolean accept(Object JavaDoc obj) {
99             if (obj instanceof LazyCompletionItem)
100                 return ((LazyCompletionItem)obj).accept();
101             return true;
102         }
103         public void scheduleUpdate(Runnable JavaDoc run) {
104             SwingUtilities.invokeLater( run );
105         }
106     };
107     
108     /** Text component being currently edited. Changed in AWT only. */
109     private WeakReference JavaDoc<JTextComponent> activeComponent = null;
110     
111     /** Document currently installed in the active component. Changed in AWT only. */
112     private WeakReference JavaDoc<Document> activeDocument = null;
113     
114     /** Map containing keystrokes that should be overriden by completion processing. Changed in AWT only. */
115     private InputMap inputMap;
116     
117     /** Action map containing actions bound to keys through input map. Changed in AWT only. */
118     private ActionMap actionMap;
119
120     /** Layout of the completion pane/documentation/tooltip. Changed in AWT only. */
121     private final CompletionLayout layout = new CompletionLayout();
122     
123     /* Completion providers registered for the active component (its mime-type). Changed in AWT only. */
124     private CompletionProvider[] activeProviders = null;
125     
126     /** Mapping of mime-type to array of providers. Changed in AWT only. */
127     private HashMap JavaDoc<String JavaDoc, CompletionProvider[]> providersCache = new HashMap JavaDoc<String JavaDoc, CompletionProvider[]>();
128
129     /**
130      * Result of the completion query.
131      * <br>
132      * It may be null which means that the query was cancelled.
133      * <br>
134      * Initiated in AWT and can be cleared from the thread that cancels the completion query.
135      */

136     private Result completionResult;
137     
138     /**
139      * Result of the documentation query.
140      * <br>
141      * It may be null which means that the query was cancelled.
142      * <br>
143      * Initiated in AWT and can be cleared from the thread that cancels the documentation query.
144      */

145     private Result docResult;
146     
147     /**
148      * Result of the tooltip query.
149      * <br>
150      * It may be null which means that the query was cancelled.
151      * <br>
152      * Initiated in AWT and can be cleared from the thread that cancels the tooltip query.
153      */

154     private Result toolTipResult;
155     
156     /** Timer for opening completion automatically. Changed in AWT only. */
157     private Timer completionAutoPopupTimer;
158     /** Timer for opening documentation window automatically. Changed in AWT only. */
159     private Timer docAutoPopupTimer;
160     /** Timer for opening Please Wait popup. Changed in AWT only. */
161     private Timer pleaseWaitTimer;
162     /** Whether it's initial or refreshed query. Changed in AWT only. */
163     private boolean refreshedQuery = false;
164     /** Whether it's explicit or automatic query. Changed in AWT only. */
165     private boolean explicitQuery = false;
166     
167     private WeakReference JavaDoc<CompletionItem> lastSelectedItem = null;
168     
169     /** Ending offset of the recent autopopup modification. */
170     private int autoModEndOffset;
171     
172     private boolean pleaseWaitDisplayed = false;
173     
174     private CompletionImpl() {
175         Registry.addChangeListener(this);
176         completionAutoPopupTimer = new Timer(0, new ActionListener() {
177             public void actionPerformed(ActionEvent e) {
178                 Result localCompletionResult;
179                 synchronized (this) {
180                     localCompletionResult = completionResult;
181                 }
182                 if (localCompletionResult != null && !localCompletionResult.isQueryInvoked()) {
183                     pleaseWaitTimer.restart();
184                     queryResultSets(localCompletionResult.getResultSets());
185                     localCompletionResult.queryInvoked();
186                 }
187             }
188         });
189         completionAutoPopupTimer.setRepeats(false);
190         
191         docAutoPopupTimer = new Timer(0, new ActionListener() {
192             public void actionPerformed(ActionEvent e) {
193                 if (lastSelectedItem == null || lastSelectedItem.get() != layout.getSelectedCompletionItem())
194                     showDocumentation();
195             }
196         });
197         docAutoPopupTimer.setRepeats(false);
198
199         pleaseWaitTimer = new Timer(PLEASE_WAIT_TIMEOUT, new ActionListener() {
200             public void actionPerformed(ActionEvent e) {
201                 String JavaDoc waitText = PLEASE_WAIT;
202                 Result localCompletionResult;
203                 synchronized (this) {
204                     localCompletionResult = completionResult;
205                 }
206                 if (localCompletionResult != null) {
207                     for (Iterator JavaDoc it = localCompletionResult.getResultSets().iterator(); it.hasNext();) {
208                         CompletionResultSetImpl resultSet = (CompletionResultSetImpl)it.next();
209                         if (resultSet.getWaitText() != null) {
210                             waitText = resultSet.getWaitText();
211                             break;
212                         }
213                     }
214                 }
215                 layout.showCompletion(Collections.singletonList(waitText),
216                         null, -1, CompletionImpl.this, false, 0);
217                 pleaseWaitDisplayed = true;
218             }
219         });
220         pleaseWaitTimer.setRepeats(false);
221         Settings.addSettingsChangeListener(this);
222     }
223     
224     private JTextComponent getActiveComponent() {
225         return activeComponent != null ? activeComponent.get() : null;
226     }
227
228     private Document getActiveDocument() {
229         return activeDocument != null ? activeDocument.get() : null;
230     }
231     
232     int getSortType() {
233         return alphaSort ? CompletionResultSet.TEXT_SORT_TYPE : CompletionResultSet.PRIORITY_SORT_TYPE;
234     }
235     
236     public void insertUpdate(javax.swing.event.DocumentEvent JavaDoc e) {
237         // Ignore insertions done outside of the AWT (various content generation)
238
if (!SwingUtilities.isEventDispatchThread()) {
239             return;
240         }
241         // Check whether the insertion came from typing
242
if (!DocumentUtilities.isTypingModification(e)) {
243             return;
244         }
245
246         if (activeProviders != null) {
247             try {
248                 int modEndOffset = e.getOffset() + e.getLength();
249                 if (getActiveComponent().getSelectionStart() != modEndOffset)
250                     return;
251
252                 String JavaDoc typedText = e.getDocument().getText(e.getOffset(), e.getLength());
253                 for (int i = 0; i < activeProviders.length; i++) {
254                     int type = activeProviders[i].getAutoQueryTypes(getActiveComponent(), typedText);
255                     boolean completionResultNull;
256                     synchronized (this) {
257                         completionResultNull = (completionResult == null);
258                     }
259                     if (completionResultNull && (type & CompletionProvider.COMPLETION_QUERY_TYPE) != 0 &&
260                             CompletionSettings.INSTANCE.completionAutoPopup()) {
261                         autoModEndOffset = modEndOffset;
262                         showCompletion(false, true, CompletionProvider.COMPLETION_QUERY_TYPE);
263                     }
264
265                     boolean tooltipResultNull;
266                     synchronized (this) {
267                         tooltipResultNull = (toolTipResult == null);
268                     }
269                     if (tooltipResultNull && (type & CompletionProvider.TOOLTIP_QUERY_TYPE) != 0) {
270                         showToolTip();
271                     }
272                 }
273             } catch (BadLocationException ex) {}
274             if (completionAutoPopupTimer.isRunning())
275                 restartCompletionAutoPopupTimer();
276         }
277     }
278     
279     public void removeUpdate(javax.swing.event.DocumentEvent JavaDoc e) {
280         // Ignore insertions done outside of the AWT (various content generation)
281
if (!SwingUtilities.isEventDispatchThread()) {
282             return;
283         }
284     }
285     
286     public void changedUpdate(javax.swing.event.DocumentEvent JavaDoc e) {
287     }
288     
289     public synchronized void caretUpdate(javax.swing.event.CaretEvent JavaDoc e) {
290         assert (SwingUtilities.isEventDispatchThread());
291
292         if (activeProviders != null) {
293             // Check whether there is an active result being computed but not yet displayed
294
// Caret update should be notified AFTER document modifications
295
// thank to document listener priorities
296
Result localCompletionResult;
297             synchronized (this) {
298                 localCompletionResult = completionResult;
299             }
300             if ((completionAutoPopupTimer.isRunning() || localCompletionResult != null)
301                 && (!layout.isCompletionVisible() || pleaseWaitDisplayed)
302                 && e.getDot() != autoModEndOffset) {
303                 hideCompletion(false);
304             }
305
306             SwingUtilities.invokeLater(new Runnable JavaDoc() {
307                 public void run() {
308                     completionRefresh();
309                     toolTipRefresh();
310                 }
311             });
312         }
313     }
314
315     public void keyPressed(KeyEvent e) {
316         dispatchKeyEvent(e);
317     }
318
319     public void keyReleased(KeyEvent e) {
320         dispatchKeyEvent(e);
321     }
322
323     public void keyTyped(KeyEvent e) {
324         dispatchKeyEvent(e);
325     }
326
327     public void focusGained(FocusEvent e) {
328     }
329
330     public void focusLost(FocusEvent e) {
331         hideAll();
332     }
333
334     public void mouseClicked(MouseEvent e) {
335         hideAll();
336     }
337     
338     public void hideAll() {
339         hideToolTip();
340         hideCompletion(true);
341         hideDocumentation(true);
342     }
343
344     /**
345      * Called from AWT when selection in the completion list pane changes.
346      */

347     public void valueChanged(javax.swing.event.ListSelectionEvent JavaDoc e) {
348         assert (SwingUtilities.isEventDispatchThread());
349
350         if (layout.isDocumentationVisible() || CompletionSettings.INSTANCE.documentationAutoPopup()) {
351             restartDocumentationAutoPopupTimer();
352         }
353     }
354
355     /**
356      * Expected to be called from the AWT only.
357      */

358     public void stateChanged(javax.swing.event.ChangeEvent JavaDoc e) {
359         assert (SwingUtilities.isEventDispatchThread()); // expected in AWT only
360

361         boolean cancel = false;
362         JTextComponent component = Registry.getMostActiveComponent();
363         if (component != getActiveComponent()) {
364             activeProviders = getCompletionProvidersForComponent(component);
365             if (debug) {
366                 StringBuffer JavaDoc sb = new StringBuffer JavaDoc("Completion PROVIDERS:\n"); // NOI18N
367
if (activeProviders != null) {
368                     for (int i = 0; i < activeProviders.length; i++) {
369                         sb.append("providers["); // NOI18N
370
sb.append(i);
371                         sb.append("]: "); // NOI18N
372
sb.append(activeProviders[i].getClass());
373                         sb.append('\n');
374                     }
375                 }
376                 System.err.println(sb.toString());
377             }
378             if (getActiveComponent() != null) {
379                 getActiveComponent().removeCaretListener(this);
380                 getActiveComponent().removeKeyListener(this);
381                 getActiveComponent().removeFocusListener(this);
382                 getActiveComponent().removeMouseListener(this);
383             }
384             if (component != null) {
385                 if (activeProviders != null) {
386                     component.addCaretListener(this);
387                     component.addKeyListener(this);
388                     component.addFocusListener(this);
389                     component.addMouseListener(this);
390                 }
391             }
392             activeComponent = component != null ? new WeakReference JavaDoc<JTextComponent>(component) : null;
393             CompletionSettings.INSTANCE.notifyEditorComponentChange(getActiveComponent());
394             layout.setEditorComponent(getActiveComponent());
395             installKeybindings();
396             cancel = true;
397         }
398         Document document = Registry.getMostActiveDocument();
399         if (component != null && document == component.getDocument() && document != getActiveDocument()) {
400             activeProviders = getCompletionProvidersForComponent(component);
401             if (debug) {
402                 StringBuffer JavaDoc sb = new StringBuffer JavaDoc("Completion PROVIDERS:\n"); // NOI18N
403
if (activeProviders != null) {
404                     for (int i = 0; i < activeProviders.length; i++) {
405                         sb.append("providers["); // NOI18N
406
sb.append(i);
407                         sb.append("]: "); // NOI18N
408
sb.append(activeProviders[i].getClass());
409                         sb.append('\n');
410                     }
411                 }
412                 System.err.println(sb.toString());
413             }
414             if (getActiveDocument() != null)
415                 DocumentUtilities.removeDocumentListener(getActiveDocument(), this,
416                         DocumentListenerPriority.AFTER_CARET_UPDATE);
417             if (activeProviders != null)
418                 DocumentUtilities.addDocumentListener(document, this,
419                         DocumentListenerPriority.AFTER_CARET_UPDATE);
420             activeDocument = new WeakReference JavaDoc<Document>(document);
421             cancel = true;
422         }
423         if (cancel)
424             completionCancel();
425     }
426     
427     private void restartCompletionAutoPopupTimer() {
428         assert (SwingUtilities.isEventDispatchThread()); // expect in AWT only
429

430         int completionDelay = CompletionSettings.INSTANCE.completionAutoPopupDelay();
431         completionAutoPopupTimer.setInitialDelay(completionDelay);
432         completionAutoPopupTimer.restart();
433     }
434     
435     private void restartDocumentationAutoPopupTimer() {
436         assert (SwingUtilities.isEventDispatchThread()); // expect in AWT only
437

438         int docDelay = CompletionSettings.INSTANCE.documentationAutoPopupDelay();
439         docAutoPopupTimer.setInitialDelay(docDelay);
440         docAutoPopupTimer.restart();
441     }
442     
443     private CompletionProvider[] getCompletionProvidersForComponent(JTextComponent component) {
444         assert (SwingUtilities.isEventDispatchThread());
445
446         if (component == null)
447             return null;
448         
449         Object JavaDoc mimeTypeObj = component.getDocument().getProperty("mimeType"); //NOI18N
450
String JavaDoc mimeType;
451         
452         if (mimeTypeObj instanceof String JavaDoc)
453             mimeType = (String JavaDoc) mimeTypeObj;
454         else {
455             BaseKit kit = Utilities.getKit(component);
456             
457             if (kit == null) {
458                 return new CompletionProvider[0];
459             }
460             
461             mimeType = kit.getContentType();
462         }
463         
464         if (providersCache.containsKey(mimeType))
465             return (CompletionProvider[])providersCache.get(mimeType);
466
467         Lookup lookup = MimeLookup.getLookup(MimePath.get(mimeType));
468         Collection JavaDoc<? extends CompletionProvider> col = lookup.lookupAll(CompletionProvider.class);
469         int size = col.size();
470         CompletionProvider[] ret = size == 0 ? null : col.toArray(new CompletionProvider[size]);
471         providersCache.put(mimeType, ret);
472         return ret;
473     }
474     
475     private void dispatchKeyEvent(KeyEvent e) {
476         if (e == null)
477             return;
478         KeyStroke ks = KeyStroke.getKeyStrokeForEvent(e);
479         JTextComponent comp = getActiveComponent();
480         boolean compEditable = (comp != null && comp.isEditable());
481         Object JavaDoc obj = inputMap.get(ks);
482         if (obj != null) {
483             Action action = actionMap.get(obj);
484             if (action != null) {
485                 if (compEditable)
486                     action.actionPerformed(null);
487                 e.consume();
488                 return;
489             }
490         }
491         if (layout.isCompletionVisible()) {
492             CompletionItem item = layout.getSelectedCompletionItem();
493             if (item != null) {
494                 if (compEditable)
495                     item.processKeyEvent(e);
496                 if (e.isConsumed()) {
497                     return;
498                 }
499                 // Call default action if ENTER was pressed
500
if (e.getKeyCode() == KeyEvent.VK_ENTER && e.getID() == KeyEvent.KEY_PRESSED) {
501                     e.consume();
502                     if (compEditable)
503                         item.defaultAction(getActiveComponent());
504                     return;
505                 }
506             } else if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN
507                     || e.getKeyCode() == KeyEvent.VK_PAGE_UP || e.getKeyCode() == KeyEvent.VK_PAGE_DOWN
508                     || e.getKeyCode() == KeyEvent.VK_HOME || e.getKeyCode() == KeyEvent.VK_END) {
509                 hideCompletion(false);
510             }
511             if (e.getKeyCode() == KeyEvent.VK_TAB) {
512                 e.consume();
513                 if (compEditable)
514                     insertCommonPrefix();
515                 return;
516             }
517         }
518         layout.processKeyEvent(e);
519     }
520     
521     private void completionQuery(boolean delayQuery, int queryType) {
522         refreshedQuery = false;
523         
524         Result newCompletionResult = new Result(activeProviders.length);
525         synchronized (this) {
526             assert (completionResult == null);
527             completionResult = newCompletionResult;
528         }
529         List JavaDoc<CompletionResultSetImpl> completionResultSets = newCompletionResult.getResultSets();
530
531         // Initialize the completion tasks
532
for (int i = 0; i < activeProviders.length; i++) {
533             CompletionTask compTask = activeProviders[i].createTask(
534                     queryType, getActiveComponent());
535             if (compTask != null) {
536                 CompletionResultSetImpl resultSet = new CompletionResultSetImpl(
537                         this, newCompletionResult, compTask, queryType);
538                 completionResultSets.add(resultSet);
539             }
540         }
541         
542         if (completionResultSets.size() > 0) {
543             // Query the tasks
544
if (delayQuery) {
545                 restartCompletionAutoPopupTimer();
546             } else {
547                 pleaseWaitTimer.restart();
548                 queryResultSets(completionResultSets);
549                 newCompletionResult.queryInvoked();
550             }
551         } else {
552             completionCancel();
553             layout.showCompletion(Collections.singletonList(NO_SUGGESTIONS), null, -1, CompletionImpl.this, false, 0);
554             pleaseWaitDisplayed = false;
555         }
556     }
557
558     /**
559      * Called from caretUpdate() to refresh the completion result after caret move.
560      * <br>
561      * Must be called in AWT thread.
562      */

563     private void completionRefresh() {
564         Result localCompletionResult;
565         synchronized (this) {
566             localCompletionResult = completionResult;
567         }
568         if (localCompletionResult != null) {
569             refreshedQuery = true;
570             Result refreshResult = localCompletionResult.createRefreshResult();
571             synchronized (this) {
572                 completionResult = refreshResult;
573             }
574             refreshResult.invokeRefresh();
575         }
576     }
577     
578     private void completionCancel() {
579         Result oldCompletionResult;
580         synchronized (this) {
581             oldCompletionResult = completionResult;
582             completionResult = null;
583         }
584         if (oldCompletionResult != null) {
585             oldCompletionResult.cancel();
586         }
587     }
588     
589     /**
590      * Called from dispatchKeyEvent() to insert prefix common to all items in the
591      * completion result after TAB.<br>
592      * Must be called in AWT thread after all tasks of the current completionResult are finished.
593      */

594     private void insertCommonPrefix() {
595         JTextComponent c = getActiveComponent();
596         Result localCompletionResult;
597         synchronized (this) {
598             localCompletionResult = completionResult;
599         }
600         if (localCompletionResult != null) {
601             CharSequence JavaDoc commonText = null;
602             int anchorOffset = -1;
603 outer: for (Iterator JavaDoc it = localCompletionResult.getResultSets().iterator(); it.hasNext();) {
604                 CompletionResultSetImpl resultSet = (CompletionResultSetImpl)it.next();
605                 if (anchorOffset == -1)
606                     anchorOffset = resultSet.getAnchorOffset();
607                 for (Iterator JavaDoc itt = resultSet.getItems().iterator(); itt.hasNext();) {
608                     CharSequence JavaDoc text = ((CompletionItem)itt.next()).getInsertPrefix();
609                     if (text == null) {
610                         commonText = null;
611                         break outer;
612                     }
613                     if (commonText == null) {
614                         commonText = text;
615                     } else {
616                         // Get the largest common part
617
int minLen = Math.min(text.length(), commonText.length());
618                         for (int commonInd = 0; commonInd < minLen; commonInd++) {
619                             if (text.charAt(commonInd) != commonText.charAt(commonInd)) {
620                                 if (commonInd == 0) {
621                                     commonText = null;
622                                     break outer; // no common text
623
}
624                                 commonText = commonText.subSequence(0, commonInd);
625                                 break;
626                             }
627                         }
628                     }
629                 }
630             }
631             if (commonText != null) {
632                 int caretOffset = c.getSelectionStart();
633                 if (anchorOffset > -1 && caretOffset - anchorOffset < commonText.length()) {
634                     commonText = commonText.subSequence(caretOffset - anchorOffset, commonText.length());
635                     // Insert the missing end part of the prefix
636
BaseDocument doc = (BaseDocument)getActiveDocument();
637                     doc.atomicLock();
638                     try {
639                         doc.insertString(caretOffset, commonText.toString(), null);
640                     } catch (BadLocationException e) {
641                     } finally {
642                         doc.atomicUnlock();
643                     }
644                 }
645             }
646         }
647     }
648     
649     /**
650      * May be called from any thread but it will be rescheduled into AWT.
651      */

652     public void showCompletion() {
653         showCompletion(false, false, CompletionProvider.COMPLETION_QUERY_TYPE);
654     }
655
656     private void showCompletion(boolean explicitQuery, boolean delayQuery, int queryType) {
657         if (!SwingUtilities.isEventDispatchThread()) {
658             // Re-call this method in AWT if necessary
659
SwingUtilities.invokeLater(new ParamRunnable(ParamRunnable.SHOW_COMPLETION, explicitQuery, delayQuery, queryType));
660             return;
661         }
662
663         this.explicitQuery = explicitQuery;
664         if (activeProviders != null) {
665             completionAutoPopupTimer.stop();
666             synchronized(this) {
667                 if (explicitQuery && completionResult != null)
668                     queryType = CompletionProvider.COMPLETION_ALL_QUERY_TYPE;
669             }
670             completionCancel(); // cancel possibly pending query
671
completionQuery(delayQuery, queryType);
672         }
673     }
674
675     /**
676      * Request displaying of the completion pane.
677      * Can be called from any thread - is called synchronously
678      * from the thread that finished last unfinished result.
679      */

680     void requestShowCompletionPane(Result result) {
681         pleaseWaitTimer.stop();
682         
683         // Compute total count of the result sets
684
int sortedResultsSize = 0;
685         int qType = 0;
686         List JavaDoc<CompletionResultSetImpl> completionResultSets = result.getResultSets();
687         for (int i = completionResultSets.size() - 1; i >= 0; i--) {
688             CompletionResultSetImpl resultSet = completionResultSets.get(i);
689             sortedResultsSize += resultSet.getItems().size();
690             qType = resultSet.getQueryType();
691         }
692         
693         // Collect and sort the gathered completion items
694
final List JavaDoc<CompletionItem> sortedResultItems = new ArrayList JavaDoc<CompletionItem>(sortedResultsSize);
695         String JavaDoc title = null;
696         int anchorOffset = -1;
697         int cnt = 0;
698         for (int i = 0; i < completionResultSets.size(); i++) {
699             CompletionResultSetImpl resultSet = (CompletionResultSetImpl)completionResultSets.get(i);
700             List JavaDoc<? extends CompletionItem> resultItems = resultSet.getItems();
701             if (resultItems.size() > 0) {
702                 if (cnt < PRESCAN) {
703                     for (CompletionItem item : resultItems) {
704                         if (cnt < PRESCAN && !filter.accept(item))
705                             continue;
706                         sortedResultItems.add(item);
707                         cnt++;
708                     }
709                 } else {
710                     sortedResultItems.addAll(resultItems);
711                 }
712                 if (title == null)
713                     title = resultSet.getTitle();
714                 if (anchorOffset == -1)
715                     anchorOffset = resultSet.getAnchorOffset();
716             }
717         }
718         Collections.sort(sortedResultItems, CompletionItemComparator.get(getSortType()));
719         
720         // Request displaying of the completion pane in AWT thread
721
final String JavaDoc displayTitle = title;
722         final int displayAnchorOffset = anchorOffset;
723         final int queryType = qType;
724         final boolean noSuggestions = sortedResultsSize == 0;
725         Runnable JavaDoc requestShowRunnable = new Runnable JavaDoc() {
726             public void run() {
727                 int caretOffset = getActiveComponent().getSelectionStart();
728                 // completionResults = null;
729
if (sortedResultItems.size() == 1 && !refreshedQuery && explicitQuery
730                         && CompletionSettings.INSTANCE.completionInstantSubstitution()
731                         && getActiveComponent().isEditable()) {
732                     try {
733                         int[] block = Utilities.getIdentifierBlock(getActiveComponent(), caretOffset);
734                         if (block == null || block[1] == caretOffset) { // NOI18N
735
CompletionItem item = (CompletionItem) sortedResultItems.get(0);
736                             if (item.instantSubstitution(getActiveComponent())) {
737                                 return;
738                             }
739                         }
740                     } catch (BadLocationException ex) {
741                     }
742                 }
743                 
744                 int selectedIndex = getCompletionPreSelectionIndex(sortedResultItems);
745                 layout.showCompletion(noSuggestions ? Collections.singletonList(NO_SUGGESTIONS) : sortedResultItems, displayTitle, displayAnchorOffset, CompletionImpl.this, allowFallbacks && queryType == CompletionProvider.COMPLETION_QUERY_TYPE, selectedIndex);
746                 pleaseWaitDisplayed = false;
747
748                 // Show documentation as well if set by default
749
if (CompletionSettings.INSTANCE.documentationAutoPopup()) {
750                     if (noSuggestions) {
751                         docAutoPopupTimer.stop(); // Ensure the popup timer gets stopped
752
documentationCancel();
753                         layout.hideDocumentation();
754                     } else {
755                         restartDocumentationAutoPopupTimer();
756                     }
757                 }
758             }
759         };
760         runInAWT(requestShowRunnable);
761     }
762     
763     private int getCompletionPreSelectionIndex(List JavaDoc<CompletionItem> items) {
764         String JavaDoc prefix = null;
765         BaseDocument doc = (BaseDocument)getActiveDocument();
766         int caretOffset = getActiveComponent().getSelectionStart();
767         try {
768             int[] block = Utilities.getIdentifierBlock(doc, caretOffset);
769             if (block != null) {
770                 block[1] = caretOffset;
771                 prefix = doc.getText(block);
772             }
773         } catch (BadLocationException ble) {
774         }
775         if (prefix != null && prefix.length() > 0) {
776             int idx = 0;
777             for (CompletionItem item : items) {
778                 if (item.getInsertPrefix().toString().startsWith(prefix))
779                     return idx;
780                 idx++;
781             }
782         }
783         return 0;
784     }
785
786     /**
787      * May be called from any thread. The UI changes will be rescheduled into AWT.
788      */

789     public boolean hideCompletion() {
790         return hideCompletion(true);
791     }
792     
793     public boolean hideCompletion(boolean completionOnly) {
794         completionCancel();
795         // Invoke hideCompletionPane() in AWT
796
if (!SwingUtilities.isEventDispatchThread()) {
797             SwingUtilities.invokeLater(new ParamRunnable(ParamRunnable.HIDE_COMPLETION_PANE, completionOnly));
798             return false;
799         } else { // in AWT
800
return hideCompletionPane(completionOnly);
801         }
802     }
803     
804     /**
805      * Hide the completion pane. This must be called in AWT thread.
806      */

807     private boolean hideCompletionPane(boolean completionOnly) {
808         completionAutoPopupTimer.stop(); // Ensure the popup timer gets stopped
809
pleaseWaitTimer.stop();
810         boolean hidePerformed = layout.hideCompletion();
811         pleaseWaitDisplayed = false;
812         if (!completionOnly && hidePerformed && CompletionSettings.INSTANCE.documentationAutoPopup()) {
813             hideDocumentation(true);
814         }
815         return hidePerformed;
816     }
817     
818     /**
819      * May be called from any thread but it will be rescheduled into AWT.
820      */

821     public void showDocumentation() {
822         if (!SwingUtilities.isEventDispatchThread()) {
823             // Re-call this method in AWT if necessary
824
SwingUtilities.invokeLater(new ParamRunnable(ParamRunnable.SHOW_DOCUMENTATION));
825             return;
826         }
827
828         if (activeProviders != null) {
829             documentationCancel();
830             layout.clearDocumentationHistory();
831             documentationQuery();
832         }
833     }
834
835     /**
836      * Request displaying of the documentation pane.
837      * Can be called from any thread - is called synchronously
838      * from the thread that finished last unfinished result.
839      */

840     void requestShowDocumentationPane(Result result) {
841         final CompletionResultSetImpl resultSet = findFirstValidResult(result.getResultSets());
842         runInAWT(new Runnable JavaDoc() {
843             public void run() {
844                 synchronized (CompletionImpl.this) {
845                     if (resultSet != null) {
846                         layout.showDocumentation(
847                                 resultSet.getDocumentation(), resultSet.getAnchorOffset());
848                     } else {
849                         documentationCancel();
850                         layout.hideDocumentation();
851                     }
852                 }
853             }
854         });
855     }
856
857     /**
858      * May be called in AWT only.
859      */

860     private void documentationQuery() {
861         Result newDocumentationResult = new Result(1); // Estimate for selected item only
862
synchronized (this) {
863             assert (docResult == null);
864             docResult = newDocumentationResult;
865         }
866         List JavaDoc<CompletionResultSetImpl> documentationResultSets = docResult.getResultSets();
867
868         CompletionTask docTask;
869         CompletionItem selectedItem = layout.getSelectedCompletionItem();
870         if (selectedItem != null) {
871             lastSelectedItem = new WeakReference JavaDoc<CompletionItem>(selectedItem);
872             docTask = selectedItem.createDocumentationTask();
873             if (docTask != null) { // attempt the documentation for selected item
874
CompletionResultSetImpl resultSet = new CompletionResultSetImpl(
875                         this, newDocumentationResult, docTask, CompletionProvider.DOCUMENTATION_QUERY_TYPE);
876                 documentationResultSets.add(resultSet);
877             }
878         } else { // No item selected => Query all providers
879
lastSelectedItem = null;
880             for (int i = 0; i < activeProviders.length; i++) {
881                 docTask = activeProviders[i].createTask(
882                         CompletionProvider.DOCUMENTATION_QUERY_TYPE, getActiveComponent());
883                 if (docTask != null) {
884                     CompletionResultSetImpl resultSet = new CompletionResultSetImpl(
885                             this, newDocumentationResult, docTask, CompletionProvider.DOCUMENTATION_QUERY_TYPE);
886                     documentationResultSets.add(resultSet);
887                 }
888             }
889         }
890
891         if (documentationResultSets.size() > 0) {
892             queryResultSets(documentationResultSets);
893             newDocumentationResult.queryInvoked();
894         } else {
895             documentationCancel();
896             layout.hideDocumentation();
897         }
898     }
899
900     private void documentationCancel() {
901         Result oldDocumentationResult;
902         synchronized (this) {
903             oldDocumentationResult = docResult;
904             docResult = null;
905         }
906         if (oldDocumentationResult != null) {
907             oldDocumentationResult.cancel();
908         }
909     }
910     
911     /**
912      * May be called from any thread. The UI changes will be rescheduled into AWT.
913      */

914     public boolean hideDocumentation() {
915         return hideDocumentation(true);
916     }
917     
918     boolean hideDocumentation(boolean documentationOnly) {
919         documentationCancel();
920         // Invoke hideDocumentationPane() in AWT
921
if (!SwingUtilities.isEventDispatchThread()) {
922             SwingUtilities.invokeLater(new ParamRunnable(ParamRunnable.HIDE_DOCUMENTATION_PANE, documentationOnly));
923             return false;
924         } else { // in AWT
925
return hideDocumentationPane(documentationOnly);
926         }
927     }
928     
929     /**
930      * May be called in AWT only.
931      */

932     boolean hideDocumentationPane(boolean documentationOnly) {
933         // Ensure the documentation popup timer is stopped
934
docAutoPopupTimer.stop();
935         boolean hidePerformed = layout.hideDocumentation();
936  // Also hide completion if documentation pops automatically
937
if (!documentationOnly && hidePerformed && CompletionSettings.INSTANCE.documentationAutoPopup()) {
938             hideCompletion(true);
939         }
940         return hidePerformed;
941     }
942
943     
944     /**
945      * May be called from any thread but it will be rescheduled into AWT.
946      */

947     public void showToolTip() {
948         if (!SwingUtilities.isEventDispatchThread()) {
949             // Re-call this method in AWT if necessary
950
SwingUtilities.invokeLater(new ParamRunnable(ParamRunnable.SHOW_TOOL_TIP));
951             return;
952         }
953
954         if (activeProviders != null) {
955             toolTipCancel();
956             toolTipQuery();
957         }
958     }
959
960     /**
961      * Request displaying of the tooltip pane.
962      * Can be called from any thread - is called synchronously
963      * from the thread that finished last unfinished result.
964      */

965     void requestShowToolTipPane(Result result) {
966         final CompletionResultSetImpl resultSet = findFirstValidResult(result.getResultSets());
967         runInAWT(new Runnable JavaDoc() {
968             public void run() {
969                 if (resultSet != null) {
970                     layout.showToolTip(
971                             resultSet.getToolTip(), resultSet.getAnchorOffset());
972                 } else {
973                     hideToolTip();
974                 }
975             }
976         });
977     }
978
979     /**
980      * May be called in AWT only.
981      */

982     private void toolTipQuery() {
983         Result newToolTipResult = new Result(1);
984         synchronized (this) {
985             assert (toolTipResult == null);
986             toolTipResult = newToolTipResult;
987         }
988         List JavaDoc<CompletionResultSetImpl> toolTipResultSets = newToolTipResult.getResultSets();
989
990         CompletionTask toolTipTask;
991         CompletionItem selectedItem = layout.getSelectedCompletionItem();
992         if (selectedItem != null && (toolTipTask = selectedItem.createToolTipTask()) != null) {
993             CompletionResultSetImpl resultSet = new CompletionResultSetImpl(
994                     this, newToolTipResult, toolTipTask, CompletionProvider.TOOLTIP_QUERY_TYPE);
995             toolTipResultSets.add(resultSet);
996         } else {
997             for (int i = 0; i < activeProviders.length; i++) {
998                 toolTipTask = activeProviders[i].createTask(
999                         CompletionProvider.TOOLTIP_QUERY_TYPE, getActiveComponent());
1000                if (toolTipTask != null) {
1001                    CompletionResultSetImpl resultSet = new CompletionResultSetImpl(
1002                            this, newToolTipResult, toolTipTask, CompletionProvider.TOOLTIP_QUERY_TYPE);
1003                    toolTipResultSets.add(resultSet);
1004                }
1005            }
1006        }
1007        
1008        queryResultSets(toolTipResultSets);
1009        newToolTipResult.queryInvoked();
1010    }
1011
1012    private void toolTipRefresh() {
1013        Result localToolTipResult;
1014        synchronized (this) {
1015            localToolTipResult = toolTipResult;
1016        }
1017        if (localToolTipResult != null) {
1018            Result refreshResult = localToolTipResult.createRefreshResult();
1019            synchronized (this) {
1020                toolTipResult = refreshResult;
1021            }
1022            refreshResult.invokeRefresh();
1023        }
1024    }
1025
1026    /**
1027     * May be called from any thread.
1028     */

1029    private void toolTipCancel() {
1030        Result oldToolTipResult;
1031        synchronized (this) {
1032            oldToolTipResult = toolTipResult;
1033            toolTipResult = null;
1034        }
1035        if (oldToolTipResult != null) {
1036            oldToolTipResult.cancel();
1037        }
1038    }
1039
1040    /**
1041     * May be called from any thread. The UI changes will be rescheduled into AWT.
1042     */

1043    public boolean hideToolTip() {
1044        toolTipCancel();
1045        // Invoke hideToolTipPane() in AWT
1046
if (!SwingUtilities.isEventDispatchThread()) {
1047            SwingUtilities.invokeLater(new ParamRunnable(ParamRunnable.HIDE_TOOL_TIP_PANE));
1048            return false;
1049        } else { // in AWT
1050
return hideToolTipPane();
1051        }
1052    }
1053    
1054    /**
1055     * May be called in AWT only.
1056     */

1057    boolean hideToolTipPane() {
1058        return layout.hideToolTip();
1059    }
1060
1061    /** Attempt to find the editor keystroke for the given editor action. */
1062    private KeyStroke[] findEditorKeys(String JavaDoc editorActionName, KeyStroke defaultKey) {
1063        // This method is implemented due to the issue
1064
// #25715 - Attempt to search keymap for the keybinding that logically corresponds to the action
1065
KeyStroke[] ret = new KeyStroke[] { defaultKey };
1066        if (editorActionName != null && getActiveComponent() != null) {
1067            TextUI JavaDoc ui = getActiveComponent().getUI();
1068            Keymap km = getActiveComponent().getKeymap();
1069            if (ui != null && km != null) {
1070                EditorKit kit = ui.getEditorKit(getActiveComponent());
1071                if (kit instanceof BaseKit) {
1072                    Action a = ((BaseKit)kit).getActionByName(editorActionName);
1073                    if (a != null) {
1074                        KeyStroke[] keys = km.getKeyStrokesForAction(a);
1075                        if (keys != null && keys.length > 0) {
1076                            ret = keys;
1077                        } else {
1078                            // try kit's keymap
1079
Keymap km2 = ((BaseKit)kit).getKeymap();
1080                            KeyStroke[] keys2 = km2.getKeyStrokesForAction(a);
1081                            if (keys2 != null && keys2.length > 0) {
1082                                ret = keys2;
1083                            }
1084                        }
1085                    }
1086                }
1087            }
1088        }
1089        return ret;
1090    }
1091
1092    private void installKeybindings() {
1093        actionMap = new ActionMap();
1094        inputMap = new InputMap();
1095        
1096        // Register completion show
1097
KeyStroke[] keys = findEditorKeys(ExtKit.completionShowAction, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_MASK));
1098        for (int i = 0; i < keys.length; i++) {
1099            inputMap.put(keys[i], COMPLETION_SHOW);
1100        }
1101        actionMap.put(COMPLETION_SHOW, new CompletionShowAction(CompletionProvider.COMPLETION_QUERY_TYPE));
1102
1103        // Register all completion show
1104
keys = findEditorKeys(ExtKit.allCompletionShowAction, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, (InputEvent.CTRL_MASK | InputEvent.ALT_MASK)));
1105        for (int i = 0; i < keys.length; i++) {
1106            inputMap.put(keys[i], COMPLETION_ALL_SHOW);
1107        }
1108        actionMap.put(COMPLETION_ALL_SHOW, new CompletionShowAction(CompletionProvider.COMPLETION_ALL_QUERY_TYPE));
1109
1110        // Register documentation show
1111
keys = findEditorKeys(ExtKit.documentationShowAction, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, (InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)));
1112        for (int i = 0; i < keys.length; i++) {
1113            inputMap.put(keys[i], DOC_SHOW);
1114        }
1115        actionMap.put(DOC_SHOW, new DocShowAction());
1116        
1117        // Register tooltip show
1118
keys = findEditorKeys(ExtKit.completionTooltipShowAction, KeyStroke.getKeyStroke(KeyEvent.VK_P, InputEvent.ALT_MASK));
1119        for (int i = 0; i < keys.length; i++) {
1120            inputMap.put(keys[i], TOOLTIP_SHOW);
1121        }
1122        actionMap.put(TOOLTIP_SHOW, new ToolTipShowAction());
1123    }
1124    
1125    /**
1126     * Notify that a particular completion result set has just been finished.
1127     * <br>
1128     * This method may be called from any thread.
1129     */

1130    void finishNotify(CompletionResultSetImpl finishedResult) {
1131        switch (finishedResult.getQueryType()) {
1132            case CompletionProvider.COMPLETION_QUERY_TYPE:
1133            case CompletionProvider.COMPLETION_ALL_QUERY_TYPE:
1134                Result localCompletionResult;
1135                synchronized (this) {
1136                    localCompletionResult = completionResult;
1137                }
1138                if (finishedResult.getResultId() == localCompletionResult) {
1139                    if (isAllResultsFinished(localCompletionResult.getResultSets())) {
1140                        requestShowCompletionPane(localCompletionResult);
1141                    }
1142                }
1143                break;
1144
1145            case CompletionProvider.DOCUMENTATION_QUERY_TYPE:
1146                Result localDocumentationResult;
1147                synchronized (this) {
1148                    localDocumentationResult = docResult;
1149                }
1150                if (finishedResult.getResultId() == localDocumentationResult) {
1151                    if (isAllResultsFinished(localDocumentationResult.getResultSets())) {
1152                        requestShowDocumentationPane(localDocumentationResult);
1153                    }
1154                }
1155                break;
1156
1157            case CompletionProvider.TOOLTIP_QUERY_TYPE:
1158                Result localToolTipResult;
1159                synchronized (this) {
1160                    localToolTipResult = toolTipResult;
1161                }
1162                if (finishedResult.getResultId() == localToolTipResult) {
1163                    if (isAllResultsFinished(localToolTipResult.getResultSets())) {
1164                        requestShowToolTipPane(localToolTipResult);
1165                    }
1166                }
1167                break;
1168                
1169            default:
1170                throw new IllegalStateException JavaDoc(); // Invalid query type
1171
}
1172    }
1173    
1174    private static boolean isAllResultsFinished(List JavaDoc<CompletionResultSetImpl> resultSets) {
1175        for (int i = resultSets.size() - 1; i >= 0; i--) {
1176            CompletionResultSetImpl result = resultSets.get(i);
1177            if (!result.isFinished()) {
1178                if (debug) {
1179                    System.err.println("CompletionTask: " + result.getTask() // NOI18N
1180
+ " not finished yet"); // NOI18N
1181
}
1182                return false;
1183            }
1184        }
1185        if (debug) {
1186            System.err.println("----- All tasks finished -----");
1187        }
1188        return true;
1189    }
1190
1191    /**
1192     * Find first result that has non-null documentation or tooltip
1193     * depending on its query type.
1194     * <br>
1195     * The method assumes that all the resultSets are already finished.
1196     */

1197    private static CompletionResultSetImpl findFirstValidResult(List JavaDoc<CompletionResultSetImpl> resultSets) {
1198        for (int i = 0; i < resultSets.size(); i++) {
1199            CompletionResultSetImpl result = resultSets.get(i);
1200            switch (result.getQueryType()) {
1201                case CompletionProvider.DOCUMENTATION_QUERY_TYPE:
1202                    if (result.getDocumentation() != null) {
1203                        return result;
1204                    }
1205                    break;
1206
1207                case CompletionProvider.TOOLTIP_QUERY_TYPE:
1208                    if (result.getToolTip() != null) {
1209                        return result;
1210                    }
1211                    break;
1212                    
1213                default:
1214                    throw new IllegalStateException JavaDoc();
1215            }
1216        }
1217        return null;
1218    }
1219    
1220    private static void runInAWT(Runnable JavaDoc r) {
1221        if (SwingUtilities.isEventDispatchThread()) {
1222            r.run();
1223        } else {
1224            SwingUtilities.invokeLater(r);
1225        }
1226    }
1227
1228    // ..........................................................................
1229

1230    CompletionLayout testGetCompletionLayout() {
1231        return layout;
1232    }
1233    
1234    void testSetActiveComponent(JTextComponent component) {
1235        activeComponent = new WeakReference JavaDoc<JTextComponent>(component);
1236    }
1237    
1238    // ..........................................................................
1239

1240    private final class CompletionShowAction extends AbstractAction {
1241        private int queryType;
1242        
1243        private CompletionShowAction(int queryType) {
1244            this.queryType = queryType;
1245        }
1246
1247        public void actionPerformed(ActionEvent e) {
1248            showCompletion(true, false, queryType);
1249        }
1250    }
1251
1252    private final class DocShowAction extends AbstractAction {
1253        public void actionPerformed(ActionEvent e) {
1254            showDocumentation();
1255        }
1256    }
1257
1258    private final class ToolTipShowAction extends AbstractAction {
1259        public void actionPerformed(ActionEvent e) {
1260            showToolTip();
1261        }
1262    }
1263
1264    private final class ParamRunnable implements Runnable JavaDoc {
1265        
1266        private static final int SHOW_COMPLETION = 0;
1267        private static final int SHOW_DOCUMENTATION = 1;
1268        private static final int SHOW_TOOL_TIP = 2;
1269        private static final int HIDE_COMPLETION_PANE = 3;
1270        private static final int HIDE_DOCUMENTATION_PANE = 4;
1271        private static final int HIDE_TOOL_TIP_PANE = 5;
1272        
1273        private final int opCode;
1274        private final boolean explicit;
1275        private final boolean delayQuery;
1276        private final int type;
1277        
1278        ParamRunnable(int opCode) {
1279            this(opCode, false);
1280        }
1281        
1282        ParamRunnable(int opCode, boolean explicit) {
1283            this(opCode, explicit, false, CompletionProvider.COMPLETION_QUERY_TYPE);
1284        }
1285
1286        ParamRunnable(int opCode, boolean explicit, boolean delayQuery, int type) {
1287            this.opCode = opCode;
1288            this.explicit = explicit;
1289            this.delayQuery = delayQuery;
1290            this.type = type;
1291        }
1292
1293        public void run() {
1294            switch (opCode) {
1295                case SHOW_COMPLETION:
1296                    showCompletion(explicitQuery, delayQuery, type);
1297                    break;
1298
1299                case SHOW_DOCUMENTATION:
1300                    showDocumentation();
1301                    break;
1302                    
1303                case SHOW_TOOL_TIP:
1304                    showToolTip();
1305                    break;
1306                    
1307                case HIDE_COMPLETION_PANE:
1308                    hideCompletionPane(explicit);
1309                    break;
1310
1311                case HIDE_DOCUMENTATION_PANE:
1312                    hideDocumentationPane(explicit);
1313                    break;
1314                    
1315                case HIDE_TOOL_TIP_PANE:
1316                    hideToolTipPane();
1317                    break;
1318                    
1319                default:
1320                    throw new IllegalStateException JavaDoc();
1321            }
1322        }
1323    }
1324    
1325    private static void queryResultSets(List JavaDoc<CompletionResultSetImpl> resultSets) {
1326        for (int i = 0; i < resultSets.size(); i++) {
1327            CompletionResultSetImpl resultSet = resultSets.get(i);
1328            resultSet.getTask().query(resultSet.getResultSet());
1329        }
1330    }
1331    
1332    private static void createRefreshResultSets(List JavaDoc<CompletionResultSetImpl> resultSets, Result refreshResult) {
1333        List JavaDoc<CompletionResultSetImpl> refreshResultSets = refreshResult.getResultSets();
1334        int size = resultSets.size();
1335        // Create new resultSets
1336
for (int i = 0; i < size; i++) {
1337            CompletionResultSetImpl result = resultSets.get(i);
1338            result.markInactive();
1339            result = new CompletionResultSetImpl(result.getCompletionImpl(),
1340                    refreshResult, result.getTask(), result.getQueryType());
1341            refreshResultSets.add(result);
1342        }
1343    }
1344    
1345    private static void refreshResultSets(List JavaDoc<CompletionResultSetImpl> resultSets, boolean beforeQuery) {
1346        try {
1347            int size = resultSets.size();
1348            for (int i = 0; i < size; i++) {
1349                CompletionResultSetImpl result = resultSets.get(i);
1350                result.getTask().refresh(beforeQuery ? null : result.getResultSet());
1351                
1352            }
1353        } catch (Exception JavaDoc ex) {
1354            ErrorManager.getDefault().notify(ex);
1355        }
1356    }
1357    
1358    private static void cancelResultSets(List JavaDoc<CompletionResultSetImpl> resultSets) {
1359        int size = resultSets.size();
1360        for (int i = 0; i < size; i++) {
1361            CompletionResultSetImpl result = resultSets.get(i);
1362            result.markInactive();
1363            result.getTask().cancel();
1364        }
1365    }
1366
1367    public void settingsChange(org.netbeans.editor.SettingsChangeEvent evt) {
1368        if( evt == null) {
1369            return;
1370        }
1371        String JavaDoc settingName = evt.getSettingName();
1372        if (SettingsNames.KEY_BINDING_LIST.equals(settingName) || settingName == null){
1373            Utilities.runInEventDispatchThread(new Runnable JavaDoc(){
1374                public void run(){
1375                    installKeybindings();
1376                }
1377            });
1378        }
1379    }
1380    
1381    /**
1382     * Result holding list of completion result sets.
1383     * <br>
1384     * Initially the result is in unprepared state which allows the holding
1385     * thread to add the result sets and start the tasks.
1386     * <br>
1387     * If another thread calls cancel() it has no effect except setting a flag
1388     * that is returned from the prepared() method.
1389     * <br>
1390     * If the result is finished then cancelling physically cancels the result sets.
1391     */

1392    static final class Result {
1393        
1394        private final List JavaDoc<CompletionResultSetImpl> resultSets;
1395        
1396        private boolean invoked;
1397        private boolean cancelled;
1398        private boolean beforeQuery = true;
1399        
1400        Result(int resultSetsSize) {
1401            resultSets = new ArrayList JavaDoc<CompletionResultSetImpl>(resultSetsSize);
1402        }
1403
1404        /**
1405         * Get the contained resultSets.
1406         *
1407         * @return non-null resultSets.
1408         */

1409        List JavaDoc<CompletionResultSetImpl> getResultSets() {
1410            return resultSets;
1411        }
1412
1413        /**
1414         * Cancel the resultSets.
1415         * <br>
1416         * If the result is not prepared a flag that the result
1417         * was cancelled is turned on (and later returned from prepared()).
1418         * <br>
1419         * Otherwise physical cancellation of the result sets is done.
1420         */

1421        void cancel() {
1422            boolean fin;
1423            synchronized (this) {
1424                assert (!cancelled);
1425                fin = invoked;
1426                if (!invoked) {
1427                    cancelled = true;
1428                }
1429            }
1430            
1431            if (fin) { // already invoked
1432
cancelResultSets(resultSets);
1433            }
1434        }
1435        
1436        synchronized boolean isQueryInvoked() {
1437            return invoked;
1438        }
1439        
1440        /**
1441         * Mark the queries were invoked on the tasks in the result sets.
1442         * @return true if the result was cancelled in the meantime.
1443         */

1444        boolean queryInvoked() {
1445            boolean canc;
1446            synchronized (this) {
1447                assert (!invoked);
1448                invoked = true;
1449                canc = cancelled;
1450                beforeQuery = false;
1451            }
1452            if (canc) {
1453                cancelResultSets(resultSets);
1454            }
1455            return canc;
1456        }
1457        
1458        /**
1459         * and return the new result set
1460         * containing the refreshed results.
1461         */

1462        Result createRefreshResult() {
1463            synchronized (this) {
1464                if (cancelled) {
1465                    return null;
1466                }
1467                if (beforeQuery) {
1468                    return this;
1469                }
1470                assert (invoked); // had to be invoked
1471
invoked = false;
1472            }
1473            Result refreshResult = new Result(getResultSets().size());
1474            refreshResult.beforeQuery = beforeQuery;
1475            createRefreshResultSets(resultSets, refreshResult);
1476            return refreshResult;
1477        }
1478        
1479        /**
1480         * Invoke refreshing of the result sets.
1481         * This method should be invoked on the result set returned from
1482         * {@link #createRefreshResult()}.
1483         */

1484        void invokeRefresh() {
1485            refreshResultSets(getResultSets(), beforeQuery);
1486            if (!beforeQuery)
1487                queryInvoked();
1488        }
1489
1490    }
1491    
1492    public CompletionResultSetImpl createTestResultSet(CompletionTask task, int queryType) {
1493        return new CompletionResultSetImpl(this, "TestResult", task, queryType);
1494    }
1495}
1496
Popular Tags