]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-before/ui/org/eclipse/jdt/internal/ui/text/java/LazyJavaTypeCompletionProposal.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / ui / org / eclipse / jdt / internal / ui / text / java / LazyJavaTypeCompletionProposal.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2011 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.core.runtime.CoreException;
14 import org.eclipse.core.runtime.NullProgressMonitor;
15
16 import org.eclipse.text.edits.TextEdit;
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.SharedASTProvider;
41 import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext;
42
43 import org.eclipse.jdt.internal.ui.JavaPlugin;
44
45
46 /**
47  * If passed compilation unit is not null, the replacement string will be seen as a qualified type name.
48   */
49 public class LazyJavaTypeCompletionProposal extends LazyJavaCompletionProposal {
50         /** Triggers for types. Do not modify. */
51         protected static final char[] TYPE_TRIGGERS= new char[] { '.', '\t', '[', '(', ' ' };
52         /** Triggers for types in javadoc. Do not modify. */
53         protected static final char[] JDOC_TYPE_TRIGGERS= new char[] { '#', '}', ' ', '.' };
54
55         /** The compilation unit, or <code>null</code> if none is available. */
56         protected final ICompilationUnit fCompilationUnit;
57
58         private String fQualifiedName;
59         private String fSimpleName;
60         private ImportRewrite fImportRewrite;
61         private ContextSensitiveImportRewriteContext fImportContext;
62
63         public LazyJavaTypeCompletionProposal(CompletionProposal proposal, JavaContentAssistInvocationContext context) {
64                 super(proposal, context);
65                 fCompilationUnit= context.getCompilationUnit();
66                 fQualifiedName= null;
67         }
68
69         void setImportRewrite(ImportRewrite importRewrite) {
70                 fImportRewrite= importRewrite;
71         }
72
73         public final String getQualifiedTypeName() {
74                 if (fQualifiedName == null)
75                         fQualifiedName= String.valueOf(Signature.toCharArray(Signature.getTypeErasure(fProposal.getSignature())));
76                 return fQualifiedName;
77         }
78
79         protected final String getSimpleTypeName() {
80                 if (fSimpleName == null)
81                         fSimpleName= Signature.getSimpleName(getQualifiedTypeName());
82                 return fSimpleName;
83         }
84
85         /*
86          * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeReplacementString()
87          */
88         @Override
89         protected String computeReplacementString() {
90                 String replacement= super.computeReplacementString();
91
92                 /* No import rewriting ever from within the import section. */
93                 if (isImportCompletion())
94                         return replacement;
95
96                 /* Always use the simple name for non-formal javadoc references to types. */
97                 // TODO fix
98                  if (fProposal.getKind() == CompletionProposal.TYPE_REF &&  fInvocationContext.getCoreContext().isInJavadocText())
99                          return getSimpleTypeName();
100
101                 String qualifiedTypeName= getQualifiedTypeName();
102
103                 // Type in package info must be fully qualified.
104                 if (fCompilationUnit != null && JavaModelUtil.isPackageInfo(fCompilationUnit))
105                         return qualifiedTypeName;
106
107                 if (qualifiedTypeName.indexOf('.') == -1 && replacement.length() > 0)
108                         // default package - no imports needed
109                         return qualifiedTypeName;
110
111                 /*
112                  * If the user types in the qualification, don't force import rewriting on him - insert the
113                  * qualified name.
114                  */
115                 IDocument document= fInvocationContext.getDocument();
116                 if (document != null) {
117                         String prefix= getPrefix(document, getReplacementOffset() + getReplacementLength());
118                         int dotIndex= prefix.lastIndexOf('.');
119                         // match up to the last dot in order to make higher level matching still work (camel case...)
120                         if (dotIndex != -1 && qualifiedTypeName.toLowerCase().startsWith(prefix.substring(0, dotIndex + 1).toLowerCase()))
121                                 return qualifiedTypeName;
122                 }
123
124                 /*
125                  * The replacement does not contain a qualification (e.g. an inner type qualified by its
126                  * parent) - use the replacement directly.
127                  */
128                 if (replacement.indexOf('.') == -1) {
129                         if (isInJavadoc())
130                                 return getSimpleTypeName(); // don't use the braces added for javadoc link proposals
131                         return replacement;
132                 }
133
134                 /* Add imports if the preference is on. */
135                 if (fImportRewrite == null)
136                         fImportRewrite= createImportRewrite();
137                 if (fImportRewrite != null) {
138                         return fImportRewrite.addImport(qualifiedTypeName, fImportContext);
139                 }
140
141                 // fall back for the case we don't have an import rewrite (see allowAddingImports)
142
143                 /* No imports for implicit imports. */
144                 if (fCompilationUnit != null && JavaModelUtil.isImplicitImport(Signature.getQualifier(qualifiedTypeName), fCompilationUnit)) {
145                         return Signature.getSimpleName(qualifiedTypeName);
146                 }
147
148                 /* Default: use the fully qualified type name. */
149                 return qualifiedTypeName;
150         }
151
152         protected final boolean isImportCompletion() {
153                 char[] completion= fProposal.getCompletion();
154                 if (completion.length == 0)
155                         return false;
156
157                 char last= completion[completion.length - 1];
158                 /*
159                  * Proposals end in a semicolon when completing types in normal imports or when completing
160                  * static members, in a period when completing types in static imports.
161                  */
162                 return last == ';' || last == '.';
163         }
164
165         private ImportRewrite createImportRewrite() {
166                 if (fCompilationUnit != null && allowAddingImports() && !JavaModelUtil.isPackageInfo(fCompilationUnit)) {
167                         try {
168                                 CompilationUnit cu= getASTRoot(fCompilationUnit);
169                                 if (cu == null) {
170                                         ImportRewrite rewrite= StubUtility.createImportRewrite(fCompilationUnit, true);
171                                         fImportContext= null;
172                                         return rewrite;
173                                 } else {
174                                         ImportRewrite rewrite= StubUtility.createImportRewrite(cu, true);
175                                         fImportContext= new ContextSensitiveImportRewriteContext(cu, fInvocationContext.getInvocationOffset(), rewrite);
176                                         return rewrite;
177                                 }
178                         } catch (CoreException x) {
179                                 JavaPlugin.log(x);
180                         }
181                 }
182                 return null;
183         }
184
185         private CompilationUnit getASTRoot(ICompilationUnit compilationUnit) {
186                 return SharedASTProvider.getAST(compilationUnit, SharedASTProvider.WAIT_NO, new NullProgressMonitor());
187         }
188
189         /*
190          * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#apply(org.eclipse.jface.text.IDocument, char, int)
191          */
192         @Override
193         public void apply(IDocument document, char trigger, int offset) {
194                 try {
195                         boolean insertClosingParenthesis= trigger == '(' && autocloseBrackets();
196                         if (insertClosingParenthesis) {
197                                 StringBuffer replacement= new StringBuffer(getReplacementString());
198                                 updateReplacementWithParentheses(replacement);
199                                 setReplacementString(replacement.toString());
200                                 trigger= '\0';
201                         }
202
203                         super.apply(document, trigger, offset);
204
205                         if (fImportRewrite != null && fImportRewrite.hasRecordedChanges()) {
206                                 int oldLen= document.getLength();
207                                 fImportRewrite.rewriteImports(new NullProgressMonitor()).apply(document, TextEdit.UPDATE_REGIONS);
208                                 setReplacementOffset(getReplacementOffset() + document.getLength() - oldLen);
209                         }
210
211                         if (insertClosingParenthesis)
212                                 setUpLinkedMode(document, ')');
213
214                         rememberSelection();
215                 } catch (CoreException e) {
216                         JavaPlugin.log(e);
217                 } catch (BadLocationException e) {
218                         JavaPlugin.log(e);
219                 }
220         }
221
222         protected void updateReplacementWithParentheses(StringBuffer replacement) {
223                 FormatterPrefs prefs= getFormatterPrefs();
224
225                 if (prefs.beforeOpeningParen)
226                         replacement.append(SPACE);
227                 replacement.append(LPAREN);
228
229
230                 if (prefs.afterOpeningParen)
231                         replacement.append(SPACE);
232
233                 setCursorPosition(replacement.length());
234
235                 if (prefs.afterOpeningParen)
236                         replacement.append(SPACE);
237
238                 replacement.append(RPAREN);
239         }
240
241         /**
242          * Remembers the selection in the content assist history.
243          *
244          * @throws JavaModelException if anything goes wrong
245          * @since 3.2
246          */
247         protected final void rememberSelection() throws JavaModelException {
248                 IType lhs= fInvocationContext.getExpectedType();
249                 IType rhs= (IType) getJavaElement();
250                 if (lhs != null && rhs != null)
251                         JavaPlugin.getDefault().getContentAssistHistory().remember(lhs, rhs);
252
253                 QualifiedTypeNameHistory.remember(getQualifiedTypeName());
254         }
255
256         /**
257          * Returns <code>true</code> if imports may be added. The return value depends on the context
258          * and preferences only and does not take into account the contents of the compilation unit or
259          * the kind of proposal. Even if <code>true</code> is returned, there may be cases where no
260          * imports are added for the proposal. For example:
261          * <ul>
262          * <li>when completing within the import section</li>
263          * <li>when completing informal javadoc references (e.g. within <code>&lt;code&gt;</code>
264          * tags)</li>
265          * <li>when completing a type that conflicts with an existing import</li>
266          * <li>when completing an implicitly imported type (same package, <code>java.lang</code>
267          * types)</li>
268          * </ul>
269          * <p>
270          * The decision whether a qualified type or the simple type name should be inserted must take
271          * into account these different scenarios.
272          * </p>
273          * <p>
274          * Subclasses may extend.
275          * </p>
276          *
277          * @return <code>true</code> if imports may be added, <code>false</code> if not
278          */
279         protected boolean allowAddingImports() {
280                 if (isInJavadoc()) {
281                         // TODO fix
282 //                      if (!fContext.isInJavadocFormalReference())
283 //                              return false;
284                         if (fProposal.getKind() == CompletionProposal.TYPE_REF &&  fInvocationContext.getCoreContext().isInJavadocText())
285                                 return false;
286
287                         if (!isJavadocProcessingEnabled())
288                                 return false;
289                 }
290
291                 IPreferenceStore preferenceStore= JavaPlugin.getDefault().getPreferenceStore();
292                 return preferenceStore.getBoolean(PreferenceConstants.CODEASSIST_ADDIMPORT);
293         }
294
295         private boolean isJavadocProcessingEnabled() {
296                 IJavaProject project= fCompilationUnit.getJavaProject();
297                 boolean processJavadoc;
298                 if (project == null)
299                         processJavadoc= JavaCore.ENABLED.equals(JavaCore.getOption(JavaCore.COMPILER_DOC_COMMENT_SUPPORT));
300                 else
301                         processJavadoc= JavaCore.ENABLED.equals(project.getOption(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, true));
302                 return processJavadoc;
303         }
304
305         /*
306          * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#isValidPrefix(java.lang.String)
307          */
308         @Override
309         protected boolean isValidPrefix(String prefix) {
310                 return isPrefix(prefix, getSimpleTypeName()) || isPrefix(prefix, getQualifiedTypeName());
311         }
312
313         /*
314          * @see org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposal#getCompletionText()
315          */
316         @Override
317         public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) {
318                 String prefix= getPrefix(document, completionOffset);
319
320                 String completion;
321                 // return the qualified name if the prefix is already qualified
322                 if (prefix.indexOf('.') != -1)
323                         completion= getQualifiedTypeName();
324                 else
325                         completion= getSimpleTypeName();
326
327                 if (isCamelCaseMatching())
328                         return getCamelCaseCompound(prefix, completion);
329
330                 return completion;
331         }
332
333         /*
334          * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeTriggerCharacters()
335          */
336         @Override
337         protected char[] computeTriggerCharacters() {
338                 return isInJavadoc() ? JDOC_TYPE_TRIGGERS : TYPE_TRIGGERS;
339         }
340
341         /*
342          * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeProposalInfo()
343          */
344         @Override
345         protected ProposalInfo computeProposalInfo() {
346                 if (fCompilationUnit != null) {
347                         IJavaProject project= fCompilationUnit.getJavaProject();
348                         if (project != null)
349                                 return new TypeProposalInfo(project, fProposal);
350                 }
351                 return super.computeProposalInfo();
352         }
353
354         /*
355          * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeSortString()
356          */
357         @Override
358         protected String computeSortString() {
359                 // try fast sort string to avoid display string creation
360                 return getSimpleTypeName() + Character.MIN_VALUE + getQualifiedTypeName();
361         }
362
363         /*
364          * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeRelevance()
365          */
366         @Override
367         protected int computeRelevance() {
368                 /*
369                  * There are two histories: the RHS history remembers types used for the current expected
370                  * type (left hand side), while the type history remembers recently used types in general).
371                  *
372                  * The presence of an RHS ranking is a much more precise sign for relevance as it proves the
373                  * subtype relationship between the proposed type and the expected type.
374                  *
375                  * The "recently used" factor (of either the RHS or general history) is less important, it should
376                  * not override other relevance factors such as if the type is already imported etc.
377                  */
378                 float rhsHistoryRank= fInvocationContext.getHistoryRelevance(getQualifiedTypeName());
379                 float typeHistoryRank= QualifiedTypeNameHistory.getDefault().getNormalizedPosition(getQualifiedTypeName());
380
381                 int recencyBoost= Math.round((rhsHistoryRank + typeHistoryRank) * 5);
382                 int rhsBoost= rhsHistoryRank > 0.0f ? 50 : 0;
383                 int baseRelevance= super.computeRelevance();
384
385                 return baseRelevance +  rhsBoost + recencyBoost;
386         }
387
388         /*
389          * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeContextInformation()
390          * @since 3.3
391          */
392         @Override
393         protected IContextInformation computeContextInformation() {
394                 char[] signature= fProposal.getSignature();
395                 char[][] typeParameters= Signature.getTypeArguments(signature);
396                 if (typeParameters.length == 0)
397                         return super.computeContextInformation();
398
399                 ProposalContextInformation contextInformation= new ProposalContextInformation(fProposal);
400                 if (fContextInformationPosition != 0 && fProposal.getCompletion().length == 0)
401                         contextInformation.setContextInformationPosition(fContextInformationPosition);
402                 return contextInformation;
403
404         }
405 }