KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > java > LazyJavaTypeCompletionProposal


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 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.java;
12
13 import org.eclipse.text.edits.TextEdit;
14
15 import org.eclipse.core.runtime.CoreException;
16 import org.eclipse.core.runtime.NullProgressMonitor;
17
18 import org.eclipse.jface.preference.IPreferenceStore;
19
20 import org.eclipse.jface.text.BadLocationException;
21 import org.eclipse.jface.text.IDocument;
22 import org.eclipse.jface.text.contentassist.IContextInformation;
23
24 import org.eclipse.jdt.core.CompletionProposal;
25 import org.eclipse.jdt.core.ICompilationUnit;
26 import org.eclipse.jdt.core.IJavaProject;
27 import org.eclipse.jdt.core.IType;
28 import org.eclipse.jdt.core.JavaCore;
29 import org.eclipse.jdt.core.JavaModelException;
30 import org.eclipse.jdt.core.Signature;
31 import org.eclipse.jdt.core.dom.CompilationUnit;
32 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
33
34 import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
35 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
36 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
37 import org.eclipse.jdt.internal.corext.util.QualifiedTypeNameHistory;
38
39 import org.eclipse.jdt.ui.PreferenceConstants;
40 import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext;
41
42 import org.eclipse.jdt.internal.ui.JavaPlugin;
43 import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
44
45 /**
46  * If passed compilation unit is not null, the replacement string will be seen as a qualified type name.
47   */

48 public class LazyJavaTypeCompletionProposal extends LazyJavaCompletionProposal {
49     /** Triggers for types. Do not modify. */
50     protected static final char[] TYPE_TRIGGERS= new char[] { '.', '\t', '[', '(', ' ' };
51     /** Triggers for types in javadoc. Do not modify. */
52     protected static final char[] JDOC_TYPE_TRIGGERS= new char[] { '#', '}', ' ', '.' };
53
54     /** The compilation unit, or <code>null</code> if none is available. */
55     protected final ICompilationUnit fCompilationUnit;
56
57     private String JavaDoc fQualifiedName;
58     private String JavaDoc fSimpleName;
59     private ImportRewrite fImportRewrite;
60     private ContextSensitiveImportRewriteContext fImportContext;
61
62     public LazyJavaTypeCompletionProposal(CompletionProposal proposal, JavaContentAssistInvocationContext context) {
63         super(proposal, context);
64         fCompilationUnit= context.getCompilationUnit();
65         fQualifiedName= null;
66     }
67     
68     public final String JavaDoc getQualifiedTypeName() {
69         if (fQualifiedName == null)
70             fQualifiedName= String.valueOf(Signature.toCharArray(Signature.getTypeErasure(fProposal.getSignature())));
71         return fQualifiedName;
72     }
73     
74     protected final String JavaDoc getSimpleTypeName() {
75         if (fSimpleName == null)
76             fSimpleName= Signature.getSimpleName(getQualifiedTypeName());
77         return fSimpleName;
78     }
79
80     /*
81      * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeReplacementString()
82      */

83     protected String JavaDoc computeReplacementString() {
84         String JavaDoc replacement= super.computeReplacementString();
85
86         /* No import rewriting ever from within the import section. */
87         if (isImportCompletion())
88             return replacement;
89         
90         /* Always use the simple name for non-formal javadoc references to types. */
91         // TODO fix
92
if (fProposal.getKind() == CompletionProposal.TYPE_REF && fInvocationContext.getCoreContext().isInJavadocText())
93              return getSimpleTypeName();
94         
95         String JavaDoc qualifiedTypeName= getQualifiedTypeName();
96         if (qualifiedTypeName.indexOf('.') == -1)
97             // default package - no imports needed
98
return qualifiedTypeName;
99
100         /*
101          * If the user types in the qualification, don't force import rewriting on him - insert the
102          * qualified name.
103          */

104         IDocument document= fInvocationContext.getDocument();
105         if (document != null) {
106             String JavaDoc prefix= getPrefix(document, getReplacementOffset() + getReplacementLength());
107             int dotIndex= prefix.lastIndexOf('.');
108             // match up to the last dot in order to make higher level matching still work (camel case...)
109
if (dotIndex != -1 && qualifiedTypeName.toLowerCase().startsWith(prefix.substring(0, dotIndex + 1).toLowerCase()))
110                 return qualifiedTypeName;
111         }
112         
113         /*
114          * The replacement does not contain a qualification (e.g. an inner type qualified by its
115          * parent) - use the replacement directly.
116          */

117         if (replacement.indexOf('.') == -1) {
118             if (isInJavadoc())
119                 return getSimpleTypeName(); // don't use the braces added for javadoc link proposals
120
return replacement;
121         }
122         
123         /* Add imports if the preference is on. */
124         fImportRewrite= createImportRewrite();
125         if (fImportRewrite != null) {
126             return fImportRewrite.addImport(qualifiedTypeName, fImportContext);
127         }
128         
129         // fall back for the case we don't have an import rewrite (see allowAddingImports)
130

131         /* No imports for implicit imports. */
132         if (fCompilationUnit != null && JavaModelUtil.isImplicitImport(Signature.getQualifier(qualifiedTypeName), fCompilationUnit)) {
133             return Signature.getSimpleName(qualifiedTypeName);
134         }
135         
136         /* Default: use the fully qualified type name. */
137         return qualifiedTypeName;
138     }
139
140     protected final boolean isImportCompletion() {
141         char[] completion= fProposal.getCompletion();
142         if (completion.length == 0)
143             return false;
144         
145         char last= completion[completion.length - 1];
146         /*
147          * Proposals end in a semicolon when completing types in normal imports or when completing
148          * static members, in a period when completing types in static imports.
149          */

150         return last == ';' || last == '.';
151     }
152
153     private ImportRewrite createImportRewrite() {
154         if (fCompilationUnit != null && allowAddingImports()) {
155             try {
156                 CompilationUnit cu= getASTRoot(fCompilationUnit);
157                 if (cu == null) {
158                     ImportRewrite rewrite= StubUtility.createImportRewrite(fCompilationUnit, true);
159                     fImportContext= null;
160                     return rewrite;
161                 } else {
162                     ImportRewrite rewrite= StubUtility.createImportRewrite(cu, true);
163                     fImportContext= new ContextSensitiveImportRewriteContext(cu, fInvocationContext.getInvocationOffset(), rewrite);
164                     return rewrite;
165                 }
166             } catch (CoreException x) {
167                 JavaPlugin.log(x);
168             }
169         }
170         return null;
171     }
172
173     private CompilationUnit getASTRoot(ICompilationUnit compilationUnit) {
174         return JavaPlugin.getDefault().getASTProvider().getAST(compilationUnit, ASTProvider.WAIT_NO, new NullProgressMonitor());
175     }
176
177     /*
178      * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#apply(org.eclipse.jface.text.IDocument, char, int)
179      */

180     public void apply(IDocument document, char trigger, int offset) {
181         try {
182             boolean insertClosingParenthesis= trigger == '(' && autocloseBrackets();
183             if (insertClosingParenthesis) {
184                 StringBuffer JavaDoc replacement= new StringBuffer JavaDoc(getReplacementString());
185                 updateReplacementWithParentheses(replacement);
186                 setReplacementString(replacement.toString());
187                 trigger= '\0';
188             }
189             
190             super.apply(document, trigger, offset);
191
192             if (fImportRewrite != null && fImportRewrite.hasRecordedChanges()) {
193                 int oldLen= document.getLength();
194                 fImportRewrite.rewriteImports(new NullProgressMonitor()).apply(document, TextEdit.UPDATE_REGIONS);
195                 setReplacementOffset(getReplacementOffset() + document.getLength() - oldLen);
196             }
197             
198             if (insertClosingParenthesis)
199                 setUpLinkedMode(document, ')');
200             
201             rememberSelection();
202         } catch (CoreException e) {
203             JavaPlugin.log(e);
204         } catch (BadLocationException e) {
205             JavaPlugin.log(e);
206         }
207     }
208
209     protected void updateReplacementWithParentheses(StringBuffer JavaDoc replacement) {
210         FormatterPrefs prefs= getFormatterPrefs();
211
212         if (prefs.beforeOpeningParen)
213             replacement.append(SPACE);
214         replacement.append(LPAREN);
215
216
217         if (prefs.afterOpeningParen)
218             replacement.append(SPACE);
219
220         setCursorPosition(replacement.length());
221         
222         if (prefs.afterOpeningParen)
223             replacement.append(SPACE);
224         
225         replacement.append(RPAREN);
226     }
227
228     /**
229      * Remembers the selection in the content assist history.
230      *
231      * @throws JavaModelException if anything goes wrong
232      * @since 3.2
233      */

234     protected final void rememberSelection() throws JavaModelException {
235         IType lhs= fInvocationContext.getExpectedType();
236         IType rhs= (IType) getJavaElement();
237         if (lhs != null && rhs != null)
238             JavaPlugin.getDefault().getContentAssistHistory().remember(lhs, rhs);
239         
240         QualifiedTypeNameHistory.remember(getQualifiedTypeName());
241     }
242
243     /**
244      * Returns <code>true</code> if imports may be added. The return value depends on the context
245      * and preferences only and does not take into account the contents of the compilation unit or
246      * the kind of proposal. Even if <code>true</code> is returned, there may be cases where no
247      * imports are added for the proposal. For example:
248      * <ul>
249      * <li>when completing within the import section</li>
250      * <li>when completing informal javadoc references (e.g. within <code>&lt;code&gt;</code>
251      * tags)</li>
252      * <li>when completing a type that conflicts with an existing import</li>
253      * <li>when completing an implicitly imported type (same package, <code>java.lang</code>
254      * types)</li>
255      * </ul>
256      * <p>
257      * The decision whether a qualified type or the simple type name should be inserted must take
258      * into account these different scenarios.
259      * </p>
260      * <p>
261      * Subclasses may extend.
262      * </p>
263      *
264      * @return <code>true</code> if imports may be added, <code>false</code> if not
265      */

266     protected boolean allowAddingImports() {
267         if (isInJavadoc()) {
268             // TODO fix
269
// if (!fContext.isInJavadocFormalReference())
270
// return false;
271
if (fProposal.getKind() == CompletionProposal.TYPE_REF && fInvocationContext.getCoreContext().isInJavadocText())
272                 return false;
273             
274             if (!isJavadocProcessingEnabled())
275                 return false;
276         }
277         
278         IPreferenceStore preferenceStore= JavaPlugin.getDefault().getPreferenceStore();
279         return preferenceStore.getBoolean(PreferenceConstants.CODEASSIST_ADDIMPORT);
280     }
281
282     private boolean isJavadocProcessingEnabled() {
283         IJavaProject project= fCompilationUnit.getJavaProject();
284         boolean processJavadoc;
285         if (project == null)
286             processJavadoc= JavaCore.ENABLED.equals(JavaCore.getOption(JavaCore.COMPILER_DOC_COMMENT_SUPPORT));
287         else
288             processJavadoc= JavaCore.ENABLED.equals(project.getOption(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, true));
289         return processJavadoc;
290     }
291
292     /*
293      * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#isValidPrefix(java.lang.String)
294      */

295     protected boolean isValidPrefix(String JavaDoc prefix) {
296         return isPrefix(prefix, getSimpleTypeName()) || isPrefix(prefix, getQualifiedTypeName());
297     }
298
299     /*
300      * @see org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposal#getCompletionText()
301      */

302     public CharSequence JavaDoc getPrefixCompletionText(IDocument document, int completionOffset) {
303         String JavaDoc prefix= getPrefix(document, completionOffset);
304         
305         String JavaDoc completion;
306         // return the qualified name if the prefix is already qualified
307
if (prefix.indexOf('.') != -1)
308             completion= getQualifiedTypeName();
309         else
310             completion= getSimpleTypeName();
311         
312         if (isCamelCaseMatching())
313             return getCamelCaseCompound(prefix, completion);
314
315         return completion;
316     }
317     
318     /*
319      * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeTriggerCharacters()
320      */

321     protected char[] computeTriggerCharacters() {
322         return isInJavadoc() ? JDOC_TYPE_TRIGGERS : TYPE_TRIGGERS;
323     }
324     
325     /*
326      * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeProposalInfo()
327      */

328     protected ProposalInfo computeProposalInfo() {
329         if (fCompilationUnit != null) {
330             IJavaProject project= fCompilationUnit.getJavaProject();
331             if (project != null)
332                 return new TypeProposalInfo(project, fProposal);
333         }
334         return super.computeProposalInfo();
335     }
336
337     /*
338      * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeSortString()
339      */

340     protected String JavaDoc computeSortString() {
341         // try fast sort string to avoid display string creation
342
return getSimpleTypeName() + Character.MIN_VALUE + getQualifiedTypeName();
343     }
344     
345     /*
346      * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeRelevance()
347      */

348     protected int computeRelevance() {
349         /*
350          * There are two histories: the RHS history remembers types used for the current expected
351          * type (left hand side), while the type history remembers recently used types in general).
352          *
353          * The presence of an RHS ranking is a much more precise sign for relevance as it proves the
354          * subtype relationship between the proposed type and the expected type.
355          *
356          * The "recently used" factor (of either the RHS or general history) is less important, it should
357          * not override other relevance factors such as if the type is already imported etc.
358          */

359         float rhsHistoryRank= fInvocationContext.getHistoryRelevance(getQualifiedTypeName());
360         float typeHistoryRank= QualifiedTypeNameHistory.getDefault().getNormalizedPosition(getQualifiedTypeName());
361
362         int recencyBoost= Math.round((rhsHistoryRank + typeHistoryRank) * 5);
363         int rhsBoost= rhsHistoryRank > 0.0f ? 50 : 0;
364         int baseRelevance= super.computeRelevance();
365         
366         return baseRelevance + rhsBoost + recencyBoost;
367     }
368     
369     /*
370      * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeContextInformation()
371      * @since 3.3
372      */

373     protected IContextInformation computeContextInformation() {
374         char[] signature= fProposal.getSignature();
375         char[][] typeParameters= Signature.getTypeArguments(signature);
376         if (typeParameters.length == 0)
377             return super.computeContextInformation();
378         
379         ProposalContextInformation contextInformation= new ProposalContextInformation(fProposal);
380         if (fContextInformationPosition != 0 && fProposal.getCompletion().length == 0)
381             contextInformation.setContextInformationPosition(fContextInformationPosition);
382         return contextInformation;
383
384     }
385 }
386
Popular Tags