KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > retouche > editor > completion > GsfCompletionProvider


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 package org.netbeans.modules.retouche.editor.completion;
20
21 import java.io.IOException JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collection JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.List JavaDoc;
26 import javax.swing.JToolTip JavaDoc;
27 import javax.swing.text.BadLocationException JavaDoc;
28 import javax.swing.text.Document JavaDoc;
29 import javax.swing.text.JTextComponent JavaDoc;
30 import org.netbeans.api.editor.completion.Completion;
31 import org.netbeans.api.gsf.CompletionProposal;
32 import org.netbeans.api.gsf.Completable;
33 import org.netbeans.api.gsf.Parser;
34 import org.netbeans.api.gsf.CancellableTask;
35 import org.netbeans.api.gsf.Element;
36 import org.netbeans.api.gsf.ElementHandle;
37 import org.netbeans.api.gsf.ElementKind;
38 import org.netbeans.api.lexer.TokenHierarchy;
39 import org.netbeans.api.lexer.TokenId;
40 import org.netbeans.api.lexer.TokenSequence;
41 import org.netbeans.api.retouche.source.CompilationController;
42 import org.netbeans.api.retouche.source.CompilationInfo;
43 import org.netbeans.api.retouche.source.Phase;
44 import org.netbeans.api.retouche.source.Source;
45 import org.netbeans.editor.BaseDocument;
46 import org.netbeans.editor.Registry;
47 import org.netbeans.modules.gsf.GsfHtmlFormatter;
48 import org.netbeans.modules.gsf.Language;
49 import org.netbeans.spi.editor.completion.*;
50 import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery;
51 import org.netbeans.spi.editor.completion.support.AsyncCompletionTask;
52 import org.openide.ErrorManager;
53 import org.openide.util.Exceptions;
54
55
56 /**
57  * Code completion provider - delegates to language plugin for actual population of result set.
58  * Based on JavaCompletionProvider by Dusan Balek.
59  *
60  * @author Tor Norbye
61  */

62 public class GsfCompletionProvider implements CompletionProvider {
63     // From Utilities
64
private static boolean caseSensitive = true;
65
66     public int getAutoQueryTypes(JTextComponent JavaDoc component, String JavaDoc typedText) {
67         // I don't want to automatically pop up completion on every dot yet because
68
// it gets in the way.
69
// if (".".equals(typedText)) {
70
// if (isJavaContext(component, component.getSelectionStart()-1)) {
71
// return COMPLETION_QUERY_TYPE;
72
// }
73
// }
74

75         return 0;
76     }
77
78     // From Utilities
79
public static boolean isJavaContext(final JTextComponent JavaDoc component, final int offset) {
80         org.netbeans.api.lexer.Language language = (org.netbeans.api.lexer.Language)component.getDocument().getProperty(org.netbeans.api.lexer.Language.class);
81         if (language == null) {
82             return true;
83         }
84         TokenSequence ts = TokenHierarchy.get(component.getDocument()).tokenSequence();
85
86         if (ts == null)
87             return false;
88         if (!ts.moveNext() || ts.move(offset) == 0)
89             return true;
90         ts.moveNext(); // Move to the next token after move(offset)
91

92         TokenId tokenId = ts.token().id();
93         return !language.tokenCategoryMembers("comment").contains(tokenId); //NOI18N
94
}
95
96     public static boolean startsWith(String JavaDoc theString, String JavaDoc prefix) {
97         if ((prefix == null) || (prefix.length() == 0)) {
98             return true;
99         }
100
101         return caseSensitive ? theString.startsWith(prefix)
102                              : theString.toLowerCase().startsWith(prefix.toLowerCase());
103     }
104
105     public CompletionTask createTask(int type, JTextComponent JavaDoc component) {
106         if (((type & COMPLETION_QUERY_TYPE) != 0) || (type == TOOLTIP_QUERY_TYPE) ||
107                 (type == DOCUMENTATION_QUERY_TYPE)) {
108             return new AsyncCompletionTask(new JavaCompletionQuery(type,
109                     component.getSelectionStart()), component);
110         }
111
112         return null;
113     }
114
115     static CompletionTask createDocTask(Element element, CompilationInfo info) { // TODO - use ComObjectHandle ??
116
JavaCompletionQuery query = new JavaCompletionQuery(DOCUMENTATION_QUERY_TYPE, -1);
117
118         Language language = info.getLanguage();
119         Parser parser = language.getParser();
120         if (parser != null) {
121             query.element = parser.createHandle(info, element);
122         }
123
124         return new AsyncCompletionTask(query, Registry.getMostActiveComponent());
125     }
126
127     static final class JavaCompletionQuery extends AsyncCompletionQuery implements CancellableTask<CompilationController> {
128         private Collection JavaDoc<CompletionItem> results;
129         private JToolTip JavaDoc toolTip;
130         private CompletionDocumentation documentation;
131         private int anchorOffset;
132         private JTextComponent JavaDoc component;
133         private int queryType;
134         private int caretOffset;
135         private String JavaDoc filterPrefix;
136         private ElementHandle element;
137         /** The compilation info that the Element was generated for */
138
139         private JavaCompletionQuery(int queryType, int caretOffset) {
140             this.queryType = queryType;
141             this.caretOffset = caretOffset;
142         }
143
144         @Override JavaDoc
145         protected void preQueryUpdate(JTextComponent JavaDoc component) {
146             int newCaretOffset = component.getSelectionStart();
147
148             if (newCaretOffset >= caretOffset) {
149                 try {
150                     if (isJavaIdentifierPart(component.getDocument()
151                                                           .getText(caretOffset,
152                                     newCaretOffset - caretOffset))) {
153                         return;
154                     }
155                 } catch (BadLocationException JavaDoc e) {
156                 }
157             }
158
159             Completion.get().hideCompletion();
160         }
161
162         @Override JavaDoc
163         protected void prepareQuery(JTextComponent JavaDoc component) {
164             this.component = component;
165         }
166
167         @Override JavaDoc
168         protected void query(CompletionResultSet resultSet, Document JavaDoc doc, int caretOffset) {
169             try {
170                 this.caretOffset = caretOffset;
171
172                 if (isJavaContext(component, caretOffset)) {
173                     results = null;
174                     documentation = null;
175                     toolTip = null;
176                     anchorOffset = -1;
177
178                     Source js = Source.forDocument(doc);
179                     js.runUserActionTask(this, (queryType & COMPLETION_QUERY_TYPE) == 0);
180
181                     if ((queryType & COMPLETION_QUERY_TYPE) != 0) {
182                         if (results != null) {
183                             resultSet.addAllItems(results);
184                         }
185                     } else if (queryType == TOOLTIP_QUERY_TYPE) {
186                         if (toolTip != null) {
187                             resultSet.setToolTip(toolTip);
188                         }
189                     } else if (queryType == DOCUMENTATION_QUERY_TYPE) {
190                         if (doc != null) {
191                             resultSet.setDocumentation(documentation);
192                         }
193                     }
194
195                     if (anchorOffset > -1) {
196                         resultSet.setAnchorOffset(anchorOffset);
197                     }
198                 }
199             } catch (IOException JavaDoc ioe) {
200                 Exceptions.printStackTrace(ioe);
201             } finally {
202                 resultSet.finish();
203             }
204         }
205
206         @Override JavaDoc
207         protected boolean canFilter(JTextComponent JavaDoc component) {
208             filterPrefix = null;
209
210             int newOffset = component.getSelectionStart();
211
212             if ((queryType & COMPLETION_QUERY_TYPE) != 0) {
213                 if (newOffset >= caretOffset) {
214                     if (anchorOffset > -1) {
215                         try {
216                             String JavaDoc prefix =
217                                 component.getDocument()
218                                          .getText(anchorOffset, newOffset - anchorOffset);
219
220                             if (isJavaIdentifierPart(prefix)) {
221                                 filterPrefix = prefix;
222                             }
223                         } catch (BadLocationException JavaDoc e) {
224                         }
225                     }
226                 }
227
228                 return filterPrefix != null;
229             } else if (queryType == TOOLTIP_QUERY_TYPE) {
230                 try {
231                     if ((newOffset - caretOffset) > 0) {
232                         filterPrefix = component.getDocument()
233                                                 .getText(caretOffset, newOffset - caretOffset);
234                     } else if ((newOffset - caretOffset) < 0) {
235                         filterPrefix = component.getDocument()
236                                                 .getText(newOffset, caretOffset - newOffset);
237                     }
238                 } catch (BadLocationException JavaDoc ex) {
239                 }
240
241                 return ((filterPrefix != null) && (filterPrefix.indexOf(',') == -1) &&
242                 (filterPrefix.indexOf('(') == -1) && (filterPrefix.indexOf(')') == -1)); // NOI18N
243
}
244
245             return false;
246         }
247
248         @Override JavaDoc
249         protected void filter(CompletionResultSet resultSet) {
250             try {
251                 if ((queryType & COMPLETION_QUERY_TYPE) != 0) {
252                     if (results != null) {
253                         resultSet.addAllItems(getFilteredData(results, filterPrefix));
254                     }
255                 } else if (queryType == TOOLTIP_QUERY_TYPE) {
256                     resultSet.setToolTip(toolTip);
257                 }
258
259                 resultSet.setAnchorOffset(anchorOffset);
260             } catch (Exception JavaDoc ex) {
261                 Exceptions.printStackTrace(ex);
262             }
263
264             resultSet.finish();
265         }
266
267         public void run(CompilationController controller)
268             throws Exception JavaDoc {
269             if ((queryType & COMPLETION_QUERY_TYPE) != 0) {
270                 resolveCompletion(controller);
271             } else if (queryType == TOOLTIP_QUERY_TYPE) {
272                 resolveToolTip(controller);
273             } else if (queryType == DOCUMENTATION_QUERY_TYPE) {
274                 resolveDocumentation(controller);
275             }
276         }
277
278         public void cancel() {
279         }
280
281         private void resolveToolTip(final CompilationController controller)
282             throws IOException JavaDoc {
283             // TODO
284
}
285
286         private void resolveDocumentation(CompilationController controller)
287             throws IOException JavaDoc {
288             controller.toPhase(Phase.RESOLVED);
289             // TODO
290
//Element el = element != null ? element.resolve(controller) : controller.getTrees().getElement(getCompletionEnvironment(controller, false).getPath());
291
//if (el != null) {
292
// documentation = JavaCompletionDoc.create(SourceUtils.javaDocFor(controller, el));
293
//}
294
if (element != null) {
295                 documentation = GsfCompletionDoc.create(controller, element);
296             }
297         }
298
299         //
300
private void resolveCompletion(CompilationController controller)
301             throws IOException JavaDoc {
302             Env env = getCompletionEnvironment(controller, true);
303             int offset = env.getOffset();
304             String JavaDoc prefix = env.getPrefix();
305             results = new ArrayList JavaDoc<CompletionItem>();
306             anchorOffset = env.getOffset() - ((prefix != null) ? prefix.length() : 0);
307
308             Language language = controller.getLanguage();
309             Completable completer = language.getCompletionProvider();
310
311             if (completer != null) {
312                 List JavaDoc<CompletionProposal> proposals =
313                     completer.complete(controller, offset, prefix, caseSensitive, new CompletionFormatter());
314
315                 if (proposals != null) {
316                     for (CompletionProposal proposal : proposals) {
317                         GsfCompletionItem item = GsfCompletionItem.createItem(proposal, controller);
318
319                         if (item != null) {
320                             results.add(item);
321                         }
322                     }
323                 }
324             }
325         }
326
327         // TODO - delegate to language support!
328
private boolean isJavaIdentifierPart(String JavaDoc text) {
329             for (int i = 0; i < text.length(); i++) {
330                 if (!(Character.isJavaIdentifierPart(text.charAt(i)))) {
331                     return false;
332                 }
333             }
334
335             return true;
336         }
337
338         private Collection JavaDoc getFilteredData(Collection JavaDoc<CompletionItem> data, String JavaDoc prefix) {
339             if (prefix.length() == 0) {
340                 return data;
341             }
342
343             List JavaDoc ret = new ArrayList JavaDoc();
344
345             for (Iterator JavaDoc<CompletionItem> it = data.iterator(); it.hasNext();) {
346                 CompletionItem itm = it.next();
347
348                 if (startsWith(itm.getInsertPrefix().toString(), prefix)) {
349                     ret.add(itm);
350                 }
351
352                 // else if (itm instanceof LazyTypeCompletionItem && Utilities.startsWith(((LazyTypeCompletionItem)itm).getItemText(), prefix))
353
// ret.add(itm);
354
}
355
356             return ret;
357         }
358
359         private Env getCompletionEnvironment(CompilationController controller, boolean upToOffset)
360             throws IOException JavaDoc {
361             // If you invoke code completion while indexing is in progress, the
362
// completion job (which stores the caret offset) will be delayed until
363
// indexing is complete - potentially minutes later. When the job
364
// is finally run we need to make sure the caret position is still valid. (93017)
365
int length = controller.getDocument().getLength();
366             if (caretOffset > length) {
367                 caretOffset = length;
368             }
369             
370             int offset = caretOffset;
371             String JavaDoc prefix = null;
372    
373             // TODO - handle the upToOffset parameter
374

375             try {
376                 // TODO: use the completion helper to get the contxt
377
Language language = controller.getLanguage();
378                 Completable completer = language.getCompletionProvider();
379                 if (completer != null) {
380                     prefix = completer.getPrefix(controller, offset);
381                 }
382                 if (prefix == null) {
383                     int[] blk =
384                         org.netbeans.editor.Utilities.getIdentifierBlock((BaseDocument)controller.getDocument(),
385                             offset);
386
387                     if (blk != null) {
388                         int start = blk[0];
389
390                         if (start < offset) {
391                             prefix = controller.getDocument().getText(start, offset - start);
392                         }
393                     }
394                 }
395             } catch (BadLocationException JavaDoc ex) {
396                 ErrorManager.getDefault().notify(ex);
397             } catch (IOException JavaDoc ex) {
398                 ErrorManager.getDefault().notify(ex);
399             }
400             
401             controller.toPhase(Phase.PARSED);
402
403             return new Env(offset, prefix, controller);
404         }
405
406         private class Env {
407             private int offset;
408             private String JavaDoc prefix;
409             private CompilationController controller;
410
411             private Env(int offset, String JavaDoc prefix, CompilationController controller) {
412                 this.offset = offset;
413                 this.prefix = prefix;
414                 this.controller = controller;
415             }
416
417             public int getOffset() {
418                 return offset;
419             }
420
421             public String JavaDoc getPrefix() {
422                 return prefix;
423             }
424
425             public CompilationController getController() {
426                 return controller;
427             }
428         }
429     }
430     
431     /** Format parameters in orange etc. */
432     private static class CompletionFormatter extends GsfHtmlFormatter {
433         private static final String JavaDoc METHOD_COLOR = "<font color=#000000>"; //NOI18N
434
private static final String JavaDoc PARAMETER_NAME_COLOR = "<font color=#a06001>"; //NOI18N
435
private static final String JavaDoc END_COLOR = "</font>"; // NOI18N
436
private static final String JavaDoc CLASS_COLOR = "<font color=#560000>"; //NOI18N
437
private static final String JavaDoc PKG_COLOR = "<font color=#808080>"; //NOI18N
438
private static final String JavaDoc KEYWORD_COLOR = "<font color=#000099>"; //NOI18N
439
private static final String JavaDoc FIELD_COLOR = "<font color=#008618>"; //NOI18N
440
private static final String JavaDoc VARIABLE_COLOR = "<font color=#00007c>"; //NOI18N
441
private static final String JavaDoc CONSTRUCTOR_COLOR = "<font color=#b28b00>"; //NOI18N
442
private static final String JavaDoc INTERFACE_COLOR = "<font color=#404040>"; //NOI18N
443

444         @Override JavaDoc
445         public void parameters(boolean start) {
446             assert start != isParameter;
447             isParameter = start;
448
449             if (isParameter) {
450                 sb.append(PARAMETER_NAME_COLOR);
451             } else {
452                 sb.append(END_COLOR);
453             }
454         }
455         
456         @Override JavaDoc
457         public void name(ElementKind kind, boolean start) {
458             assert start != isName;
459             isName = start;
460
461             if (isName) {
462                 switch (kind) {
463                 case CONSTRUCTOR:
464                     sb.append(CONSTRUCTOR_COLOR);
465                     sb.append("<b>");
466                     break;
467                 case METHOD:
468                     sb.append(METHOD_COLOR);
469                     sb.append("<b>");
470                     break;
471                 case CLASS:
472                     sb.append(CLASS_COLOR);
473                     break;
474                 case ATTRIBUTE:
475                 case FIELD:
476                     sb.append(FIELD_COLOR);
477                     sb.append("<b>");
478                     break;
479                 case MODULE:
480                     sb.append(PKG_COLOR);
481                     break;
482                 case KEYWORD:
483                     sb.append(KEYWORD_COLOR);
484                     sb.append("<b>");
485                     break;
486                 case VARIABLE:
487                     sb.append(VARIABLE_COLOR);
488                     sb.append("<b>");
489                     break;
490                 default:
491                     sb.append("<font>");
492                 }
493             } else {
494                 switch (kind) {
495                 case CONSTRUCTOR:
496                 case METHOD:
497                 case ATTRIBUTE:
498                 case FIELD:
499                 case KEYWORD:
500                 case VARIABLE:
501                     sb.append("</b>");
502                     break;
503                 }
504                 sb.append(END_COLOR);
505             }
506         }
507         
508     }
509 }
510
Popular Tags