/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.codeassistant; import org.eclipse.che.ide.ext.java.jdt.core.CompletionProposal; import org.eclipse.che.ide.ext.java.jdt.core.IType; import org.eclipse.che.ide.ext.java.jdt.core.JavaCore; import org.eclipse.che.ide.ext.java.jdt.core.Signature; import org.eclipse.che.ide.ext.java.jdt.core.dom.CompilationUnit; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.StubUtility; import org.eclipse.che.ide.ext.java.jdt.internal.corext.util.ModelUtil; import org.eclipse.che.ide.ext.java.worker.WorkerMessageHandler; import org.eclipse.che.ide.ext.java.jdt.text.Document; import org.eclipse.che.ide.ext.java.jdt.text.edits.TextEdit; import org.eclipse.che.ide.api.text.BadLocationException; /** If passed compilation unit is not null, the replacement string will be seen as a qualified type name. */ public class LazyJavaTypeCompletionProposal extends LazyJavaCompletionProposal { /** Triggers for types. Do not modify. */ protected static final char[] TYPE_TRIGGERS = new char[]{'.', '\t', '[', '(', ' '}; /** Triggers for types in javadoc. Do not modify. */ protected static final char[] JDOC_TYPE_TRIGGERS = new char[]{'#', '}', ' ', '.'}; private String fQualifiedName; private String fSimpleName; private ImportRewrite fImportRewrite; private ContextSensitiveImportRewriteContext fImportContext; private CompilationUnit fCompilationUnit; public LazyJavaTypeCompletionProposal(CompletionProposal proposal, JavaContentAssistInvocationContext context) { super(proposal, context); fCompilationUnit = context.getCompilationUnit(); fQualifiedName = null; } void setImportRewrite(ImportRewrite importRewrite) { fImportRewrite = importRewrite; } public final String getQualifiedTypeName() { if (fQualifiedName == null) fQualifiedName = String.valueOf(Signature.toCharArray(Signature.getTypeErasure(fProposal.getSignature()))); return fQualifiedName; } protected final String getSimpleTypeName() { if (fSimpleName == null) fSimpleName = Signature.getSimpleName(getQualifiedTypeName()); return fSimpleName; } /* * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeReplacementString() */ @Override protected String computeReplacementString() { String replacement = super.computeReplacementString(); /* No import rewriting ever from within the import section. */ if (isImportCompletion()) return replacement; /* Always use the simple name for non-formal javadoc references to types. */ // TODO fix if (fProposal.getKind() == CompletionProposal.TYPE_REF && fInvocationContext.getCoreContext().isInJavadocText()) return getSimpleTypeName(); String qualifiedTypeName = getQualifiedTypeName(); // // Type in package info must be fully qualified. // if (fCompilationUnit != null && JavaModelUtil.isPackageInfo(fCompilationUnit)) // return qualifiedTypeName; if (qualifiedTypeName.indexOf('.') == -1 && replacement.length() > 0) // default package - no imports needed return qualifiedTypeName; /* * If the user types in the qualification, don't force import rewriting on him - insert the qualified name. */ Document document = fInvocationContext.getDocument(); if (document != null) { String prefix = getPrefix(document, getReplacementOffset() + getReplacementLength()); int dotIndex = prefix.lastIndexOf('.'); // match up to the last dot in order to make higher level matching still work (camel case...) if (dotIndex != -1 && qualifiedTypeName.toLowerCase().startsWith(prefix.substring(0, dotIndex + 1).toLowerCase())) return qualifiedTypeName; } /* * The replacement does not contain a qualification (e.g. an inner type qualified by its parent) - use the replacement * directly. */ if (replacement.indexOf('.') == -1) { if (isInJavadoc()) return getSimpleTypeName(); // don't use the braces added for javadoc link proposals return replacement; } /* Add imports if the preference is on. */ if (fImportRewrite == null) fImportRewrite = createImportRewrite(); if (fImportRewrite != null) { return fImportRewrite.addImport(qualifiedTypeName, fImportContext); } // fall back for the case we don't have an import rewrite (see allowAddingImports) /* No imports for implicit imports. */ if (fCompilationUnit != null && ModelUtil.isImplicitImport(Signature.getQualifier(qualifiedTypeName), fCompilationUnit)) { return Signature.getSimpleName(qualifiedTypeName); } /* Default: use the fully qualified type name. */ return qualifiedTypeName; } protected final boolean isImportCompletion() { char[] completion = fProposal.getCompletion(); if (completion.length == 0) return false; char last = completion[completion.length - 1]; /* * Proposals end in a semicolon when completing types in normal imports or when completing static members, in a period when * completing types in static imports. */ return last == ';' || last == '.'; } private ImportRewrite createImportRewrite() { if (fCompilationUnit != null && allowAddingImports()) { ImportRewrite rewrite = StubUtility.createImportRewrite(fInvocationContext.getDocument(), fCompilationUnit, true); fImportContext = new ContextSensitiveImportRewriteContext(fCompilationUnit, fInvocationContext.getInvocationOffset(), rewrite); return rewrite; } return null; } // private CompilationUnit getASTRoot(ICompilationUnit compilationUnit) { // return SharedASTProvider.getAST(compilationUnit, SharedASTProvider.WAIT_NO, new NullProgressMonitor()); // } /* * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#apply(org.eclipse.jface.text.IDocument, char, int) */ @Override public void apply(Document document, char trigger, int offset) { try { boolean insertClosingParenthesis = trigger == '(' && autocloseBrackets(); if (insertClosingParenthesis) { StringBuffer replacement = new StringBuffer(getReplacementString()); updateReplacementWithParentheses(replacement); setReplacementString(replacement.toString()); trigger = '\0'; } super.apply(document, trigger, offset); if (fImportRewrite != null && fImportRewrite.hasRecordedChanges()) { int oldLen = document.getLength(); fImportRewrite.rewriteImports().apply(document, TextEdit.UPDATE_REGIONS); setReplacementOffset(getReplacementOffset() + document.getLength() - oldLen); } if (insertClosingParenthesis) setUpLinkedMode(document, ')'); rememberSelection(); } catch (BadLocationException e) { // TODO Log exception e.printStackTrace();//NOSONAR } } protected void updateReplacementWithParentheses(StringBuffer replacement) { FormatterPrefs prefs = getFormatterPrefs(); if (prefs.beforeOpeningParen) replacement.append(SPACE); replacement.append(LPAREN); if (prefs.afterOpeningParen) replacement.append(SPACE); setCursorPosition(replacement.length()); if (prefs.afterOpeningParen) replacement.append(SPACE); replacement.append(RPAREN); } /** * Remembers the selection in the content assist history. * * @throws JavaModelException * if anything goes wrong * @since 3.2 */ protected final void rememberSelection() { IType lhs = fInvocationContext.getExpectedType(); IType rhs = (IType)getJavaElement(); if (lhs != null && rhs != null) WorkerMessageHandler.get().getContentAssistHistory().remember(lhs, rhs); QualifiedTypeNameHistory.remember(getQualifiedTypeName()); } /** * Returns <code>true</code> if imports may be added. The return value depends on the context and preferences only and does not * take into account the contents of the compilation unit or the kind of proposal. Even if <code>true</code> is returned, there * may be cases where no imports are added for the proposal. For example: * <ul> * <li>when completing within the import section</li> * <li>when completing informal javadoc references (e.g. within <code><code></code> tags)</li> * <li>when completing a type that conflicts with an existing import</li> * <li>when completing an implicitly imported type (same package, <code>java.lang</code> types)</li> * </ul> * <p> * The decision whether a qualified type or the simple type name should be inserted must take into account these different * scenarios. * </p> * <p> * Subclasses may extend. * </p> * * @return <code>true</code> if imports may be added, <code>false</code> if not */ protected boolean allowAddingImports() { if (isInJavadoc()) { // TODO fix // if (!fContext.isInJavadocFormalReference()) // return false; if (fProposal.getKind() == CompletionProposal.TYPE_REF && fInvocationContext.getCoreContext().isInJavadocText()) return false; if (!isJavadocProcessingEnabled()) return false; } // TODO // IPreferenceStore preferenceStore= JavaPlugin.getDefault().getPreferenceStore(); // return preferenceStore.getBoolean(PreferenceConstants.CODEASSIST_ADDIMPORT); return true; } private boolean isJavadocProcessingEnabled() { // IJavaProject project= fCompilationUnit.getJavaProject(); boolean processJavadoc; // if (project == null) processJavadoc = JavaCore.ENABLED.equals(JavaCore.getOption(JavaCore.COMPILER_DOC_COMMENT_SUPPORT)); // else // processJavadoc= JavaCore.ENABLED.equals(project.getOption(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, true)); return processJavadoc; } /* * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#isValidPrefix(java.lang.String) */ protected boolean isValidPrefix(String prefix) { return isPrefix(prefix, getSimpleTypeName()) || isPrefix(prefix, getQualifiedTypeName()); } /* * @see org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposal#getCompletionText() */ @Override public CharSequence getPrefixCompletionText(Document document, int completionOffset) { String prefix = getPrefix(document, completionOffset); String completion; // return the qualified name if the prefix is already qualified if (prefix.indexOf('.') != -1) completion = getQualifiedTypeName(); else completion = getSimpleTypeName(); if (isCamelCaseMatching()) return getCamelCaseCompound(prefix, completion); return completion; } /* * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeTriggerCharacters() */ @Override protected char[] computeTriggerCharacters() { return isInJavadoc() ? JDOC_TYPE_TRIGGERS : TYPE_TRIGGERS; } /* * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeProposalInfo() */ @Override protected ProposalInfo computeProposalInfo() { // TODO // if (fCompilationUnit != null) { // IJavaProject project= fCompilationUnit.getJavaProject(); // if (project != null) return new TypeProposalInfo(fProposal, fInvocationContext.getProjectId(), fInvocationContext.getDocContext(), fInvocationContext.getVfsId()); // } // return super.computeProposalInfo(); } /* * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeSortString() */ @Override protected String computeSortString() { // try fast sort string to avoid display string creation return getSimpleTypeName() + Character.MIN_VALUE + getQualifiedTypeName(); } /* * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeRelevance() */ @Override protected int computeRelevance() { /* * There are two histories: the RHS history remembers types used for the current expected type (left hand side), while the * type history remembers recently used types in general). The presence of an RHS ranking is a much more precise sign for * relevance as it proves the subtype relationship between the proposed type and the expected type. The "recently used" * factor (of either the RHS or general history) is less important, it should not override other relevance factors such as * if the type is already imported etc. */ float rhsHistoryRank = fInvocationContext.getHistoryRelevance(getQualifiedTypeName()); float typeHistoryRank = QualifiedTypeNameHistory.getDefault().getNormalizedPosition(getQualifiedTypeName()); int recencyBoost = Math.round((rhsHistoryRank + typeHistoryRank) * 5); int rhsBoost = rhsHistoryRank > 0.0f ? 50 : 0; int baseRelevance = super.computeRelevance(); return baseRelevance + rhsBoost + recencyBoost; } // /** // * @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeContextInformation() // * @since 3.3 // */ // @Override // protected ContextInformation computeContextInformation() // { // char[] signature = fProposal.getSignature(); // char[][] typeParameters = Signature.getTypeArguments(signature); // if (typeParameters.length == 0) // return super.computeContextInformation(); // // ProposalContextInformation contextInformation = new ProposalContextInformation(fProposal); // if (fContextInformationPosition != 0 && fProposal.getCompletion().length == 0) // contextInformation.setContextInformationPosition(fContextInformationPosition); // return contextInformation; // // } }