blob: 412b641899209829edad44ca98209c2f912b211d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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.jdt.internal.ui.text.java;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationExtension;
import org.eclipse.jface.text.link.LinkedModeModel;
import org.eclipse.jface.text.link.LinkedModeUI;
import org.eclipse.jface.text.link.LinkedPosition;
import org.eclipse.jface.text.link.LinkedPositionGroup;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
import org.eclipse.jdt.core.CompletionProposal;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTRequestor;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.internal.corext.template.java.SignatureUtil;
import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.javaeditor.EditorHighlightingSynchronizer;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
/**
* An experimental proposal.
*/
public final class GenericJavaTypeProposal extends LazyJavaTypeCompletionProposal {
/** Triggers for types. Do not modify. */
private final static char[] GENERIC_TYPE_TRIGGERS= new char[] { '.', '\t', '[', '(', '<', ' ' };
/**
* Short-lived context information object for generic types. Currently, these
* are only created after inserting a type proposal, as core doesn't give us
* the correct type proposal from within SomeType<|>.
*/
private static class ContextInformation implements IContextInformation, IContextInformationExtension {
private final String fInformationDisplayString;
private final String fContextDisplayString;
private final Image fImage;
private final int fPosition;
ContextInformation(GenericJavaTypeProposal proposal) {
// don't cache the proposal as content assistant
// might hang on to the context info
fContextDisplayString= proposal.getDisplayString();
fInformationDisplayString= computeContextString(proposal);
fImage= proposal.getImage();
fPosition= proposal.getReplacementOffset() + proposal.getReplacementString().indexOf('<') + 1;
}
/*
* @see org.eclipse.jface.text.contentassist.IContextInformation#getContextDisplayString()
*/
public String getContextDisplayString() {
return fContextDisplayString;
}
/*
* @see org.eclipse.jface.text.contentassist.IContextInformation#getImage()
*/
public Image getImage() {
return fImage;
}
/*
* @see org.eclipse.jface.text.contentassist.IContextInformation#getInformationDisplayString()
*/
public String getInformationDisplayString() {
return fInformationDisplayString;
}
private String computeContextString(GenericJavaTypeProposal proposal) {
try {
TypeArgumentProposal[] proposals= proposal.computeTypeArgumentProposals();
if (proposals.length == 0)
return null;
StringBuffer buf= new StringBuffer();
for (int i= 0; i < proposals.length; i++) {
buf.append(proposals[i].getDisplayName());
if (i < proposals.length - 1)
buf.append(", "); //$NON-NLS-1$
}
return buf.toString();
} catch (JavaModelException e) {
return null;
}
}
/*
* @see org.eclipse.jface.text.contentassist.IContextInformationExtension#getContextInformationPosition()
*/
public int getContextInformationPosition() {
return fPosition;
}
/*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (obj instanceof ContextInformation) {
ContextInformation ci= (ContextInformation) obj;
return getContextInformationPosition() == ci.getContextInformationPosition() && getInformationDisplayString().equals(ci.getInformationDisplayString());
}
return false;
}
}
private static final class TypeArgumentProposal {
private final boolean fIsAmbiguous;
private final String fProposal;
private final String fTypeDisplayName;
TypeArgumentProposal(String proposal, boolean ambiguous, String typeDisplayName) {
fIsAmbiguous= ambiguous;
fProposal= proposal;
fTypeDisplayName= typeDisplayName;
}
public String getDisplayName() {
return fTypeDisplayName;
}
boolean isAmbiguous() {
return fIsAmbiguous;
}
String getProposals() {
return fProposal;
}
public String toString() {
return fProposal;
}
}
private IRegion fSelectedRegion; // initialized by apply()
private TypeArgumentProposal[] fTypeArgumentProposals;
public GenericJavaTypeProposal(CompletionProposal typeProposal, JavaContentAssistInvocationContext context) {
super(typeProposal, context);
}
/*
* @see ICompletionProposalExtension#apply(IDocument, char)
*/
public void apply(IDocument document, char trigger, int offset) {
if (shouldAppendArguments(document, offset, trigger)) {
try {
TypeArgumentProposal[] typeArgumentProposals= computeTypeArgumentProposals();
if (typeArgumentProposals.length > 0) {
int[] offsets= new int[typeArgumentProposals.length];
int[] lengths= new int[typeArgumentProposals.length];
StringBuffer buffer= createParameterList(typeArgumentProposals, offsets, lengths);
// set the generic type as replacement string
super.setReplacementString(buffer.toString());
// add import & remove package, update replacement offset
super.apply(document, '\0', offset);
if (getTextViewer() != null) {
if (hasAmbiguousProposals(typeArgumentProposals)) {
adaptOffsets(offsets, buffer);
installLinkedMode(document, offsets, lengths, typeArgumentProposals);
} else {
fSelectedRegion= new Region(getReplacementOffset() + getReplacementString().length(), 0);
}
}
return;
}
} catch (JavaModelException e) {
// log and continue
JavaPlugin.log(e);
}
}
// default is to use the super implementation
// reasons:
// - not a parameterized type,
// - already followed by <type arguments>
// - proposal type does not inherit from expected type
super.apply(document, trigger, offset);
}
/*
* @see org.eclipse.jdt.internal.ui.text.java.LazyJavaTypeCompletionProposal#computeTriggerCharacters()
*/
protected char[] computeTriggerCharacters() {
return GENERIC_TYPE_TRIGGERS;
}
/**
* Adapt the parameter offsets to any modification of the replacement
* string done by <code>apply</code>. For example, applying the proposal
* may add an import instead of inserting the fully qualified name.
* <p>
* This assumes that modifications happen only at the beginning of the
* replacement string and do not touch the type arguments list.
* </p>
*
* @param offsets the offsets to modify
* @param buffer the original replacement string
*/
private void adaptOffsets(int[] offsets, StringBuffer buffer) {
String replacementString= getReplacementString();
int delta= buffer.length() - replacementString.length(); // due to using an import instead of package
for (int i= 0; i < offsets.length; i++) {
offsets[i]-= delta;
}
}
/**
* Computes the type argument proposals for this type proposals. If there is
* an expected type binding that is a super type of the proposed type, the
* wildcard type arguments of the proposed type that can be mapped through
* to type the arguments of the expected type binding are bound accordingly.
* <p>
* For type arguments that cannot be mapped to arguments in the expected
* type, or if there is no expected type, the upper bound of the type
* argument is proposed.
* </p>
* <p>
* The argument proposals have their <code>isAmbiguos</code> flag set to
* <code>false</code> if the argument can be mapped to a non-wildcard type
* argument in the expected type, otherwise the proposal is ambiguous.
* </p>
*
* @return the type argument proposals for the proposed type
* @throws JavaModelException if accessing the java model fails
*/
private TypeArgumentProposal[] computeTypeArgumentProposals() throws JavaModelException {
if (fTypeArgumentProposals == null) {
IType type= (IType) getJavaElement();
if (type == null)
return new TypeArgumentProposal[0];
ITypeParameter[] parameters= type.getTypeParameters();
if (parameters.length == 0)
return new TypeArgumentProposal[0];
TypeArgumentProposal[] arguments= new TypeArgumentProposal[parameters.length];
ITypeBinding expectedTypeBinding= getExpectedType();
if (expectedTypeBinding != null && expectedTypeBinding.isParameterizedType()) {
// in this case, the type arguments we propose need to be compatible
// with the corresponding type parameters to declared type
IType expectedType= (IType) expectedTypeBinding.getJavaElement();
IType[] path= computeInheritancePath(type, expectedType);
if (path == null)
// proposed type does not inherit from expected type
// the user might be looking for an inner type of proposed type
// to instantiate -> do not add any type arguments
return new TypeArgumentProposal[0];
int[] indices= new int[parameters.length];
for (int paramIdx= 0; paramIdx < parameters.length; paramIdx++) {
indices[paramIdx]= mapTypeParameterIndex(path, path.length - 1, paramIdx);
}
// for type arguments that are mapped through to the expected type's
// parameters, take the arguments of the expected type
ITypeBinding[] typeArguments= expectedTypeBinding.getTypeArguments();
for (int paramIdx= 0; paramIdx < parameters.length; paramIdx++) {
if (indices[paramIdx] != -1) {
// type argument is mapped through
ITypeBinding binding= typeArguments[indices[paramIdx]];
arguments[paramIdx]= computeTypeProposal(binding, parameters[paramIdx]);
}
}
}
// for type arguments that are not mapped through to the expected type,
// take the lower bound of the type parameter
for (int i= 0; i < arguments.length; i++) {
if (arguments[i] == null) {
arguments[i]= computeTypeProposal(parameters[i]);
}
}
fTypeArgumentProposals= arguments;
}
return fTypeArgumentProposals;
}
/**
* Returns a type argument proposal for a given type parameter. The proposal is:
* <ul>
* <li>the type bound for type parameters with a single bound</li>
* <li>the type parameter name for all other (unbounded or more than one bound) type parameters</li>
* </ul>
* Type argument proposals for type parameters are always ambiguous.
*
* @param parameter the type parameter of the inserted type
* @return a type argument proposal for <code>parameter</code>
* @throws JavaModelException
*/
private TypeArgumentProposal computeTypeProposal(ITypeParameter parameter) throws JavaModelException {
String[] bounds= parameter.getBounds();
String elementName= parameter.getElementName();
String displayName= computeTypeParameterDisplayName(parameter, bounds);
if (bounds.length == 1 && !"java.lang.Object".equals(bounds[0])) //$NON-NLS-1$
return new TypeArgumentProposal(Signature.getSimpleName(bounds[0]), true, displayName);
else
return new TypeArgumentProposal(elementName, true, displayName);
}
private String computeTypeParameterDisplayName(ITypeParameter parameter, String[] bounds) {
if (bounds.length == 0 || bounds.length == 1 && "java.lang.Object".equals(bounds[0])) //$NON-NLS-1$
return parameter.getElementName();
StringBuffer buf= new StringBuffer(parameter.getElementName());
buf.append(" extends "); //$NON-NLS-1$
for (int i= 0; i < bounds.length; i++) {
buf.append(Signature.getSimpleName(bounds[i]));
if (i < bounds.length - 1)
buf.append(" & "); //$NON-NLS-1$
}
return buf.toString();
}
/**
* Returns a type argument proposal for a given type binding. The proposal is:
* <ul>
* <li>the simple type name for normal types or type variables (unambigous proposal)</li>
* <li>for wildcard types (ambigous proposals):
* <ul>
* <li>the upper bound for wildcards with an upper bound</li>
* <li>the {@linkplain #computeTypeProposal(ITypeParameter) parameter proposal} for unbounded
* wildcards or wildcards with a lower bound</li>
* </ul>
* </li>
* </ul>
*
* @param binding the type argument binding in the expected type
* @param parameter the type parameter of the inserted type
* @return a type argument proposal for <code>binding</code>
* @throws JavaModelException
* @see #computeTypeProposal(ITypeParameter)
*/
private TypeArgumentProposal computeTypeProposal(ITypeBinding binding, ITypeParameter parameter) throws JavaModelException {
final String name= binding.getName();
if (binding.isWildcardType()) {
if (binding.isUpperbound()) {
// replace the wildcard ? with the type parameter name to get "E extends Bound" instead of "? extends Bound"
String contextName= name.replaceFirst("\\?", parameter.getElementName()); //$NON-NLS-1$
// upper bound - the upper bound is the bound itself
return new TypeArgumentProposal(binding.getBound().getName(), true, contextName);
}
// no or upper bound - use the type parameter of the inserted type, as it may be more
// restrictive (eg. List<?> list= new SerializableList<Serializable>())
return computeTypeProposal(parameter);
}
// not a wildcard but a type or type variable - this is unambigously the right thing to insert
return new TypeArgumentProposal(name, false, name);
}
/**
* Computes one inheritance path from <code>superType</code> to
* <code>subType</code> or <code>null</code> if <code>subType</code>
* does not inherit from <code>superType</code>. Note that there may be
* more than one inheritance path - this method simply returns one.
* <p>
* The returned array contains <code>superType</code> at its first index,
* and <code>subType</code> at its last index. If <code>subType</code>
* equals <code>superType</code>, an array of length 1 is returned
* containing that type.
* </p>
*
* @param subType the sub type
* @param superType the super type
* @return an inheritance path from <code>superType</code> to
* <code>subType</code>, or <code>null</code> if
* <code>subType</code> does not inherit from
* <code>superType</code>
* @throws JavaModelException
*/
private IType[] computeInheritancePath(IType subType, IType superType) throws JavaModelException {
if (superType == null)
return null;
// optimization: avoid building the type hierarchy for the identity case
if (superType.equals(subType))
return new IType[] { subType };
ITypeHierarchy hierarchy= subType.newSupertypeHierarchy(getProgressMonitor());
if (!hierarchy.contains(superType))
return null; // no path
List path= new LinkedList();
path.add(superType);
do {
// any sub type must be on a hierarchy chain from superType to subType
superType= hierarchy.getSubtypes(superType)[0];
path.add(superType);
} while (!superType.equals(subType)); // since the equality case is handled above, we can spare one check
return (IType[]) path.toArray(new IType[path.size()]);
}
private NullProgressMonitor getProgressMonitor() {
return new NullProgressMonitor();
}
/**
* For the type parameter at <code>paramIndex</code> in the type at
* <code>path[pathIndex]</code>, this method computes the corresponding
* type parameter index in the type at <code>path[0]</code>. If the type
* parameter does not map to a type parameter of the super type,
* <code>-1</code> is returned.
*
* @param path the type inheritance path, a non-empty array of consecutive
* sub types
* @param pathIndex an index into <code>path</code> specifying the type to
* start with
* @param paramIndex the index of the type parameter to map -
* <code>path[pathIndex]</code> must have a type parameter at that
* index, lest an <code>ArrayIndexOutOfBoundsException</code> is
* thrown
* @return the index of the type parameter in <code>path[0]</code>
* corresponding to the type parameter at <code>paramIndex</code>
* in <code>path[pathIndex]</code>, or -1 if there is no
* corresponding type parameter
* @throws JavaModelException
* @throws ArrayIndexOutOfBoundsException if <code>path[pathIndex]</code>
* has &lt;= <code>paramIndex</code> parameters
*/
private int mapTypeParameterIndex(IType[] path, int pathIndex, int paramIndex) throws JavaModelException, ArrayIndexOutOfBoundsException {
if (pathIndex == 0)
// break condition: we've reached the top of the hierarchy
return paramIndex;
IType subType= path[pathIndex];
IType superType= path[pathIndex - 1];
String superSignature= findMatchingSuperTypeSignature(subType, superType);
ITypeParameter param= subType.getTypeParameters()[paramIndex];
int index= findMatchingTypeArgumentIndex(superSignature, param.getElementName());
if (index == -1) {
// not mapped through
return -1;
}
return mapTypeParameterIndex(path, pathIndex - 1, index);
}
/**
* Finds and returns the super type signature in the
* <code>extends</code> or <code>implements</code> clause of
* <code>subType</code> that corresponds to <code>superType</code>.
*
* @param subType a direct and true sub type of <code>superType</code>
* @param superType a direct super type (super class or interface) of
* <code>subType</code>
* @return the super type signature of <code>subType</code> referring
* to <code>superType</code>
* @throws JavaModelException if extracting the super type signatures
* fails, or if <code>subType</code> contains no super type
* signature to <code>superType</code>
*/
private String findMatchingSuperTypeSignature(IType subType, IType superType) throws JavaModelException {
String[] signatures= getSuperTypeSignatures(subType, superType);
for (int i= 0; i < signatures.length; i++) {
String signature= signatures[i];
String qualified= SignatureUtil.qualifySignature(signature, subType);
String subFQN= SignatureUtil.stripSignatureToFQN(qualified);
String superFQN= superType.getFullyQualifiedName();
if (subFQN.equals(superFQN)) {
return signature;
}
// TODO handle local types
}
throw new JavaModelException(new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.OK, "Illegal hierarchy", null))); //$NON-NLS-1$
}
/**
* Finds and returns the index of the type argument named
* <code>argument</code> in the given super type signature.
* <p>
* If <code>signature</code> does not contain a corresponding type
* argument, or if <code>signature</code> has no type parameters (i.e. is
* a reference to a non-parameterized type or a raw type), -1 is returned.
* </p>
*
* @param signature the super type signature from a type's
* <code>extends</code> or <code>implements</code> clause
* @param argument the name of the type argument to find
* @return the index of the given type argument, or -1 if there is none
*/
private int findMatchingTypeArgumentIndex(String signature, String argument) {
String[] typeArguments= Signature.getTypeArguments(signature);
for (int i= 0; i < typeArguments.length; i++) {
if (Signature.getSignatureSimpleName(typeArguments[i]).equals(argument))
return i;
}
return -1;
}
/**
* Returns the super interface signatures of <code>subType</code> if
* <code>superType</code> is an interface, otherwise returns the super
* type signature.
*
* @param subType the sub type signature
* @param superType the super type signature
* @return the super type signatures of <code>subType</code>
* @throws JavaModelException if any java model operation fails
*/
private String[] getSuperTypeSignatures(IType subType, IType superType) throws JavaModelException {
if (superType.isInterface())
return subType.getSuperInterfaceTypeSignatures();
else
return new String[] {subType.getSuperclassTypeSignature()};
}
/**
* Returns the type binding of the expected type as it is contained in the
* code completion context.
*
* @return the binding of the expected type
*/
private ITypeBinding getExpectedType() {
char[][] chKeys= fInvocationContext.getCoreContext().getExpectedTypesKeys();
if (chKeys == null || chKeys.length == 0)
return null;
String[] keys= new String[chKeys.length];
for (int i= 0; i < keys.length; i++) {
keys[i]= String.valueOf(chKeys[0]);
}
final ASTParser parser= ASTParser.newParser(AST.JLS3);
parser.setProject(fCompilationUnit.getJavaProject());
parser.setResolveBindings(true);
final Map bindings= new HashMap();
ASTRequestor requestor= new ASTRequestor() {
public void acceptBinding(String bindingKey, IBinding binding) {
bindings.put(bindingKey, binding);
}
};
parser.createASTs(new ICompilationUnit[0], keys, requestor, null);
if (bindings.size() > 0)
return (ITypeBinding) bindings.get(keys[0]);
return null;
}
/**
* Returns <code>true</code> if type arguments should be appended when
* applying this proposal, <code>false</code> if not (for example if the
* document already contains a type argument list after the insertion point.
*
* @param document the document
* @param offset the insertion offset
* @param trigger the trigger character
* @return <code>true</code> if arguments should be appended
*/
private boolean shouldAppendArguments(IDocument document, int offset, char trigger) {
if (trigger != '\0' && trigger != '<')
return false;
try {
IRegion region= document.getLineInformationOfOffset(offset);
String line= document.get(region.getOffset(), region.getLength());
int index= offset - region.getOffset();
while (index != line.length() && Character.isUnicodeIdentifierPart(line.charAt(index)))
++index;
if (index == line.length())
return true;
char ch= line.charAt(index);
return ch != '<';
} catch (BadLocationException e) {
return true;
}
}
private StringBuffer createParameterList(TypeArgumentProposal[] typeArguments, int[] offsets, int[] lengths) {
StringBuffer buffer= new StringBuffer();
buffer.append(getReplacementString());
FormatterPrefs prefs= getFormatterPrefs();
final char LESS= '<';
final char GREATER= '>';
if (prefs.beforeOpeningBracket)
buffer.append(SPACE);
buffer.append(LESS);
if (prefs.afterOpeningBracket)
buffer.append(SPACE);
StringBuffer separator= new StringBuffer(3);
if (prefs.beforeTypeArgumentComma)
separator.append(SPACE);
separator.append(COMMA);
if (prefs.afterTypeArgumentComma)
separator.append(SPACE);
for (int i= 0; i != typeArguments.length; i++) {
if (i != 0)
buffer.append(separator);
offsets[i]= buffer.length();
buffer.append(typeArguments[i]);
lengths[i]= buffer.length() - offsets[i];
}
if (prefs.beforeClosingBracket)
buffer.append(SPACE);
buffer.append(GREATER);
return buffer;
}
private void installLinkedMode(IDocument document, int[] offsets, int[] lengths, TypeArgumentProposal[] typeArgumentProposals) {
int replacementOffset= getReplacementOffset();
String replacementString= getReplacementString();
try {
LinkedModeModel model= new LinkedModeModel();
for (int i= 0; i != offsets.length; i++) {
if (typeArgumentProposals[i].isAmbiguous()) {
LinkedPositionGroup group= new LinkedPositionGroup();
group.addPosition(new LinkedPosition(document, replacementOffset + offsets[i], lengths[i], LinkedPositionGroup.NO_STOP));
model.addGroup(group);
}
}
model.forceInstall();
JavaEditor editor= getJavaEditor();
if (editor != null) {
model.addLinkingListener(new EditorHighlightingSynchronizer(editor));
}
LinkedModeUI ui= new EditorLinkedModeUI(model, getTextViewer());
ui.setExitPolicy(new ExitPolicy('>', document));
ui.setExitPosition(getTextViewer(), replacementOffset + replacementString.length(), 0, Integer.MAX_VALUE);
ui.setDoContextInfo(true);
ui.enter();
fSelectedRegion= ui.getSelectedRegion();
} catch (BadLocationException e) {
JavaPlugin.log(e);
openErrorDialog(e);
}
}
private boolean hasAmbiguousProposals(TypeArgumentProposal[] typeArgumentProposals) {
boolean hasAmbiguousProposals= false;
for (int i= 0; i < typeArgumentProposals.length; i++) {
if (typeArgumentProposals[i].isAmbiguous()) {
hasAmbiguousProposals= true;
break;
}
}
return hasAmbiguousProposals;
}
/**
* Returns the currently active java editor, or <code>null</code> if it
* cannot be determined.
*
* @return the currently active java editor, or <code>null</code>
*/
private JavaEditor getJavaEditor() {
IEditorPart part= JavaPlugin.getActivePage().getActiveEditor();
if (part instanceof JavaEditor)
return (JavaEditor) part;
else
return null;
}
/*
* @see ICompletionProposal#getSelection(IDocument)
*/
public Point getSelection(IDocument document) {
if (fSelectedRegion == null)
return super.getSelection(document);
return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength());
}
private void openErrorDialog(BadLocationException e) {
Shell shell= getTextViewer().getTextWidget().getShell();
MessageDialog.openError(shell, JavaTextMessages.ExperimentalProposal_error_msg, e.getMessage());
}
/*
* @see org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal#computeContextInformation()
*/
protected IContextInformation computeContextInformation() {
// only return information if we're already computed
// -> avoids creating context information for invalid proposals
if (fTypeArgumentProposals != null) {
try {
if (hasParameters()) {
TypeArgumentProposal[] proposals= computeTypeArgumentProposals();
if (hasAmbiguousProposals(proposals))
return new ContextInformation(this);
}
} catch (JavaModelException e) {
}
}
return super.computeContextInformation();
}
protected int computeCursorPosition() {
if (fSelectedRegion != null)
return fSelectedRegion.getOffset() - getReplacementOffset();
return super.computeCursorPosition();
}
private boolean hasParameters() {
try {
IType type= (IType) getJavaElement();
if (type == null)
return false;
return type.getTypeParameters().length > 0;
} catch (JavaModelException e) {
return false;
}
}
}