PageRenderTime 20ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/ui/text/java/LazyJavaTypeCompletionProposal.java

https://bitbucket.org/tomotaro1065/webtools.jsdt.core
Java | 377 lines | 206 code | 52 blank | 119 comment | 60 complexity | 827b769d9d891b82bbd0d6243a705c5d MD5 | raw file
  1. /*******************************************************************************
  2. * Copyright (c) 2000, 2010 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.wst.jsdt.internal.ui.text.java;
  12. import org.eclipse.core.runtime.CoreException;
  13. import org.eclipse.core.runtime.NullProgressMonitor;
  14. import org.eclipse.jface.preference.IPreferenceStore;
  15. import org.eclipse.jface.text.BadLocationException;
  16. import org.eclipse.jface.text.IDocument;
  17. import org.eclipse.text.edits.TextEdit;
  18. import org.eclipse.wst.jsdt.core.CompletionProposal;
  19. import org.eclipse.wst.jsdt.core.IJavaScriptProject;
  20. import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  21. import org.eclipse.wst.jsdt.core.IType;
  22. import org.eclipse.wst.jsdt.core.JavaScriptCore;
  23. import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  24. import org.eclipse.wst.jsdt.core.NJSDocCompletionProposal;
  25. import org.eclipse.wst.jsdt.core.Signature;
  26. import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
  27. import org.eclipse.wst.jsdt.core.dom.rewrite.ImportRewrite;
  28. import org.eclipse.wst.jsdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
  29. import org.eclipse.wst.jsdt.internal.corext.codemanipulation.StubUtility;
  30. import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil;
  31. import org.eclipse.wst.jsdt.internal.corext.util.QualifiedTypeNameHistory;
  32. import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  33. import org.eclipse.wst.jsdt.internal.ui.javaeditor.ASTProvider;
  34. import org.eclipse.wst.jsdt.ui.PreferenceConstants;
  35. import org.eclipse.wst.jsdt.ui.text.java.JavaContentAssistInvocationContext;
  36. /**
  37. * If passed compilation unit is not null, the replacement string will be seen as a qualified type name.
  38. */
  39. public class LazyJavaTypeCompletionProposal extends LazyJavaCompletionProposal {
  40. /** Triggers for types. Do not modify. */
  41. protected static final char[] TYPE_TRIGGERS= new char[] { '\t', '[', '(', ' ' };
  42. /** Triggers for types in javadoc. Do not modify. */
  43. protected static final char[] JDOC_TYPE_TRIGGERS= new char[] { '#', '}', ' ', '.' };
  44. /** The compilation unit, or <code>null</code> if none is available. */
  45. protected final IJavaScriptUnit fCompilationUnit;
  46. private String fQualifiedName;
  47. private String fSimpleName;
  48. private ImportRewrite fImportRewrite;
  49. private ContextSensitiveImportRewriteContext fImportContext;
  50. public LazyJavaTypeCompletionProposal(CompletionProposal proposal, JavaContentAssistInvocationContext context) {
  51. super(proposal, context);
  52. fCompilationUnit= context.getCompilationUnit();
  53. fQualifiedName= null;
  54. }
  55. public final String getQualifiedTypeName() {
  56. if (fQualifiedName == null)
  57. if (fProposal instanceof NJSDocCompletionProposal) {
  58. fQualifiedName = ((NJSDocCompletionProposal)fProposal).getQualifiedTypeName();
  59. } else {
  60. fQualifiedName= String.valueOf(Signature.toCharArray(fProposal.getSignature()));
  61. }
  62. return fQualifiedName;
  63. }
  64. protected final String getSimpleTypeName() {
  65. if (fSimpleName == null)
  66. fSimpleName= Signature.getSimpleName(getQualifiedTypeName());
  67. return fSimpleName;
  68. }
  69. /*
  70. * @see org.eclipse.wst.jsdt.internal.ui.text.java.LazyJavaCompletionProposal#computeReplacementString()
  71. */
  72. protected String computeReplacementString() {
  73. String replacement= super.computeReplacementString();
  74. // /* No import rewriting ever from within the import section. */
  75. // if (isImportCompletion())
  76. // return replacement;
  77. /* Always use the simple name for non-formal javadoc references to types. */
  78. // TODO fix
  79. if (fProposal.getKind() == CompletionProposal.TYPE_REF && fInvocationContext.getCoreContext().isInJsdocText())
  80. return getSimpleTypeName();
  81. String qualifiedTypeName= replacement;
  82. // if (qualifiedTypeName.indexOf('.') == -1)
  83. // // default package - no imports needed
  84. // return qualifiedTypeName;
  85. /*
  86. * If the user types in the qualification, don't force import rewriting on him - insert the
  87. * qualified name.
  88. */
  89. IDocument document= fInvocationContext.getDocument();
  90. if (document != null) {
  91. String prefix= getPrefix(document, getReplacementOffset() + getReplacementLength());
  92. int dotIndex= prefix.lastIndexOf('.');
  93. // match up to the last dot in order to make higher level matching still work (camel case...)
  94. if (dotIndex != -1 && qualifiedTypeName.toLowerCase().startsWith(prefix.substring(0, dotIndex + 1).toLowerCase()))
  95. return qualifiedTypeName;
  96. }
  97. /*
  98. * The replacement does not contain a qualification (e.g. an inner type qualified by its
  99. * parent) - use the replacement directly.
  100. */
  101. if (replacement.indexOf('.') == -1) {
  102. if (isInJavadoc())
  103. return getSimpleTypeName(); // don't use the braces added for javadoc link proposals
  104. return replacement;
  105. }
  106. /* Add imports if the preference is on. */
  107. fImportRewrite= createImportRewrite();
  108. // if (fImportRewrite != null) {
  109. // String packageName=null;
  110. // try {
  111. // IJavaScriptElement javaElement = this.getProposalInfo().getJavaElement();
  112. // packageName=JavaModelUtil.getFilePackage(javaElement);
  113. // } catch (JavaScriptModelException e) {
  114. // JavaScriptPlugin.log(e);
  115. // }
  116. // return fImportRewrite.addImport(qualifiedTypeName,packageName, fImportContext);
  117. // }
  118. // fall back for the case we don't have an import rewrite (see allowAddingImports)
  119. /* No imports for implicit imports. */
  120. if (fCompilationUnit != null && JavaModelUtil.isImplicitImport(Signature.getQualifier(qualifiedTypeName), fCompilationUnit)) {
  121. return Signature.getSimpleName(qualifiedTypeName);
  122. }
  123. /* Default: use the fully qualified type name. */
  124. return qualifiedTypeName;
  125. }
  126. protected final boolean isImportCompletion() {
  127. char[] completion= fProposal.getCompletion();
  128. if (completion.length == 0)
  129. return false;
  130. char last= completion[completion.length - 1];
  131. /*
  132. * Proposals end in a semicolon when completing types in normal imports or when completing
  133. * static members, in a period when completing types in static imports.
  134. */
  135. return last == ';' || last == '.';
  136. }
  137. private ImportRewrite createImportRewrite() {
  138. if (fCompilationUnit != null && allowAddingImports()) {
  139. try {
  140. JavaScriptUnit cu= getASTRoot(fCompilationUnit);
  141. if (cu == null) {
  142. ImportRewrite rewrite= StubUtility.createImportRewrite(fCompilationUnit, true);
  143. fImportContext= null;
  144. return rewrite;
  145. } else {
  146. ImportRewrite rewrite= StubUtility.createImportRewrite(cu, true);
  147. fImportContext= new ContextSensitiveImportRewriteContext(cu, fInvocationContext.getInvocationOffset(), rewrite);
  148. return rewrite;
  149. }
  150. } catch (CoreException x) {
  151. JavaScriptPlugin.log(x);
  152. }
  153. }
  154. return null;
  155. }
  156. private JavaScriptUnit getASTRoot(IJavaScriptUnit compilationUnit) {
  157. return JavaScriptPlugin.getDefault().getASTProvider().getAST(compilationUnit, ASTProvider.WAIT_NO, new NullProgressMonitor());
  158. }
  159. /*
  160. * @see org.eclipse.wst.jsdt.internal.ui.text.java.LazyJavaCompletionProposal#apply(org.eclipse.jface.text.IDocument, char, int)
  161. */
  162. public void apply(IDocument document, char trigger, int offset) {
  163. try {
  164. boolean insertClosingParenthesis= trigger == '(' && autocloseBrackets();
  165. if (insertClosingParenthesis) {
  166. StringBuffer replacement= new StringBuffer(getReplacementString());
  167. updateReplacementWithParentheses(replacement);
  168. setReplacementString(replacement.toString());
  169. trigger= '\0';
  170. }
  171. super.apply(document, trigger, offset);
  172. if (fImportRewrite != null && fImportRewrite.hasRecordedChanges()) {
  173. int oldLen= document.getLength();
  174. fImportRewrite.rewriteImports(new NullProgressMonitor()).apply(document, TextEdit.UPDATE_REGIONS);
  175. setReplacementOffset(getReplacementOffset() + document.getLength() - oldLen);
  176. }
  177. if (insertClosingParenthesis)
  178. setUpLinkedMode(document, ')');
  179. rememberSelection();
  180. } catch (CoreException e) {
  181. JavaScriptPlugin.log(e);
  182. } catch (BadLocationException e) {
  183. JavaScriptPlugin.log(e);
  184. }
  185. }
  186. protected void updateReplacementWithParentheses(StringBuffer replacement) {
  187. FormatterPrefs prefs= getFormatterPrefs();
  188. if (prefs.beforeOpeningParen)
  189. replacement.append(SPACE);
  190. replacement.append(LPAREN);
  191. if (prefs.afterOpeningParen)
  192. replacement.append(SPACE);
  193. setCursorPosition(replacement.length());
  194. if (prefs.afterOpeningParen)
  195. replacement.append(SPACE);
  196. replacement.append(RPAREN);
  197. }
  198. /**
  199. * Remembers the selection in the content assist history.
  200. *
  201. * @throws JavaScriptModelException if anything goes wrong
  202. *
  203. */
  204. protected final void rememberSelection() throws JavaScriptModelException {
  205. IType lhs= fInvocationContext.getExpectedType();
  206. IType rhs= (IType) getJavaElement();
  207. if (lhs != null && rhs != null)
  208. JavaScriptPlugin.getDefault().getContentAssistHistory().remember(lhs, rhs);
  209. QualifiedTypeNameHistory.remember(getQualifiedTypeName());
  210. }
  211. /**
  212. * Returns <code>true</code> if imports may be added. The return value depends on the context
  213. * and preferences only and does not take into account the contents of the compilation unit or
  214. * the kind of proposal. Even if <code>true</code> is returned, there may be cases where no
  215. * imports are added for the proposal. For example:
  216. * <ul>
  217. * <li>when completing within the import section</li>
  218. * <li>when completing informal javadoc references (e.g. within <code>&lt;code&gt;</code>
  219. * tags)</li>
  220. * <li>when completing a type that conflicts with an existing import</li>
  221. * <li>when completing an implicitly imported type (same package, <code>java.lang</code>
  222. * types)</li>
  223. * </ul>
  224. * <p>
  225. * The decision whether a qualified type or the simple type name should be inserted must take
  226. * into account these different scenarios.
  227. * </p>
  228. * <p>
  229. * Subclasses may extend.
  230. * </p>
  231. *
  232. * @return <code>true</code> if imports may be added, <code>false</code> if not
  233. */
  234. protected boolean allowAddingImports() {
  235. if (isInJavadoc()) {
  236. // TODO fix
  237. // if (!fContext.isInJavadocFormalReference())
  238. // return false;
  239. if (fProposal.getKind() == CompletionProposal.TYPE_REF && fInvocationContext.getCoreContext().isInJsdocText())
  240. return false;
  241. if (!isJavadocProcessingEnabled())
  242. return false;
  243. }
  244. IPreferenceStore preferenceStore= JavaScriptPlugin.getDefault().getPreferenceStore();
  245. return preferenceStore.getBoolean(PreferenceConstants.CODEASSIST_ADDIMPORT);
  246. }
  247. private boolean isJavadocProcessingEnabled() {
  248. IJavaScriptProject project= fCompilationUnit.getJavaScriptProject();
  249. boolean processJavadoc;
  250. if (project == null)
  251. processJavadoc= JavaScriptCore.ENABLED.equals(JavaScriptCore.getOption(JavaScriptCore.COMPILER_DOC_COMMENT_SUPPORT));
  252. else
  253. processJavadoc= JavaScriptCore.ENABLED.equals(project.getOption(JavaScriptCore.COMPILER_DOC_COMMENT_SUPPORT, true));
  254. return processJavadoc;
  255. }
  256. /*
  257. * @see org.eclipse.wst.jsdt.internal.ui.text.java.LazyJavaCompletionProposal#isValidPrefix(java.lang.String)
  258. */
  259. protected boolean isValidPrefix(String prefix) {
  260. return isPrefix(prefix, getSimpleTypeName()) || isPrefix(prefix, getQualifiedTypeName());
  261. }
  262. public boolean isValidTypePrefix(String prefix) {
  263. return isValidPrefix(prefix);
  264. }
  265. /*
  266. * @see org.eclipse.wst.jsdt.internal.ui.text.java.JavaCompletionProposal#getCompletionText()
  267. */
  268. public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) {
  269. String prefix= getPrefix(document, completionOffset);
  270. String completion;
  271. // return the qualified name if the prefix is already qualified
  272. if (prefix.indexOf('.') != -1)
  273. completion= getQualifiedTypeName();
  274. else
  275. completion= getSimpleTypeName();
  276. if (isCamelCaseMatching())
  277. return getCamelCaseCompound(prefix, completion);
  278. return completion;
  279. }
  280. /*
  281. * @see org.eclipse.wst.jsdt.internal.ui.text.java.LazyJavaCompletionProposal#computeTriggerCharacters()
  282. */
  283. protected char[] computeTriggerCharacters() {
  284. return isInJavadoc() ? JDOC_TYPE_TRIGGERS : TYPE_TRIGGERS;
  285. }
  286. /*
  287. * @see org.eclipse.wst.jsdt.internal.ui.text.java.LazyJavaCompletionProposal#computeProposalInfo()
  288. */
  289. protected ProposalInfo computeProposalInfo() {
  290. if (fCompilationUnit != null) {
  291. IJavaScriptProject project= fCompilationUnit.getJavaScriptProject();
  292. if (project != null)
  293. return new TypeProposalInfo(project, fProposal);
  294. }
  295. return super.computeProposalInfo();
  296. }
  297. /*
  298. * @see org.eclipse.wst.jsdt.internal.ui.text.java.LazyJavaCompletionProposal#computeSortString()
  299. */
  300. protected String computeSortString() {
  301. // try fast sort string to avoid display string creation
  302. return getQualifiedTypeName();
  303. }
  304. /*
  305. * @see org.eclipse.wst.jsdt.internal.ui.text.java.LazyJavaCompletionProposal#computeRelevance()
  306. */
  307. protected int computeRelevance() {
  308. /*
  309. * There are two histories: the RHS history remembers types used for the current expected
  310. * type (left hand side), while the type history remembers recently used types in general).
  311. *
  312. * The presence of an RHS ranking is a much more precise sign for relevance as it proves the
  313. * subtype relationship between the proposed type and the expected type.
  314. *
  315. * The "recently used" factor (of either the RHS or general history) is less important, it should
  316. * not override other relevance factors such as if the type is already imported etc.
  317. */
  318. float rhsHistoryRank= fInvocationContext.getHistoryRelevance(getQualifiedTypeName());
  319. float typeHistoryRank= QualifiedTypeNameHistory.getDefault().getNormalizedPosition(getQualifiedTypeName());
  320. int recencyBoost= Math.round((rhsHistoryRank + typeHistoryRank) * 5);
  321. int rhsBoost= rhsHistoryRank > 0.0f ? 50 : 0;
  322. int baseRelevance= super.computeRelevance();
  323. return baseRelevance + rhsBoost + recencyBoost;
  324. }
  325. }