KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > javadoc > JavaDocCompletionEvaluator


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.ui.text.javadoc;
12
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.List JavaDoc;
16
17 import org.eclipse.core.resources.IFile;
18
19 import org.eclipse.swt.graphics.Image;
20
21 import org.eclipse.jface.resource.ImageDescriptor;
22
23 import org.eclipse.jface.text.BadLocationException;
24 import org.eclipse.jface.text.IDocument;
25 import org.eclipse.jface.text.IRegion;
26 import org.eclipse.jface.text.contentassist.IContextInformation;
27
28 import org.eclipse.ui.IEditorInput;
29 import org.eclipse.ui.part.FileEditorInput;
30
31 import org.eclipse.jdt.core.CompletionProposal;
32 import org.eclipse.jdt.core.CompletionRequestor;
33 import org.eclipse.jdt.core.ICompilationUnit;
34 import org.eclipse.jdt.core.IField;
35 import org.eclipse.jdt.core.IJavaElement;
36 import org.eclipse.jdt.core.IMember;
37 import org.eclipse.jdt.core.IMethod;
38 import org.eclipse.jdt.core.IType;
39 import org.eclipse.jdt.core.ITypeParameter;
40 import org.eclipse.jdt.core.JavaModelException;
41 import org.eclipse.jdt.core.Signature;
42
43 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
44 import org.eclipse.jdt.internal.corext.util.TypeFilter;
45
46 import org.eclipse.jdt.ui.JavaElementLabelProvider;
47 import org.eclipse.jdt.ui.JavaElementLabels;
48 import org.eclipse.jdt.ui.JavaUI;
49 import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
50 import org.eclipse.jdt.ui.text.java.IJavadocCompletionProcessor;
51
52 import org.eclipse.jdt.internal.ui.JavaPlugin;
53 import org.eclipse.jdt.internal.ui.JavaPluginImages;
54 import org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposal;
55 import org.eclipse.jdt.internal.ui.text.java.ProposalInfo;
56 import org.eclipse.jdt.internal.ui.viewsupport.JavaElementImageProvider;
57
58 public class JavaDocCompletionEvaluator implements IJavadocCompletionProcessor, IJavaDocTagConstants, IHtmlTagConstants {
59
60     private static final String JavaDoc[] fgHTMLProposals= new String JavaDoc[HTML_GENERAL_TAGS.length * 2];
61     {
62         String JavaDoc tag= null;
63
64         int index= 0;
65         int offset= 0;
66
67         while (index < fgHTMLProposals.length) {
68
69             tag= HTML_GENERAL_TAGS[offset];
70             fgHTMLProposals[index++]= HTML_TAG_PREFIX + tag + HTML_TAG_POSTFIX;
71             fgHTMLProposals[index++]= HTML_CLOSE_PREFIX + tag + HTML_TAG_POSTFIX;
72             offset++;
73         }
74     }
75
76     private ICompilationUnit fCompilationUnit;
77     private IDocument fDocument;
78     private int fCurrentPos;
79     private int fCurrentLength;
80
81     private String JavaDoc fErrorMessage;
82
83     private JavaElementLabelProvider fLabelProvider;
84     private List JavaDoc fResult;
85
86     private boolean fRestrictToMatchingCase;
87
88     public JavaDocCompletionEvaluator() {
89         fResult= new ArrayList JavaDoc();
90     }
91
92     private static boolean isWordPart(char ch) {
93         return Character.isJavaIdentifierPart(ch) || (ch == '#') || (ch == '.') || (ch == '/');
94     }
95
96     private static int findCharBeforeWord(IDocument doc, int lineBeginPos, int pos) {
97         int currPos= pos - 1;
98         if (currPos > lineBeginPos) {
99             try {
100                 while (currPos > lineBeginPos && isWordPart(doc.getChar(currPos))) {
101                     currPos--;
102                 }
103                 return currPos;
104             } catch (BadLocationException e) {
105                 // ignore
106
}
107         }
108         return pos;
109     }
110
111     private static int findLastWhitespace(IDocument doc, int lineBeginPos, int pos) {
112         try {
113             int currPos= pos - 1;
114             while (currPos >= lineBeginPos && Character.isWhitespace(doc.getChar(currPos))) {
115                 currPos--;
116             }
117             return currPos + 1;
118         } catch (BadLocationException e) {
119             // ignore
120
}
121         return pos;
122     }
123
124     private static int findClosingCharacter(IDocument doc, int pos, int end, char endChar) throws BadLocationException {
125         int curr= pos;
126         while (curr < end && (doc.getChar(curr) != endChar)) {
127             curr++;
128         }
129         if (curr < end) {
130             return curr + 1;
131         }
132         return pos;
133     }
134
135     private static int findReplaceEndPos(IDocument doc, String JavaDoc newText, String JavaDoc oldText, int pos) {
136         if (oldText.length() == 0 || oldText.equals(newText)) {
137             return pos;
138         }
139
140         try {
141             IRegion lineInfo= doc.getLineInformationOfOffset(pos);
142             int end= lineInfo.getOffset() + lineInfo.getLength();
143
144             if (newText.endsWith(">")) { //$NON-NLS-1$
145
// for html, search the tag end character
146
return findClosingCharacter(doc, pos, end, '>');
147             }
148             char ch= 0;
149             int pos1= pos;
150             while (pos1 < end && Character.isJavaIdentifierPart(ch= doc.getChar(pos1))) {
151                 pos1++;
152             }
153             if (pos1 < end) {
154                 // for method references, search the closing bracket
155
if ((ch == '(') && newText.endsWith(")")) { //$NON-NLS-1$
156
return findClosingCharacter(doc, pos1, end, ')');
157                 }
158
159             }
160             return pos1;
161         } catch (BadLocationException e) {
162             // ignore
163
}
164         return pos;
165     }
166
167     /* (non-Javadoc)
168      * @see org.eclipse.jdt.ui.text.java.IJavaDocCompletionProcessor#computeCompletionProposals(org.eclipse.jdt.core.ICompilationUnit, int, int, int)
169      */

170     public IJavaCompletionProposal[] computeCompletionProposals(ICompilationUnit cu, int offset, int length, int flags) {
171         fCompilationUnit= cu;
172         fCurrentPos= offset;
173         fCurrentLength= length;
174         fRestrictToMatchingCase= (flags & RESTRICT_TO_MATCHING_CASE) != 0;
175
176         IEditorInput editorInput= new FileEditorInput((IFile) cu.getResource());
177         fDocument= JavaUI.getDocumentProvider().getDocument(editorInput);
178         if (fDocument == null) {
179             return null;
180         }
181
182         fLabelProvider= new JavaElementLabelProvider(JavaElementLabelProvider.SHOW_POST_QUALIFIED | JavaElementLabelProvider.SHOW_PARAMETERS);
183         try {
184             evalProposals();
185             return (JavaCompletionProposal[]) fResult.toArray(new JavaCompletionProposal[fResult.size()]);
186         } catch (JavaModelException e) {
187             fErrorMessage= e.getLocalizedMessage();
188         } finally {
189             fLabelProvider.dispose();
190             fLabelProvider= null;
191             fResult.clear();
192         }
193         return null;
194     }
195
196     private void evalProposals() throws JavaModelException {
197         try {
198
199             IRegion info= fDocument.getLineInformationOfOffset(fCurrentPos);
200             int lineBeginPos= info.getOffset();
201
202             int word1Begin= findCharBeforeWord(fDocument, lineBeginPos, fCurrentPos);
203             if (word1Begin == fCurrentPos) {
204                 return;
205             }
206             char firstChar= fDocument.getChar(word1Begin);
207             if (firstChar == '@') {
208                 String JavaDoc prefix= fDocument.get(word1Begin, fCurrentPos - word1Begin);
209                 addProposals(prefix, JAVADOC_GENERAL_TAGS, JavaPluginImages.IMG_OBJS_JAVADOCTAG);
210                 return;
211             } else if (firstChar == '<') {
212                 String JavaDoc prefix= fDocument.get(word1Begin, fCurrentPos - word1Begin);
213                 addProposals(prefix, fgHTMLProposals, JavaPluginImages.IMG_OBJS_HTMLTAG);
214                 return;
215             } else if (!Character.isWhitespace(firstChar)) {
216                 return;
217             }
218             String JavaDoc prefix= fDocument.get(word1Begin + 1, fCurrentPos - word1Begin - 1);
219
220             // could be a composed java doc construct (@param, @see ...)
221
int word2End= findLastWhitespace(fDocument, lineBeginPos, word1Begin);
222             if (word2End != lineBeginPos) {
223                 // find the word before the prefix
224
int word2Begin= findCharBeforeWord(fDocument, lineBeginPos, word2End);
225                 if (fDocument.getChar(word2Begin) == '@') {
226                     String JavaDoc tag= fDocument.get(word2Begin, word2End - word2Begin);
227                     if (addArgumentProposals(tag, prefix)) {
228                         return;
229                     }
230                 }
231             }
232             addAllTags(prefix);
233         } catch (BadLocationException e) {
234             // ignore
235
}
236     }
237
238     private boolean prefixMatches(String JavaDoc prefix, String JavaDoc proposal) {
239         if (fRestrictToMatchingCase) {
240             return proposal.startsWith(prefix);
241         } else if (proposal.length() >= prefix.length()) {
242             return prefix.equalsIgnoreCase(proposal.substring(0, prefix.length()));
243         }
244         return false;
245     }
246
247
248
249
250     private void addAllTags(String JavaDoc prefix) {
251         String JavaDoc jdocPrefix= "@" + prefix; //$NON-NLS-1$
252
for (int i= 0; i < JAVADOC_GENERAL_TAGS.length; i++) {
253             String JavaDoc curr= JAVADOC_GENERAL_TAGS[i];
254             if (prefixMatches(jdocPrefix, curr)) {
255                 fResult.add(createCompletion(curr, prefix, curr, JavaPluginImages.get(JavaPluginImages.IMG_OBJS_JAVADOCTAG), null, 0));
256             }
257         }
258         String JavaDoc htmlPrefix= "<" + prefix; //$NON-NLS-1$
259
for (int i= 0; i < fgHTMLProposals.length; i++) {
260             String JavaDoc curr= fgHTMLProposals[i];
261             if (prefixMatches(htmlPrefix, curr)) {
262                 fResult.add(createCompletion(curr, prefix, curr, JavaPluginImages.get(JavaPluginImages.IMG_OBJS_HTMLTAG), null, 0));
263             }
264         }
265     }
266
267     private void addProposals(String JavaDoc prefix, String JavaDoc[] choices, String JavaDoc imageName) {
268         for (int i= 0; i < choices.length; i++) {
269             String JavaDoc curr= choices[i];
270             if (prefixMatches(prefix, curr)) {
271                 fResult.add(createCompletion(curr, prefix, curr, JavaPluginImages.get(imageName), null, 0));
272             }
273         }
274     }
275
276     private void addProposals(String JavaDoc prefix, IJavaElement[] choices) {
277         for (int i= 0; i < choices.length; i++) {
278             IJavaElement elem= choices[i];
279             String JavaDoc curr= getReplaceString(elem);
280             if (prefixMatches(prefix, curr)) {
281                 ProposalInfo info= (elem instanceof IMember) ? new ProposalInfo((IMember) elem) : null;
282                 fResult.add(createCompletion(curr, prefix, fLabelProvider.getText(elem), fLabelProvider.getImage(elem), info, 0));
283             }
284         }
285     }
286
287     private String JavaDoc getReplaceString(IJavaElement elem) {
288         if (elem instanceof IMethod) {
289             IMethod meth= (IMethod)elem;
290             StringBuffer JavaDoc buf= new StringBuffer JavaDoc();
291             buf.append(meth.getElementName());
292             buf.append('(');
293             String JavaDoc[] types= meth.getParameterTypes();
294             int last= types.length - 1;
295             for (int i= 0; i <= last; i++) {
296                 buf.append(Signature.toString(Signature.getTypeErasure(types[i])));
297                 if (i != last) {
298                     buf.append(", "); //$NON-NLS-1$
299
}
300             }
301             buf.append(')');
302             return buf.toString();
303         }
304         return elem.getElementName();
305     }
306
307     /*
308      * Returns true if case is handled
309      */

310     private boolean addArgumentProposals(String JavaDoc tag, String JavaDoc argument) throws JavaModelException {
311         IJavaElement elem= fCompilationUnit.getElementAt(fCurrentPos);
312         if ("@see".equals(tag) || "@link".equals(tag) || "@linkplain".equals(tag) || "@value".equals(tag)) { //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-1$
313
if (elem instanceof IMember) {
314                 evalSeeTag((IMember) elem, argument);
315                 return true;
316             }
317         } else if ("@param".equals(tag)) { //$NON-NLS-1$
318
if (elem instanceof IMethod) {
319                 String JavaDoc[] names= ((IMethod)elem).getParameterNames();
320                 addProposals(argument, names, JavaPluginImages.IMG_MISC_DEFAULT);
321                 ITypeParameter[] typeParameters= ((IMethod)elem).getTypeParameters();
322                 String JavaDoc[] typeParameterNames= new String JavaDoc[typeParameters.length];
323                 for (int i= 0; i < typeParameters.length; i++) {
324                     typeParameterNames[i]= "<"+typeParameters[i].getElementName()+">"; //$NON-NLS-1$ //$NON-NLS-2$
325
}
326                 addProposals(argument, typeParameterNames, JavaPluginImages.IMG_MISC_DEFAULT);
327             } else if (elem instanceof IType) {
328                 ITypeParameter[] typeParameters= ((IType) elem).getTypeParameters();
329                 String JavaDoc[] typeParameterNames= new String JavaDoc[typeParameters.length];
330                 for (int i= 0; i < typeParameters.length; i++) {
331                     typeParameterNames[i]= "<"+typeParameters[i].getElementName()+">"; //$NON-NLS-1$ //$NON-NLS-2$
332
}
333                 addProposals(argument, typeParameterNames, JavaPluginImages.IMG_MISC_DEFAULT);
334             }
335             return true;
336         } else if ("@throws".equals(tag) || "@exception".equals(tag)) { //$NON-NLS-2$ //$NON-NLS-1$
337
if (elem instanceof IMethod) {
338                 String JavaDoc[] exceptions= ((IMethod)elem).getExceptionTypes();
339                 for (int i= 0; i < exceptions.length; i++) {
340                     String JavaDoc curr= Signature.toString(exceptions[i]);
341                     if (prefixMatches(argument, curr)) {
342                         fResult.add(createCompletion(curr, argument, curr, JavaPluginImages.get(JavaPluginImages.IMG_OBJS_CLASS), null, 100));
343                     }
344                 }
345                 evalTypeNameCompletions((IMethod)elem, fCurrentPos - argument.length(), argument);
346             }
347             return true;
348         } else if ("@serialData".equals(tag)) { //$NON-NLS-1$
349
if (elem instanceof IField) {
350                 String JavaDoc name= ((IField)elem).getElementName();
351                 fResult.add(createCompletion(name, argument, name, fLabelProvider.getImage(elem), null, 0));
352             }
353             return true;
354         }
355         return false;
356     }
357
358     private void evalSeeTag(IMember elem, String JavaDoc arg) throws JavaModelException {
359         int wordStart= fCurrentPos - arg.length();
360         int pidx= arg.indexOf('#');
361         if (pidx == -1) {
362             evalTypeNameCompletions(elem, wordStart, arg);
363         } else {
364             IType parent= null;
365             if (pidx > 0) {
366                 // method or field
367
parent= getTypeNameResolve(elem, wordStart, wordStart + pidx);
368             } else {
369                 // '@see #foo'
370
parent= (IType) elem.getAncestor(IJavaElement.TYPE);
371             }
372
373             if (parent != null) {
374                 int nidx= arg.indexOf('(', pidx);
375                 if (nidx == -1) {
376                     nidx= arg.length();
377                 }
378                 String JavaDoc prefix= arg.substring(pidx + 1, nidx);
379
380                 addProposals(prefix, parent.getMethods());
381                 addProposals(prefix, parent.getFields());
382             }
383         }
384     }
385
386     private void evalTypeNameCompletions(IMember currElem, int wordStart, String JavaDoc arg) throws JavaModelException {
387         ICompilationUnit preparedCU= createPreparedCU(currElem, wordStart, fCurrentPos);
388         if (preparedCU != null) {
389             CompletionRequestor requestor= new CompletionRequestor() {
390                 public void accept(CompletionProposal proposal) {
391                     if (proposal.getKind() == CompletionProposal.TYPE_REF) {
392                         String JavaDoc fullTypeName= new String JavaDoc(Signature.toCharArray(proposal.getSignature()));
393                         if (TypeFilter.isFiltered(fullTypeName)) {
394                             return;
395                         }
396                         
397                         int start= proposal.getReplaceStart();
398                         int end= proposal.getReplaceEnd();
399                         char[] completion= proposal.getCompletion();
400                         fResult.add(createSeeTypeCompletion(proposal.getFlags(), start, end, completion, fullTypeName, proposal.getRelevance()));
401                     }
402                 }
403             };
404             try {
405                 preparedCU.codeComplete(fCurrentPos, requestor);
406                 if (currElem.getDeclaringType() == null && fCurrentPos > wordStart && currElem.getElementName().startsWith(arg)) {
407                     // for top level types, we use a fake import statement and the current type is never suggested
408
IType type= (IType) currElem;
409                     char[] name= type.getElementName().toCharArray();
410                     fResult.add(createSeeTypeCompletion(0, wordStart, fCurrentPos, name, JavaModelUtil.getFullyQualifiedName(type), 50));
411                 }
412             } finally {
413                 preparedCU.discardWorkingCopy();
414             }
415         }
416     }
417
418     private IType getTypeNameResolve(IMember elem, int wordStart, int wordEnd) throws JavaModelException {
419         ICompilationUnit preparedCU= createPreparedCU(elem, wordStart, wordEnd);
420         if (preparedCU != null) {
421             try {
422                 IJavaElement[] elements= preparedCU.codeSelect(wordEnd, 0);
423                 if (elements != null && elements.length == 1 && elements[0] instanceof IType) {
424                     IType type= (IType) elements[0];
425                     if (preparedCU.equals(type.getCompilationUnit())) {
426                         IJavaElement[] res= elem.getCompilationUnit().findElements(type);
427                         if (res.length > 0) {
428                             return (IType) res[0];
429                         }
430                     } else {
431                         return type;
432                     }
433                 }
434             } finally {
435                 preparedCU.getBuffer().setContents(fCompilationUnit.getBuffer().getCharacters());
436                 preparedCU.discardWorkingCopy();
437             }
438         }
439         return null;
440     }
441
442     private ICompilationUnit createPreparedCU(IMember elem, int wordStart, int wordEnd) throws JavaModelException {
443         int startpos= elem.getSourceRange().getOffset();
444         char[] content= (char[]) fCompilationUnit.getBuffer().getCharacters().clone();
445         if ((elem.getDeclaringType() == null) && (wordStart + 6 < content.length)) {
446             content[startpos++]= 'i'; content[startpos++]= 'm'; content[startpos++]= 'p';
447             content[startpos++]= 'o'; content[startpos++]= 'r'; content[startpos++]= 't';
448         }
449         if (wordStart < content.length) {
450             for (int i= startpos; i < wordStart; i++) {
451                 content[i]= ' ';
452             }
453         }
454
455         /*
456          * Explicitly create a new non-shared working copy.
457          */

458         ICompilationUnit newCU= fCompilationUnit.getWorkingCopy(null);
459         newCU.getBuffer().setContents(content);
460         return newCU;
461     }
462
463
464     private JavaCompletionProposal createCompletion(String JavaDoc newText, String JavaDoc oldText, String JavaDoc labelText, Image image, ProposalInfo proposalInfo, int severity) {
465         int offset= fCurrentPos - oldText.length();
466         int length= fCurrentLength + oldText.length();
467         if (fCurrentLength == 0)
468             length= findReplaceEndPos(fDocument, newText, oldText, fCurrentPos) - offset;
469
470         JavaCompletionProposal proposal= new JavaCompletionProposal(newText, offset, length, image, labelText, severity);
471         proposal.setProposalInfo(proposalInfo);
472         proposal.setTriggerCharacters( new char[] { '#' });
473         return proposal;
474     }
475
476     private JavaCompletionProposal createSeeTypeCompletion(int flags, int start, int end, char[] completion, String JavaDoc fullTypeName, int severity) {
477         ProposalInfo proposalInfo= new ProposalInfo(fCompilationUnit.getJavaProject(), fullTypeName);
478         StringBuffer JavaDoc nameBuffer= new StringBuffer JavaDoc();
479         String JavaDoc simpleName= Signature.getSimpleName(fullTypeName);
480         nameBuffer.append(simpleName);
481         if (simpleName.length() != fullTypeName.length()) {
482             nameBuffer.append(JavaElementLabels.CONCAT_STRING);
483             nameBuffer.append(Signature.getQualifier(fullTypeName));
484         }
485         ImageDescriptor desc= JavaElementImageProvider.getTypeImageDescriptor(false, false, flags, false);
486         Image image= JavaPlugin.getImageDescriptorRegistry().get(desc);
487
488         int compLen= completion.length;
489         if (compLen > 0 && completion[compLen - 1] == ';') {
490             compLen--; // remove the semicolon from import proposals
491
}
492
493         JavaCompletionProposal proposal= new JavaCompletionProposal(new String JavaDoc(completion, 0, compLen), start, end - start, image, nameBuffer.toString(), severity);
494         proposal.setProposalInfo(proposalInfo);
495         proposal.setTriggerCharacters( new char[] { '#' });
496         return proposal;
497     }
498
499
500     /* (non-Javadoc)
501      * @see org.eclipse.jdt.ui.text.java.IJavaDocCompletionProcessor#computeContextInformation(org.eclipse.jdt.core.ICompilationUnit, int)
502      */

503     public IContextInformation[] computeContextInformation(ICompilationUnit cu, int offset) {
504         fErrorMessage= null;
505         return null;
506     }
507
508     /* (non-Javadoc)
509      * @see org.eclipse.jdt.ui.text.java.IJavaDocCompletionProcessor#getErrorMessage()
510      */

511     public String JavaDoc getErrorMessage() {
512         return fErrorMessage;
513     }
514 }
515
Popular Tags