/*
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.codeassist.processors;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ConstructorNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.PropertyNode;
import org.codehaus.groovy.eclipse.codeassist.GroovyContentAssist;
import org.codehaus.groovy.eclipse.codeassist.ProposalUtils;
import org.codehaus.groovy.eclipse.codeassist.completions.GroovyJavaMethodCompletionProposal;
import org.codehaus.groovy.eclipse.codeassist.proposals.GroovyNamedArgumentProposal;
import org.codehaus.groovy.eclipse.codeassist.proposals.ProposalFormattingOptions;
import org.codehaus.groovy.eclipse.codeassist.relevance.Relevance;
import org.codehaus.groovy.eclipse.codeassist.relevance.RelevanceRules;
import org.codehaus.groovy.eclipse.codeassist.requestor.ContentAssistContext;
import org.codehaus.groovy.eclipse.codeassist.requestor.ContentAssistLocation;
import org.codehaus.groovy.eclipse.codeassist.requestor.MethodInfoContentAssistContext;
import org.codehaus.jdt.groovy.internal.compiler.ast.GroovyCompilationUnitScope;
import org.codehaus.jdt.groovy.internal.compiler.ast.JDTResolver;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.CompletionProposal;
import org.eclipse.jdt.core.CompletionRequestor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.groovy.core.util.ReflectionUtils;
import org.eclipse.jdt.internal.codeassist.CompletionEngine;
import org.eclipse.jdt.internal.codeassist.ISearchRequestor;
import org.eclipse.jdt.internal.codeassist.RelevanceConstants;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.lookup.ImportBinding;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
import org.eclipse.jdt.internal.compiler.util.ObjectVector;
import org.eclipse.jdt.internal.core.NameLookup;
import org.eclipse.jdt.internal.corext.util.TypeFilter;
import org.eclipse.jdt.internal.ui.text.java.JavaTypeCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.LazyGenericTypeProposal;
import org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.LazyJavaTypeCompletionProposal;
import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
/**
* This type requestor searches for groovy type content assist proposals in the current
* scope. This class is largely copied from {@link CompletionEngine}. Method
* names used here are the same as the method names used in the original code
* Method parts are omitted or commented out when they are not relevant for
* or not supported by groovy completion.
*/
public class GroovyProposalTypeSearchRequestor implements ISearchRequestor, RelevanceConstants {
private static final char[] NO_TYPE_NAME = {'.'};
private static final int CHECK_CANCEL_FREQUENCY = 50;
private int foundTypesCount = 0;
private int foundConstructorsCount = 0;
private final IProgressMonitor monitor;
private ObjectVector acceptedTypes;
private Set<String> acceptedPackages;
private boolean shouldAcceptConstructors;
private ObjectVector acceptedConstructors;
private final int offset;
private final int replaceLength;
private final int actualCompletionPosition;
/** Array of simple name, fully-qualified name pairs. Default imports should be included (aka BigDecimal, etc.). */
private char[][][] imports;
/** Array of fully-qualified names. Default imports should be included (aka java.lang, groovy.lang, etc.). */
private char[][] onDemandimports;
private final NameLookup nameLookup;
private final String completionExpression;
private final ModuleNode module;
private final GroovyCompilationUnit unit;
private GroovyImportRewriteFactory groovyRewriter;
private ProposalFormattingOptions groovyProposalPrefs;
private final JavaContentAssistInvocationContext javaContext;
// use this completion engine only to create parameter names for Constructors
private CompletionEngine mockEngine;
// all the types in the target Compilation unit
private IType[] allTypesInUnit;
// instead of inserting text, show context information only for constructors
private boolean contextOnly;
private final boolean isImport;
private final ContentAssistContext context;
public GroovyProposalTypeSearchRequestor(ContentAssistContext context,
JavaContentAssistInvocationContext javaContext, int exprStart,
int replaceLength, NameLookup nameLookup, IProgressMonitor monitor) {
this.context = context;
this.offset = exprStart;
this.javaContext = javaContext;
this.module = context.unit.getModuleNode();
this.unit = context.unit;
this.replaceLength = replaceLength;
this.actualCompletionPosition = context.completionLocation;
this.monitor = monitor;
this.acceptedTypes = new ObjectVector();
this.nameLookup = nameLookup;
this.isImport = context.location == ContentAssistLocation.IMPORT;
this.shouldAcceptConstructors = context.location == ContentAssistLocation.CONSTRUCTOR
|| context.location == ContentAssistLocation.METHOD_CONTEXT;
// if contextOnly, then do not insert any text, only show context
// information
this.contextOnly = context.location == ContentAssistLocation.METHOD_CONTEXT;
this.completionExpression = context.location == ContentAssistLocation.METHOD_CONTEXT
? ((MethodInfoContentAssistContext) context).methodName : context.completionExpression;
groovyRewriter = new GroovyImportRewriteFactory(this.unit, this.module);
try {
allTypesInUnit = unit.getAllTypes();
} catch (JavaModelException e) {
GroovyContentAssist.logError("Problem with type completion", e);
allTypesInUnit = new IType[0];
}
}
public void acceptPackage(char[] packageName) {
checkCancel();
if (TypeFilter.isFiltered(packageName, NO_TYPE_NAME)) {
return;
}
if (acceptedPackages == null)
acceptedPackages = new HashSet<String>();
acceptedPackages.add(String.valueOf(packageName));
}
public void acceptConstructor(int modifiers, char[] simpleTypeName,
int parameterCount, char[] signature, char[][] parameterTypes,
char[][] parameterNames, int typeModifiers, char[] packageName,
int extraFlags, String path, AccessRestriction accessRestriction) {
if (shouldAcceptConstructors) {
// do not check cancellation for every types to avoid performance loss
if ((this.foundConstructorsCount++ % (CHECK_CANCEL_FREQUENCY)) == 0)
checkCancel();
// do not propose enum constructors
if ((typeModifiers & ClassFileConstants.AccEnum) != 0)
return;
if (TypeFilter.isFiltered(packageName, simpleTypeName)) {
return;
}
int accessibility = IAccessRule.K_ACCESSIBLE;
if (accessRestriction != null) {
switch (accessRestriction.getProblemId()) {
case IProblem.ForbiddenReference:
// forbidden references are removed
return;
case IProblem.DiscouragedReference:
// discouraged references have lower priority
accessibility = IAccessRule.K_DISCOURAGED;
}
}
if (signature == null) {
// signature = Signature.createArraySignature(typeSignature, arrayCount)
}
if (this.acceptedConstructors == null)
this.acceptedConstructors = new ObjectVector();
this.acceptedConstructors.add(new AcceptedCtor(modifiers, simpleTypeName, parameterCount,
signature, parameterTypes, parameterNames, typeModifiers, packageName, extraFlags, accessibility));
}
}
public void acceptType(char[] packageName, char[] simpleTypeName,
char[][] enclosingTypeNames, int modifiers, AccessRestriction accessRestriction) {
// do not check cancellation for every types to avoid performance loss
if ((this.foundTypesCount++ % CHECK_CANCEL_FREQUENCY) == 0)
checkCancel();
// do not propose synthetic types
if (CharOperation.contains('$', simpleTypeName))
return;
if (TypeFilter.isFiltered(packageName, simpleTypeName)) {
return;
}
int accessibility = IAccessRule.K_ACCESSIBLE;
if (accessRestriction != null) {
switch (accessRestriction.getProblemId()) {
case IProblem.ForbiddenReference:
// forbidden references are removed
return;
case IProblem.DiscouragedReference:
// discouraged references have a lower priority
accessibility = IAccessRule.K_DISCOURAGED;
}
}
if (this.acceptedTypes == null)
this.acceptedTypes = new ObjectVector();
this.acceptedTypes.add(new AcceptedType(packageName, simpleTypeName, enclosingTypeNames, modifiers, accessibility));
}
private void checkCancel() {
if (this.monitor != null && this.monitor.isCanceled()) {
throw new OperationCanceledException();
}
}
/**
* This method is called after all types have been accepted by
* this requestor. Converts each type into an {@link ICompletionProposal}
* @return list of all {@link ICompletionProposal}s applicable for this
* content assist request.
*/
List<ICompletionProposal> processAcceptedTypes(JDTResolver resolver) {
this.checkCancel();
if (this.acceptedTypes == null)
return Collections.EMPTY_LIST;
int length = this.acceptedTypes.size();
if (length == 0)
return Collections.EMPTY_LIST;
HashtableOfObject onDemandFound = new HashtableOfObject();
String thisPackageName = module.getPackageName() == null ? "" : module.getPackageName();
List<ICompletionProposal> proposals = new LinkedList<ICompletionProposal>();
try {
next: for (int i = 0; i < length; i += 1) {
// does not check cancellation for every types to avoid
// performance loss
if ((i % CHECK_CANCEL_FREQUENCY) == 0) {
checkCancel();
}
AcceptedType acceptedType = (AcceptedType) this.acceptedTypes.elementAt(i);
char[] packageName = acceptedType.packageName;
char[] simpleTypeName = acceptedType.simpleTypeName;
char[][] enclosingTypeNames = acceptedType.enclosingTypeNames;
int modifiers = acceptedType.modifiers;
int accessibility = acceptedType.accessibility;
char[] typeName;
char[] flatEnclosingTypeNames;
if (enclosingTypeNames == null
|| enclosingTypeNames.length == 0) {
flatEnclosingTypeNames = null;
typeName = simpleTypeName;
} else {
flatEnclosingTypeNames = CharOperation.concatWith(acceptedType.enclosingTypeNames, '.');
typeName = CharOperation.concat(flatEnclosingTypeNames, simpleTypeName, '.');
}
char[] fullyQualifiedName = CharOperation.concat(packageName, typeName, '.');
// get this imports from the module node
if (imports == null && resolver.getScope() != null) {
initializeImportArrays(resolver.getScope());
}
// check to see if this type is imported explicitly
for (int j = 0; j < imports.length; j++) {
char[][] importName = imports[j];
if (CharOperation.equals(typeName, importName[0])) {
// potentially use fully qualified type name
// if there is already something else with the same
// simple
// name imported
proposals.add(proposeType(packageName, simpleTypeName, modifiers, accessibility,
typeName, fullyQualifiedName, !CharOperation.equals(fullyQualifiedName, importName[1])));
continue next;
}
}
if ((enclosingTypeNames == null || enclosingTypeNames.length == 0)
&& CharOperation.equals(thisPackageName
.toCharArray(), packageName)) {
proposals.add(proposeType(packageName, simpleTypeName,
modifiers, accessibility, typeName,
fullyQualifiedName, false));
continue next;
} else {
char[] fullyQualifiedEnclosingTypeOrPackageName = null;
if (((AcceptedType) onDemandFound
.get(simpleTypeName)) == null) {
for (int j = 0; j < this.onDemandimports.length; j++) {
char[] importFlatName = onDemandimports[j];
if (fullyQualifiedEnclosingTypeOrPackageName == null) {
if (enclosingTypeNames != null
&& enclosingTypeNames.length != 0) {
fullyQualifiedEnclosingTypeOrPackageName = CharOperation
.concat(packageName,
flatEnclosingTypeNames, '.');
} else {
fullyQualifiedEnclosingTypeOrPackageName = packageName;
}
}
if (CharOperation.equals(
fullyQualifiedEnclosingTypeOrPackageName,
importFlatName)) {
acceptedType.qualifiedTypeName = typeName;
acceptedType.fullyQualifiedName = fullyQualifiedName;
onDemandFound.put(simpleTypeName,
acceptedType);
continue next;
}
}
proposals.add(proposeType(fullyQualifiedEnclosingTypeOrPackageName != null ? fullyQualifiedEnclosingTypeOrPackageName : packageName,
simpleTypeName, modifiers, accessibility,
typeName, fullyQualifiedName, true));
}
}
}
char[][] keys = onDemandFound.keyTable;
Object[] values = onDemandFound.valueTable;
int max = keys.length;
for (int i = 0; i < max; i++) {
if ((i % CHECK_CANCEL_FREQUENCY) == 0)
checkCancel();
if (keys[i] != null) {
AcceptedType value = (AcceptedType) values[i];
if (value != null) {
proposals.add(proposeType(value.packageName,
value.simpleTypeName, value.modifiers,
value.accessibility,
value.qualifiedTypeName,
value.fullyQualifiedName,
value.mustBeQualified));
}
}
}
} finally {
this.acceptedTypes = null; // reset
}
return proposals;
}
private ICompletionProposal proposeNoImportType(char[] packageName,
char[] simpleTypeName, int modifiers, int accessibility,
char[] qualifiedTypeName, char[] fullyQualifiedName,
boolean isQualified) {
char[] completionName;
if (isQualified) {
completionName = fullyQualifiedName;
} else {
completionName = simpleTypeName;
}
GroovyCompletionProposal proposal = createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition - this.offset);
proposal.setDeclarationSignature(packageName);
proposal.setSignature(CompletionEngine.createNonGenericTypeSignature(packageName, simpleTypeName));
proposal.setCompletion(completionName);
proposal.setFlags(modifiers);
proposal.setReplaceRange(this.offset, this.offset + this.replaceLength);
proposal.setTokenRange(this.offset, this.actualCompletionPosition);
proposal.setRelevance(RelevanceRules.ALL_RULES.getRelevance(fullyQualifiedName, allTypesInUnit, accessibility, modifiers));
proposal.setTypeName(simpleTypeName);
proposal.setAccessibility(accessibility);
proposal.setPackageName(packageName);
String completionString = new String(completionName);
JavaTypeCompletionProposal javaCompletionProposal = new JavaTypeCompletionProposal(
completionString, null, this.offset, this.replaceLength,
ProposalUtils.getImage(proposal), ProposalUtils.createDisplayString(proposal),
proposal.getRelevance(), completionString, javaContext);
javaCompletionProposal.setRelevance(proposal.getRelevance());
return javaCompletionProposal;
}
private ICompletionProposal proposeType(char[] packageName,
char[] simpleTypeName, int modifiers, int accessibility,
char[] qualifiedTypeName, char[] fullyQualifiedName,
boolean isQualified) {
return isImport ?
proposeNoImportType(packageName, simpleTypeName, modifiers, accessibility, qualifiedTypeName, fullyQualifiedName, isQualified) :
proposeImportableType(packageName, simpleTypeName, modifiers, accessibility, qualifiedTypeName, fullyQualifiedName, isQualified);
}
private ICompletionProposal proposeImportableType(char[] packageName,
char[] simpleTypeName, int modifiers, int accessibility,
char[] qualifiedTypeName, char[] fullyQualifiedName,
boolean isQualified) {
char[] completionName;
if (isQualified) {
completionName = fullyQualifiedName;
} else {
completionName = simpleTypeName;
}
GroovyCompletionProposal proposal = createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition - this.offset);
proposal.setDeclarationSignature(packageName);
proposal.setSignature(CompletionEngine.createNonGenericTypeSignature(packageName, simpleTypeName));
proposal.setCompletion(completionName);
proposal.setFlags(modifiers);
proposal.setReplaceRange(this.offset, this.offset + this.replaceLength);
proposal.setTokenRange(this.offset, this.actualCompletionPosition);
proposal.setRelevance(RelevanceRules.ALL_RULES.getRelevance(fullyQualifiedName, allTypesInUnit, accessibility, modifiers));
proposal.setNameLookup(nameLookup);
proposal.setTypeName(simpleTypeName);
proposal.setAccessibility(accessibility);
proposal.setPackageName(packageName);
LazyGenericTypeProposal javaCompletionProposal = new LazyGenericTypeProposal(proposal, javaContext);
javaCompletionProposal.setTriggerCharacters(ProposalUtils.TYPE_TRIGGERS);
javaCompletionProposal.setRelevance(proposal.getRelevance());
ImportRewrite r = groovyRewriter.getImportRewrite(monitor);
if (r != null) {
ReflectionUtils.setPrivateField(
LazyJavaTypeCompletionProposal.class, "fImportRewrite",
javaCompletionProposal, r);
}
return javaCompletionProposal;
}
List<ICompletionProposal> processAcceptedPackages() {
this.checkCancel();
List<ICompletionProposal> proposals = new LinkedList<ICompletionProposal>();
if (acceptedPackages != null && acceptedPackages.size() > 0) {
for (String packageNameStr : acceptedPackages) {
char[] packageName = packageNameStr.toCharArray();
GroovyCompletionProposal proposal = createProposal(CompletionProposal.PACKAGE_REF, this.actualCompletionPosition);
proposal.setDeclarationSignature(packageName);
proposal.setPackageName(packageName);
proposal.setCompletion(packageName);
proposal.setReplaceRange(this.offset, this.actualCompletionPosition);
proposal.setTokenRange(this.offset, this.actualCompletionPosition);
proposal.setRelevance(Relevance.LOWEST.getRelavance());
LazyJavaCompletionProposal javaProposal = new LazyJavaCompletionProposal(proposal, javaContext);
proposals.add(javaProposal);
javaProposal.setRelevance(proposal.getRelevance());
}
}
return proposals;
}
List<ICompletionProposal> processAcceptedConstructors(Set<String> usedParams, JDTResolver resolver) {
this.checkCancel();
if (this.acceptedConstructors == null)
return Collections.emptyList();
int length = this.acceptedConstructors.size();
if (length == 0)
return Collections.emptyList();
String currentPackageNameStr = this.module.getPackageName();
char[] currentPackageName;
if (currentPackageNameStr == null) {
currentPackageName = CharOperation.NO_CHAR;
} else {
currentPackageName = currentPackageNameStr.toCharArray();
if (currentPackageName[currentPackageName.length - 1] == '.') {
char[] newPackageName = new char[currentPackageName.length - 1];
System.arraycopy(currentPackageName, 0, newPackageName, 0,
newPackageName.length);
currentPackageName = newPackageName;
}
}
List<ICompletionProposal> proposals = new LinkedList<ICompletionProposal>();
try {
for (int i = 0; i < length; i += 1) {
// does not check cancellation for every types to avoid
// performance loss
if ((i % CHECK_CANCEL_FREQUENCY) == 0)
checkCancel();
AcceptedCtor acceptedConstructor = (AcceptedCtor) this.acceptedConstructors.elementAt(i);
final int typeModifiers = acceptedConstructor.typeModifiers;
if (isInterfaceAnnotationAbstractOrEnum(typeModifiers)) {
continue;
}
final char[] packageName = acceptedConstructor.packageName;
final char[] simpleTypeName = acceptedConstructor.simpleTypeName;
final int modifiers = acceptedConstructor.modifiers;
final int parameterCount = acceptedConstructor.parameterCount;
final char[] signature = acceptedConstructor.signature;
final char[][] parameterTypes = acceptedConstructor.parameterTypes;
final char[][] parameterNames = acceptedConstructor.parameterNames;
final int extraFlags = acceptedConstructor.extraFlags;
final int accessibility = acceptedConstructor.accessibility;
char[] fullyQualifiedName = CharOperation.concat(packageName, simpleTypeName, '.');
if (imports == null && resolver.getScope() != null) {
initializeImportArrays(resolver.getScope());
}
// propose all constructors regardless of package, but ignore
// enums
if (!Flags.isEnum(typeModifiers)) {
ICompletionProposal constructorProposal = proposeConstructor(simpleTypeName, parameterCount, signature,
parameterTypes, parameterNames, modifiers, packageName, typeModifiers, accessibility, simpleTypeName,
fullyQualifiedName, false, extraFlags);
if (constructorProposal != null) {
proposals.add(constructorProposal);
if (contextOnly) {
// also add all of the constructor arguments for
// constructors with no
// args and when it is the only constructor in the
// classs
ClassNode resolved = resolver.resolve(String.valueOf(fullyQualifiedName));
if (resolved != null) {
List<ConstructorNode> constructors = resolved.getDeclaredConstructors();
if (constructors != null && constructors.size() == 1) {
ConstructorNode constructor = constructors.get(0);
Parameter[] parameters = constructor.getParameters();
if (constructor.getStart() <= 0 && (parameters == null || parameters.length == 0)) {
for (PropertyNode prop : resolved.getProperties()) {
if (!prop.getName().equals("metaClass") && !usedParams.contains(prop.getName())) {
GroovyNamedArgumentProposal namedProp = new GroovyNamedArgumentProposal(prop.getName(),
prop.getType(), null, null);
proposals.add(namedProp.createJavaProposal(context, javaContext));
}
}
}
}
}
}
}
}
}
} finally {
this.acceptedTypes = null; // reset
}
return proposals;
}
private boolean isInterfaceAnnotationAbstractOrEnum(int typeModifiers) {
return (typeModifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation)) != 0;
}
private ICompletionProposal proposeConstructor(char[] simpleTypeName,
int parameterCount, char[] signature, char[][] parameterTypes,
char[][] parameterNames, int modifiers, char[] packageName,
int typeModifiers, int accessibility, char[] typeName,
char[] fullyQualifiedName, boolean isQualified, int extraFlags) {
// only show context information and only for methods
// that exactly match the name. This happens when we are at the
// start of an argument or an open paren
String simpleTypeNameStr = String.valueOf(simpleTypeName);
String fullyQualifiedNameStr = String.valueOf(fullyQualifiedName);
if (contextOnly && !completionExpression.equals(simpleTypeNameStr) && !completionExpression.equals(fullyQualifiedNameStr)) {
return null;
}
char[] typeCompletion;
if(isQualified) {
typeCompletion = fullyQualifiedName;
if (packageName == null || packageName.length == 0) {
typeCompletion = simpleTypeName;
}
} else {
typeCompletion = simpleTypeName;
}
float relevanceMultiplier = 1;
relevanceMultiplier += accessibility == IAccessRule.K_ACCESSIBLE ? 2 : -1;
relevanceMultiplier += computeRelevanceForCaseMatching(this.completionExpression.toCharArray(), simpleTypeName);
int augmentedModifiers = modifiers;
if (Flags.isDeprecated(typeModifiers)) {
augmentedModifiers |= Flags.AccDeprecated;
}
if (parameterCount == -1) {
// default constructor
parameterNames = CharOperation.NO_CHAR_CHAR;
parameterTypes = CharOperation.NO_CHAR_CHAR;
} else {
int parameterNamesLength = parameterNames == null ? 0
: parameterNames.length;
if (parameterCount != parameterNamesLength) {
parameterNames = null;
}
}
GroovyCompletionProposal proposal = createProposal(contextOnly ? CompletionProposal.METHOD_REF : CompletionProposal.CONSTRUCTOR_INVOCATION, offset - 1);
char[] declarationSignature = CompletionEngine.createNonGenericTypeSignature(packageName, typeName);
proposal.setDeclarationSignature(declarationSignature);
if (contextOnly) {
proposal.setReplaceRange(actualCompletionPosition, actualCompletionPosition);
proposal.setTokenRange(actualCompletionPosition, actualCompletionPosition);
proposal.setCompletion(CharOperation.NO_CHAR);
} else {
// otherwise this is a normal constructor proposal
proposal.setCompletion(this.completionExpression.toCharArray());
// looks strange, but this is just copying similar code in CompletionEngine.java
proposal.setReplaceRange(this.offset + this.replaceLength, this.offset + this.replaceLength);
proposal.setTokenRange(this.offset, this.actualCompletionPosition);
char[] completion = new char[] {'(', ')'};
try {
// try not to insert an extra set of parenthesis when completing the constructor name
if (this.javaContext.getDocument().getChar(this.actualCompletionPosition) == '(') {
completion = CharOperation.NO_CHAR;
}
} catch (BadLocationException ignored) {
}
proposal.setCompletion(completion);
// provides the import statement
GroovyCompletionProposal typeProposal = createTypeProposal(packageName, typeModifiers, accessibility, typeName,
fullyQualifiedName, isQualified, typeCompletion, augmentedModifiers, declarationSignature);
proposal.setRequiredProposals(new CompletionProposal[] { typeProposal });
}
if (signature == null) {
proposal.setSignature(createConstructorSignature(parameterTypes, isQualified));
} else {
char[] copy = new char[signature.length];
System.arraycopy(signature, 0, copy, 0, copy.length);
CharOperation.replace(copy, '/', '.');
proposal.setSignature(copy);
}
if (parameterNames != null) {
proposal.setParameterNames(parameterNames);
} else {
proposal.setHasNoParameterNamesFromIndex(true);
if (mockEngine == null) {
// used for caching types only
mockEngine = new CompletionEngine(null,
new CompletionRequestor() {
@Override
public void accept(CompletionProposal proposal) {
}
}, null, this.javaContext.getProject(), null, null);
}
proposal.setCompletionEngine(mockEngine);
}
if (parameterTypes == null) {
parameterTypes = new char[parameterCount][];
for (int i = 0; i < parameterCount; i++) {
parameterTypes[i] = "def".toCharArray();
}
}
proposal.setParameterTypeNames(parameterTypes);
proposal.setDeclarationPackageName(packageName);
proposal.setDeclarationTypeName(simpleTypeName);
// proposal.setParameterPackageNames(); not right
proposal.setName(simpleTypeName);
proposal.setIsContructor(true);
proposal.setRelevance(Relevance.MEDIUM_HIGH
.getRelevance(relevanceMultiplier));
proposal.setFlags(augmentedModifiers);
proposal.setTypeName(simpleTypeName);
proposal.setAccessibility(typeModifiers);
proposal.setPackageName(packageName);
LazyJavaCompletionProposal lazyProposal = new GroovyJavaMethodCompletionProposal(proposal, javaContext,
getProposalOptions());
lazyProposal.setRelevance(proposal.getRelevance());
if (proposal.hasParameters()) {
lazyProposal.setTriggerCharacters(ProposalUtils.METHOD_WITH_ARGUMENTS_TRIGGERS);
} else {
lazyProposal.setTriggerCharacters(ProposalUtils.METHOD_TRIGGERS);
}
ImportRewrite r = groovyRewriter.getImportRewrite(monitor);
if (r != null) {
ReflectionUtils.setPrivateField(LazyJavaTypeCompletionProposal.class, "fImportRewrite", lazyProposal, r);
}
if (contextOnly) {
((GroovyJavaMethodCompletionProposal) lazyProposal).contextOnly();
}
return lazyProposal;
}
private GroovyCompletionProposal createTypeProposal(char[] packageName, int typeModifiers,
int accessibility, char[] typeName, char[] fullyQualifiedName, boolean isQualified,
char[] typeCompletion, int augmentedModifiers, char[] declarationSignature) {
GroovyCompletionProposal typeProposal = createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition - 1);
typeProposal.setNameLookup(nameLookup);
typeProposal.setDeclarationSignature(declarationSignature);
typeProposal.setSignature(CompletionEngine.createNonGenericTypeSignature(packageName, typeName));
typeProposal.setPackageName(packageName);
typeProposal.setTypeName(typeName);
typeProposal.setFlags(typeModifiers);
typeProposal.setCompletion(typeCompletion);
typeProposal.setReplaceRange(this.offset, this.offset + this.replaceLength);
typeProposal.setTokenRange(this.offset, this.offset + this.replaceLength);
typeProposal.setRelevance(RelevanceRules.ALL_RULES.getRelevance(fullyQualifiedName, allTypesInUnit, accessibility, augmentedModifiers));
return typeProposal;
}
private ProposalFormattingOptions getProposalOptions() {
if (groovyProposalPrefs == null) {
groovyProposalPrefs = ProposalFormattingOptions.newFromOptions();
}
return groovyProposalPrefs;
}
private char[] createConstructorSignature(char[][] parameterTypes, boolean isQualified) {
char[][] parameterTypeSigs;
if (parameterTypes == null) {
parameterTypeSigs = CharOperation.NO_CHAR_CHAR;
} else {
parameterTypeSigs = new char[parameterTypes.length][];
for (int i = 0; i < parameterTypes.length; i++) {
char[] copy = new char[parameterTypes[i].length];
System.arraycopy(parameterTypes[i], 0, copy, 0, copy.length);
CharOperation.replace(copy, '/', '.');
parameterTypeSigs[i] = Signature.createCharArrayTypeSignature(copy, isQualified);
}
}
return Signature.createMethodSignature(parameterTypeSigs, new char[] { 'V' });
}
int computeRelevanceForCaseMatching(char[] token, char[] proposalName) {
if (CharOperation.equals(token, proposalName, true /* do not ignore case */)) {
return R_CASE + R_EXACT_NAME;
} else if (CharOperation.equals(token, proposalName, false /* ignore case */)) {
return R_EXACT_NAME;
}
return 0;
}
protected final GroovyCompletionProposal createProposal(int kind, int completionOffset) {
GroovyCompletionProposal proposal = new GroovyCompletionProposal(kind, completionOffset);
proposal.setNameLookup(nameLookup);
return proposal;
}
/**
* Fills in {@link #imports} and {@link #onDemandimports} from the compilation unit.
*
* NOTE: The original implementation of this method did not add "java.lang" to star
* imports. Adding it to the array may result in extra type proposals. Not sure...
*/
private void initializeImportArrays(GroovyCompilationUnitScope scope) {
int i, n = (scope.imports != null) ? scope.imports.length : 0, s, t;
for (i = 0, s = 0, t = 0; i < n; i += 1) {
if (!scope.imports[i].isStatic()) {
if (scope.imports[i].onDemand) {
s += 1;
} else {
t += 1;
}
}
}
char[][] starImports = new char[s][];
char[][][] typeImports = new char[t][][];
for (i = 0, s = 0, t = 0; i < n; i += 1) {
if (!scope.imports[i].isStatic()) {
if (scope.imports[i].onDemand) {
starImports[s++] = getImportName(scope.imports[i]);
} else {
typeImports[t++] = new char[][] {getSimpleName(scope.imports[i]), getImportName(scope.imports[i])};
}
}
}
imports = typeImports;
onDemandimports = starImports;
}
private static char[] getImportName(ImportBinding binding) {
if (binding.reference != null) {
return CharOperation.concatWith(binding.reference.getImportName(), '.');
}
return CharOperation.concatWith(binding.compoundName, '.');
}
private static char[] getSimpleName(ImportBinding binding) {
if (binding.reference != null) {
return binding.reference.getSimpleName();
}
return binding.compoundName[binding.compoundName.length - 1];
}
//--------------------------------------------------------------------------
private static class AcceptedCtor {
public int modifiers;
public char[] simpleTypeName;
public int parameterCount;
public char[] signature;
public char[][] parameterTypes;
public char[][] parameterNames;
public int typeModifiers;
public char[] packageName;
public int extraFlags;
public int accessibility;
public AcceptedCtor(int modifiers, char[] simpleTypeName,
int parameterCount, char[] signature, char[][] parameterTypes,
char[][] parameterNames, int typeModifiers, char[] packageName,
int extraFlags, int accessibility) {
this.modifiers = modifiers;
this.simpleTypeName = simpleTypeName;
this.parameterCount = parameterCount;
this.signature = signature;
this.parameterTypes = parameterTypes;
this.parameterNames = parameterNames;
this.typeModifiers = typeModifiers;
this.packageName = packageName;
this.extraFlags = extraFlags;
this.accessibility = accessibility;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append('{');
buffer.append(packageName);
buffer.append(',');
buffer.append(simpleTypeName);
buffer.append('}');
return buffer.toString();
}
}
private static class AcceptedType {
public char[] packageName;
public char[] simpleTypeName;
public char[][] enclosingTypeNames;
public int modifiers;
public int accessibility;
public boolean mustBeQualified = false;
public char[] fullyQualifiedName = null;
public char[] qualifiedTypeName = null;
AcceptedType(char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, int modifiers, int accessibility) {
this.packageName = packageName;
this.simpleTypeName = simpleTypeName;
this.enclosingTypeNames = enclosingTypeNames;
this.modifiers = modifiers;
this.accessibility = accessibility;
}
@Override
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append('{');
buffer.append(packageName);
buffer.append(',');
buffer.append(simpleTypeName);
buffer.append(',');
buffer.append(CharOperation.concatWith(enclosingTypeNames, '.'));
buffer.append('}');
return buffer.toString();
}
}
}