blob: 2c6eee43f7e480b21ee661098a986e0b8043693e [file] [log] [blame]
package org.eclipse.jdt.internal.codeassist;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.util.*;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.internal.codeassist.impl.*;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompletionRequestor;
import org.eclipse.jdt.core.IJavaModelMarker;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.codeassist.complete.*;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.parser.*;
import org.eclipse.jdt.internal.compiler.problem.*;
import org.eclipse.jdt.internal.compiler.util.*;
import org.eclipse.jdt.internal.core.BasicCompilationUnit;
import org.eclipse.jdt.internal.core.TypeConverter;
import org.eclipse.jdt.internal.compiler.impl.*;
/**
* This class is the entry point for source completions.
* It contains two public APIs used to call CodeAssist on a given source with
* a given environment, assisting position and storage (and possibly options).
*/
public final class CompletionEngine
extends Engine
implements ISearchRequestor, TypeConstants , ITerminalSymbols , RelevanceConstants {
public static boolean DEBUG = false;
private final static char[] ERROR_PATTERN = "*error*".toCharArray(); //$NON-NLS-1$
private final static char[] EXCEPTION_PATTERN = "*exception*".toCharArray(); //$NON-NLS-1$
private final static char[] SEMICOLON = new char[] { ';' };
TypeBinding[] expectedTypes;
boolean assistNodeIsClass;
boolean assistNodeIsException;
boolean assistNodeIsInterface;
AssistOptions options;
CompletionParser parser;
ICompletionRequestor requestor;
ProblemReporter problemReporter;
char[] source;
char[] token;
boolean resolvingImports = false;
boolean insideQualifiedReference = false;
int startPosition, actualCompletionPosition, endPosition, offset;
HashtableOfObject knownPkgs = new HashtableOfObject(10);
HashtableOfObject knownTypes = new HashtableOfObject(10);
Scanner nameScanner;
/*
static final char[][] mainDeclarations =
new char[][] {
"package".toCharArray(),
"import".toCharArray(),
"abstract".toCharArray(),
"final".toCharArray(),
"public".toCharArray(),
"class".toCharArray(),
"interface".toCharArray()};
static final char[][] modifiers = // may want field, method, type & member type modifiers
new char[][] {
"abstract".toCharArray(),
"final".toCharArray(),
"native".toCharArray(),
"public".toCharArray(),
"protected".toCharArray(),
"private".toCharArray(),
"static".toCharArray(),
"strictfp".toCharArray(),
"synchronized".toCharArray(),
"transient".toCharArray(),
"volatile".toCharArray()};
*/
static final char[][] baseTypes = new char[][] {
"boolean".toCharArray(), //$NON-NLS-1$
"byte".toCharArray(), //$NON-NLS-1$
"char".toCharArray(), //$NON-NLS-1$
"double".toCharArray(), //$NON-NLS-1$
"float".toCharArray(), //$NON-NLS-1$
"int".toCharArray(), //$NON-NLS-1$
"long".toCharArray(), //$NON-NLS-1$
"short".toCharArray(), //$NON-NLS-1$
"void".toCharArray(), //$NON-NLS-1$
};
static final char[] classField = "class".toCharArray(); //$NON-NLS-1$
static final char[] lengthField = "length".toCharArray(); //$NON-NLS-1$
static final char[] THIS = "this".toCharArray(); //$NON-NLS-1$
static final char[] THROWS = "throws".toCharArray(); //$NON-NLS-1$
static InvocationSite FakeInvocationSite = new InvocationSite(){
public boolean isSuperAccess(){ return false; }
public boolean isTypeAccess(){ return false; }
public void setActualReceiverType(ReferenceBinding receiverType) {}
public void setDepth(int depth){}
public void setFieldIndex(int depth){}
};
/**
* The CompletionEngine is responsible for computing source completions.
*
* It requires a searchable name environment, which supports some
* specific search APIs, and a requestor to feed back the results to a UI.
*
* @param nameEnvironment org.eclipse.jdt.internal.codeassist.ISearchableNameEnvironment
* used to resolve type/package references and search for types/packages
* based on partial names.
*
* @param requestor org.eclipse.jdt.internal.codeassist.ICompletionRequestor
* since the engine might produce answers of various forms, the engine
* is associated with a requestor able to accept all possible completions.
*
* @param settings java.util.Map
* set of options used to configure the code assist engine.
*/
public CompletionEngine(
ISearchableNameEnvironment nameEnvironment,
ICompletionRequestor requestor,
Map settings) {
this.requestor = requestor;
this.nameEnvironment = nameEnvironment;
options = new AssistOptions(settings);
CompilerOptions compilerOptions = new CompilerOptions(settings);
problemReporter = new ProblemReporter(
DefaultErrorHandlingPolicies.proceedWithAllProblems(),
compilerOptions,
new DefaultProblemFactory(Locale.getDefault()) {
public void record(IProblem problem, CompilationResult unitResult, ReferenceContext referenceContext) {
if (problem.isError() && (problem.getID() & IProblem.Syntax) != 0) {
CompletionEngine.this.requestor.acceptError(problem);
}
}
});
this.parser =
new CompletionParser(problemReporter, compilerOptions.assertMode);
this.lookupEnvironment =
new LookupEnvironment(this, compilerOptions, problemReporter, nameEnvironment);
this.nameScanner =
new Scanner(false, false, false, compilerOptions.assertMode);
}
/**
* One result of the search consists of a new class.
*
* NOTE - All package and type names are presented in their readable form:
* Package names are in the form "a.b.c".
* Nested type names are in the qualified form "A.M".
* The default package is represented by an empty array.
*/
public void acceptClass(char[] packageName, char[] className, int modifiers) {
char[] fullyQualifiedName = CharOperation.concat(packageName, className, '.');
char[] completionName = fullyQualifiedName;
if (this.knownTypes.containsKey(completionName)) return;
this.knownTypes.put(completionName, this);
int relevance = R_DEFAULT;
if (resolvingImports) {
completionName = CharOperation.concat(completionName, SEMICOLON);
relevance += computeRelevanceForCaseMatching(token, fullyQualifiedName);
} else {
if (!insideQualifiedReference) {
if (mustQualifyType(packageName, className)) {
if (packageName == null || packageName.length == 0)
if (unitScope != null && unitScope.fPackage.compoundName != NoCharChar)
return; // ignore types from the default package from outside it
} else {
completionName = className;
}
}
relevance += computeRelevanceForCaseMatching(token, className);
relevance += computeRelevanceForExpectingType(packageName, className);
relevance += computeRelevanceForClass();
relevance += computeRelevanceForException(className);
}
requestor.acceptClass(
packageName,
className,
completionName,
modifiers,
startPosition - offset,
endPosition - offset,
relevance);
}
/**
* One result of the search consists of a new interface.
*
* NOTE - All package and type names are presented in their readable form:
* Package names are in the form "a.b.c".
* Nested type names are in the qualified form "A.I".
* The default package is represented by an empty array.
*/
public void acceptInterface(
char[] packageName,
char[] interfaceName,
int modifiers) {
char[] fullyQualifiedName = CharOperation.concat(packageName, interfaceName, '.');
char[] completionName = fullyQualifiedName;
if (this.knownTypes.containsKey(completionName)) return;
this.knownTypes.put(completionName, this);
int relevance = R_DEFAULT;
if (resolvingImports) {
completionName = CharOperation.concat(completionName, new char[] { ';' });
relevance += computeRelevanceForCaseMatching(token, fullyQualifiedName);
} else {
if (!insideQualifiedReference) {
if (mustQualifyType(packageName, interfaceName)) {
if (packageName == null || packageName.length == 0)
if (unitScope != null && unitScope.fPackage.compoundName != NoCharChar)
return; // ignore types from the default package from outside it
} else {
completionName = interfaceName;
}
}
relevance += computeRelevanceForCaseMatching(token, interfaceName);
relevance += computeRelevanceForExpectingType(packageName, interfaceName);
relevance += computeRelevanceForInterface();
}
requestor.acceptInterface(
packageName,
interfaceName,
completionName,
modifiers,
startPosition - offset,
endPosition - offset,
relevance);
}
/**
* One result of the search consists of a new package.
*
* NOTE - All package names are presented in their readable form:
* Package names are in the form "a.b.c".
* The default package is represented by an empty array.
*/
public void acceptPackage(char[] packageName) {
if (this.knownPkgs.containsKey(packageName)) return;
this.knownPkgs.put(packageName, this);
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(token, packageName);
requestor.acceptPackage(
packageName,
resolvingImports
? CharOperation.concat(packageName, new char[] { '.', '*', ';' })
: packageName,
startPosition - offset,
endPosition - offset,
relevance);
}
/**
* One result of the search consists of a new type.
*
* NOTE - All package and type names are presented in their readable form:
* Package names are in the form "a.b.c".
* Nested type names are in the qualified form "A.M".
* The default package is represented by an empty array.
*/
public void acceptType(char[] packageName, char[] typeName) {
char[] fullyQualifiedName = CharOperation.concat(packageName, typeName, '.');
char[] completionName = fullyQualifiedName;
if (this.knownTypes.containsKey(completionName)) return;
this.knownTypes.put(completionName, this);
int relevance = R_DEFAULT;
if (resolvingImports) {
completionName = CharOperation.concat(completionName, new char[] { ';' });
relevance += computeRelevanceForCaseMatching(token, fullyQualifiedName);
} else {
if (!insideQualifiedReference) {
if (mustQualifyType(packageName, typeName)) {
if (packageName == null || packageName.length == 0)
if (unitScope != null && unitScope.fPackage.compoundName != NoCharChar)
return; // ignore types from the default package from outside it
} else {
completionName = typeName;
}
}
relevance += computeRelevanceForCaseMatching(token, typeName);
relevance += computeRelevanceForExpectingType(packageName, typeName);
}
requestor.acceptType(
packageName,
typeName,
completionName,
startPosition - offset,
endPosition - offset,
relevance);
}
private void complete(AstNode astNode, Binding qualifiedBinding, Scope scope) {
setSourceRange(astNode.sourceStart, astNode.sourceEnd);
if(parser.assistNodeParent != null) {
computeExpectedTypes(parser.assistNodeParent, scope);
}
// defaults... some nodes will change these
if (astNode instanceof CompletionOnFieldType) {
CompletionOnFieldType field = (CompletionOnFieldType) astNode;
CompletionOnSingleTypeReference type = (CompletionOnSingleTypeReference) field.type;
token = type.token;
setSourceRange(type.sourceStart, type.sourceEnd);
// findKeywords(token, modifiers, scope); // could be the start of a field, method or member type
findTypesAndPackages(token, scope);
if(!field.isLocalVariable && field.modifiers == CompilerModifiers.AccDefault) {
findMethods(token,null,scope.enclosingSourceType(),scope,new ObjectVector(),false,false,true,null,null,false);
}
} else {
if(astNode instanceof CompletionOnMethodReturnType) {
CompletionOnMethodReturnType method = (CompletionOnMethodReturnType) astNode;
SingleTypeReference type = (CompletionOnSingleTypeReference) method.returnType;
token = type.token;
setSourceRange(type.sourceStart, type.sourceEnd);
findTypesAndPackages(token, scope);
if(method.modifiers == CompilerModifiers.AccDefault) {
findMethods(token,null,scope.enclosingSourceType(),scope,new ObjectVector(),false,false,true,null,null,false);
}
} else {
if (astNode instanceof CompletionOnSingleNameReference) {
token = ((CompletionOnSingleNameReference) astNode).token;
findVariablesAndMethods(
token,
scope,
(CompletionOnSingleNameReference) astNode,
scope);
// can be the start of a qualified type name
findTypesAndPackages(token, scope);
} else {
if (astNode instanceof CompletionOnSingleTypeReference) {
token = ((CompletionOnSingleTypeReference) astNode).token;
assistNodeIsClass = astNode instanceof CompletionOnClassReference;
assistNodeIsException = astNode instanceof CompletionOnExceptionReference;
assistNodeIsInterface = astNode instanceof CompletionOnInterfaceReference;
// can be the start of a qualified type name
if (qualifiedBinding == null) {
findTypesAndPackages(token, scope);
} else {
findMemberTypes(
token,
(ReferenceBinding) qualifiedBinding,
scope,
scope.enclosingSourceType());
}
} else {
if (astNode instanceof CompletionOnQualifiedNameReference) {
insideQualifiedReference = true;
CompletionOnQualifiedNameReference ref =
(CompletionOnQualifiedNameReference) astNode;
token = ref.completionIdentifier;
long completionPosition = ref.sourcePositions[ref.sourcePositions.length - 1];
if (qualifiedBinding instanceof VariableBinding) {
setSourceRange((int) (completionPosition >>> 32), (int) completionPosition);
TypeBinding receiverType = ((VariableBinding) qualifiedBinding).type;
if (receiverType != null) {
findFieldsAndMethods(token, receiverType, scope, ref, scope,false);
}
} else {
if (qualifiedBinding instanceof ReferenceBinding) {
ReferenceBinding receiverType = (ReferenceBinding) qualifiedBinding;
setSourceRange((int) (completionPosition >>> 32), (int) completionPosition);
findMemberTypes(token, receiverType, scope, scope.enclosingSourceType());
findClassField(token, (TypeBinding) qualifiedBinding, scope);
findFields(
token,
receiverType,
scope,
new ObjectVector(),
new ObjectVector(),
true,
ref,
scope,
false);
findMethods(
token,
null,
receiverType,
scope,
new ObjectVector(),
true,
false,
false,
ref,
scope,
false);
} else {
if (qualifiedBinding instanceof PackageBinding) {
setSourceRange(astNode.sourceStart, (int) completionPosition);
// replace to the end of the completion identifier
findTypesAndSubpackages(token, (PackageBinding) qualifiedBinding);
}
}
}
} else {
if (astNode instanceof CompletionOnQualifiedTypeReference) {
insideQualifiedReference = true;
assistNodeIsClass = astNode instanceof CompletionOnQualifiedClassReference;
assistNodeIsException = astNode instanceof CompletionOnQualifiedExceptionReference;
assistNodeIsInterface = astNode instanceof CompletionOnQualifiedInterfaceReference;
CompletionOnQualifiedTypeReference ref =
(CompletionOnQualifiedTypeReference) astNode;
token = ref.completionIdentifier;
long completionPosition = ref.sourcePositions[ref.tokens.length];
// get the source positions of the completion identifier
if (qualifiedBinding instanceof ReferenceBinding) {
setSourceRange((int) (completionPosition >>> 32), (int) completionPosition);
findMemberTypes(
token,
(ReferenceBinding) qualifiedBinding,
scope,
scope.enclosingSourceType());
} else {
if (qualifiedBinding instanceof PackageBinding) {
setSourceRange(astNode.sourceStart, (int) completionPosition);
// replace to the end of the completion identifier
findTypesAndSubpackages(token, (PackageBinding) qualifiedBinding);
}
}
} else {
if (astNode instanceof CompletionOnMemberAccess) {
CompletionOnMemberAccess access = (CompletionOnMemberAccess) astNode;
long completionPosition = access.nameSourcePosition;
setSourceRange((int) (completionPosition >>> 32), (int) completionPosition);
token = access.token;
findFieldsAndMethods(
token,
(TypeBinding) qualifiedBinding,
scope,
access,
scope,
false);
} else {
if (astNode instanceof CompletionOnMessageSend) {
CompletionOnMessageSend messageSend = (CompletionOnMessageSend) astNode;
TypeBinding[] argTypes =
computeTypes(messageSend.arguments, (BlockScope) scope);
token = messageSend.selector;
if (qualifiedBinding == null) {
findImplicitMessageSends(token, argTypes, scope, messageSend, scope);
} else {
findMethods(
token,
argTypes,
(ReferenceBinding) qualifiedBinding,
scope,
new ObjectVector(),
false,
true,
false,
messageSend,
scope,
false);
}
} else {
if (astNode instanceof CompletionOnExplicitConstructorCall) {
CompletionOnExplicitConstructorCall constructorCall =
(CompletionOnExplicitConstructorCall) astNode;
TypeBinding[] argTypes =
computeTypes(constructorCall.arguments, (BlockScope) scope);
findConstructors(
(ReferenceBinding) qualifiedBinding,
argTypes,
scope,
constructorCall,
false);
} else {
if (astNode instanceof CompletionOnQualifiedAllocationExpression) {
CompletionOnQualifiedAllocationExpression allocExpression =
(CompletionOnQualifiedAllocationExpression) astNode;
TypeBinding[] argTypes =
computeTypes(allocExpression.arguments, (BlockScope) scope);
ReferenceBinding ref = (ReferenceBinding) qualifiedBinding;
if(ref.isClass()) {
if(!ref.isAbstract()) {
findConstructors(
ref,
argTypes,
scope,
allocExpression,
false);
}
}
if(!ref.isFinal()){
findAnonymousType(
ref,
argTypes,
scope,
allocExpression);
}
} else {
if (astNode instanceof CompletionOnClassLiteralAccess) {
CompletionOnClassLiteralAccess access = (CompletionOnClassLiteralAccess) astNode;
setSourceRange(access.classStart, access.sourceEnd);
token = access.completionIdentifier;
findClassField(token, (TypeBinding) qualifiedBinding, scope);
} else {
if(astNode instanceof CompletionOnMethodName) {
CompletionOnMethodName method = (CompletionOnMethodName) astNode;
setSourceRange(method.sourceStart, method.selectorEnd);
FieldBinding[] fields = scope.enclosingSourceType().fields();
char[][] excludeNames = new char[fields.length][];
for(int i = 0 ; i < fields.length ; i++){
excludeNames[i] = fields[i].name;
}
token = method.selector;
findVariableNames(token, method.returnType, excludeNames);
} else {
if (astNode instanceof CompletionOnFieldName) {
CompletionOnFieldName field = (CompletionOnFieldName) astNode;
FieldBinding[] fields = scope.enclosingSourceType().fields();
char[][] excludeNames = new char[fields.length][];
for(int i = 0 ; i < fields.length ; i++){
excludeNames[i] = fields[i].name;
}
token = field.realName;
findVariableNames(field.realName, field.type, excludeNames);
} else {
if (astNode instanceof CompletionOnLocalName ||
astNode instanceof CompletionOnArgumentName){
LocalDeclaration variable = (LocalDeclaration) astNode;
LocalVariableBinding[] locals = ((BlockScope)scope).locals;
char[][] excludeNames = new char[locals.length][];
int localCount = 0;
for(int i = 0 ; i < locals.length ; i++){
if(locals[i] != null) {
excludeNames[localCount++] = locals[i].name;
}
}
System.arraycopy(excludeNames, 0, excludeNames = new char[localCount][], 0, localCount);
if(variable instanceof CompletionOnLocalName){
token = ((CompletionOnLocalName) variable).realName;
} else {
token = ((CompletionOnArgumentName) variable).realName;
}
findVariableNames(token, variable.type, excludeNames);
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
public void complete(IType type, char[] snippet, int position, char[][] localVariableTypeNames, char[][] localVariableNames, int[] localVariableModifiers, boolean isStatic){
TypeConverter converter = new TypeConverter();
IType topLevelType = type;
while(topLevelType.getDeclaringType() != null) {
topLevelType = topLevelType.getDeclaringType();
}
CompilationResult compilationResult = new CompilationResult((topLevelType.getElementName() + ".java").toCharArray(), 1, 1); //$NON-NLS-1$
CompilationUnitDeclaration compilationUnit = new CompilationUnitDeclaration(problemReporter, compilationResult, 0);
try {
TypeDeclaration typeDeclaration = converter.buildTypeDeclaration(type, compilationUnit, compilationResult, problemReporter);
if(typeDeclaration != null) {
// build AST from snippet
Initializer fakeInitializer = parseSnippeInitializer(snippet, position, localVariableTypeNames, localVariableNames, localVariableModifiers, isStatic);
// merge AST
FieldDeclaration[] oldFields = typeDeclaration.fields;
FieldDeclaration[] newFields = new FieldDeclaration[oldFields.length + 1];
System.arraycopy(oldFields, 0, newFields, 0, oldFields.length);
newFields[oldFields.length] = fakeInitializer;
typeDeclaration.fields = newFields;
if(DEBUG) {
System.out.println("SNIPPET COMPLETION AST :"); //$NON-NLS-1$
System.out.println(compilationUnit.toString());
}
if (compilationUnit.types != null) {
try {
lookupEnvironment.buildTypeBindings(compilationUnit);
if ((unitScope = compilationUnit.scope) != null) {
lookupEnvironment.completeTypeBindings(compilationUnit, true);
compilationUnit.scope.faultInTypes();
compilationUnit.resolve();
}
} catch (CompletionNodeFound e) {
// completionNodeFound = true;
if (e.astNode != null) {
// if null then we found a problem in the completion node
complete(e.astNode, e.qualifiedBinding, e.scope);
}
}
}
}
} catch(JavaModelException e) {
// Do nothing
}
}
private Initializer parseSnippeInitializer(char[] snippet, int position, char[][] localVariableTypeNames, char[][] localVariableNames, int[] localVariableModifiers, boolean isStatic){
StringBuffer prefix = new StringBuffer();
prefix.append("public class FakeType {\n "); //$NON-NLS-1$
if(isStatic) {
prefix.append("static "); //$NON-NLS-1$
}
prefix.append("{\n"); //$NON-NLS-1$
for (int i = 0; i < localVariableTypeNames.length; i++) {
prefix.append(AstNode.modifiersString(localVariableModifiers[i]));
prefix.append(' ');
prefix.append(localVariableTypeNames[i]);
prefix.append(' ');
prefix.append(localVariableNames[i]);
prefix.append(';');
}
char[] fakeSource = CharOperation.concat(prefix.toString().toCharArray(), snippet, "}}".toCharArray());//$NON-NLS-1$
offset = prefix.length();
String encoding = (String) JavaCore.getOptions().get(CompilerOptions.OPTION_Encoding);
if ("".equals(encoding)) encoding = null; //$NON-NLS-1$
BasicCompilationUnit fakeUnit = new BasicCompilationUnit(
fakeSource,
null,
"FakeType.java", //$NON-NLS-1$
encoding);
actualCompletionPosition = prefix.length() + position - 1;
CompilationResult fakeResult = new CompilationResult(fakeUnit, 1, 1);
CompilationUnitDeclaration fakeAST = parser.dietParse(fakeUnit, fakeResult, actualCompletionPosition);
parseMethod(fakeAST, actualCompletionPosition);
return (Initializer)fakeAST.types[0].fields[0];
}
/**
* Ask the engine to compute a completion at the specified position
* of the given compilation unit.
*
* @return void
* completion results are answered through a requestor.
*
* @param sourceUnit org.eclipse.jdt.internal.compiler.env.ICompilationUnit
* the source of the current compilation unit.
*
* @param completionPosition int
* a position in the source where the completion is taking place.
* This position is relative to the source provided.
*/
public void complete(ICompilationUnit sourceUnit, int completionPosition, int offset) {
if(DEBUG) {
System.out.print("COMPLETION IN "); //$NON-NLS-1$
System.out.print(sourceUnit.getFileName());
System.out.print(" AT POSITION "); //$NON-NLS-1$
System.out.println(completionPosition);
System.out.println("COMPLETION - Source :"); //$NON-NLS-1$
System.out.println(sourceUnit.getContents());
}
try {
actualCompletionPosition = completionPosition - 1;
this.offset = offset;
// for now until we can change the UI.
CompilationResult result = new CompilationResult(sourceUnit, 1, 1);
CompilationUnitDeclaration parsedUnit = parser.dietParse(sourceUnit, result, actualCompletionPosition);
// boolean completionNodeFound = false;
if (parsedUnit != null) {
if(DEBUG) {
System.out.println("COMPLETION - Diet AST :"); //$NON-NLS-1$
System.out.println(parsedUnit.toString());
}
// scan the package & import statements first
if (parsedUnit.currentPackage instanceof CompletionOnPackageReference) {
findPackages((CompletionOnPackageReference) parsedUnit.currentPackage);
return;
}
ImportReference[] imports = parsedUnit.imports;
if (imports != null) {
for (int i = 0, length = imports.length; i < length; i++) {
ImportReference importReference = imports[i];
if (importReference instanceof CompletionOnImportReference) {
findImports((CompletionOnImportReference) importReference);
return;
}
}
}
if (parsedUnit.types != null) {
try {
lookupEnvironment.buildTypeBindings(parsedUnit);
if ((unitScope = parsedUnit.scope) != null) {
source = sourceUnit.getContents();
lookupEnvironment.completeTypeBindings(parsedUnit, true);
parsedUnit.scope.faultInTypes();
parseMethod(parsedUnit, actualCompletionPosition);
if(DEBUG) {
System.out.println("COMPLETION - AST :"); //$NON-NLS-1$
System.out.println(parsedUnit.toString());
}
parsedUnit.resolve();
}
} catch (CompletionNodeFound e) {
// completionNodeFound = true;
if (e.astNode != null) {
if(DEBUG) {
System.out.print("COMPLETION - Completion node : "); //$NON-NLS-1$
System.out.println(e.astNode.toString());
}
// if null then we found a problem in the completion node
complete(e.astNode, e.qualifiedBinding, e.scope);
}
}
}
}
/* Ignore package, import, class & interface keywords for now...
if (!completionNodeFound) {
if (parsedUnit == null || parsedUnit.types == null) {
// this is not good enough... can still be trying to define a second type
CompletionScanner scanner = (CompletionScanner) parser.scanner;
setSourceRange(scanner.completedIdentifierStart, scanner.completedIdentifierEnd);
findKeywords(scanner.completionIdentifier, mainDeclarations, null);
}
// currently have no way to know if extends/implements are possible keywords
}
*/
} catch (IndexOutOfBoundsException e) { // work-around internal failure - 1GEMF6D
} catch (InvalidCursorLocation e) { // may eventually report a usefull error
} catch (AbortCompilation e) { // ignore this exception for now since it typically means we cannot find java.lang.Object
} catch (CompletionNodeFound e){ // internal failure - bugs 5618
} finally {
reset();
}
}
private TypeBinding[] computeTypes(Expression[] arguments, BlockScope scope) {
if (arguments == null)
return null;
int argsLength = arguments.length;
TypeBinding[] argTypes = new TypeBinding[argsLength];
for (int a = argsLength; --a >= 0;)
argTypes[a] = arguments[a].resolveType(scope);
return argTypes;
}
private void findAnonymousType(
ReferenceBinding currentType,
TypeBinding[] argTypes,
Scope scope,
InvocationSite invocationSite) {
if (currentType.isInterface()) {
char[] completion = TypeConstants.NoChar;
// nothing to insert - do not want to replace the existing selector & arguments
if (source == null
|| source.length <= endPosition
|| source[endPosition] != ')')
completion = new char[] { ')' };
requestor.acceptAnonymousType(
currentType.qualifiedPackageName(),
currentType.qualifiedSourceName(),
TypeConstants.NoCharChar,
TypeConstants.NoCharChar,
TypeConstants.NoCharChar,
completion,
IConstants.AccPublic,
endPosition - offset,
endPosition - offset,
R_DEFAULT);
} else {
findConstructors(
currentType,
argTypes,
scope,
invocationSite,
true);
}
}
private void findClassField(char[] token, TypeBinding receiverType, Scope scope) {
if (token == null)
return;
if (token.length <= classField.length
&& CharOperation.prefixEquals(token, classField, false /* ignore case */
)) {
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(token, classField);
relevance += computeRelevanceForExpectingType(scope.getJavaLangClass());
requestor.acceptField(
NoChar,
NoChar,
classField,
NoChar,
NoChar,
classField,
CompilerModifiers.AccStatic | CompilerModifiers.AccPublic,
startPosition - offset,
endPosition - offset,
relevance);
}
}
private void findConstructors(
ReferenceBinding currentType,
TypeBinding[] argTypes,
Scope scope,
InvocationSite invocationSite,
boolean forAnonymousType) {
// No visibility checks can be performed without the scope & invocationSite
MethodBinding[] methods = currentType.availableMethods();
if(methods != null) {
int minArgLength = argTypes == null ? 0 : argTypes.length;
next : for (int f = methods.length; --f >= 0;) {
MethodBinding constructor = methods[f];
if (constructor.isConstructor()) {
if (constructor.isSynthetic()) continue next;
if (options.checkVisibility
&& !constructor.canBeSeenBy(invocationSite, scope)) continue next;
TypeBinding[] parameters = constructor.parameters;
int paramLength = parameters.length;
if (minArgLength > paramLength)
continue next;
for (int a = minArgLength; --a >= 0;)
if (argTypes[a] != null) // can be null if it could not be resolved properly
if (!scope.areTypesCompatible(argTypes[a], constructor.parameters[a]))
continue next;
char[][] parameterPackageNames = new char[paramLength][];
char[][] parameterTypeNames = new char[paramLength][];
for (int i = 0; i < paramLength; i++) {
TypeBinding type = parameters[i];
parameterPackageNames[i] = type.qualifiedPackageName();
parameterTypeNames[i] = type.qualifiedSourceName();
}
char[][] parameterNames = findMethodParameterNames(constructor,parameterTypeNames);
char[] completion = TypeConstants.NoChar;
// nothing to insert - do not want to replace the existing selector & arguments
if (source == null
|| source.length <= endPosition
|| source[endPosition] != ')')
completion = new char[] { ')' };
if(forAnonymousType){
requestor.acceptAnonymousType(
currentType.qualifiedPackageName(),
currentType.qualifiedSourceName(),
parameterPackageNames,
parameterTypeNames,
parameterNames,
completion,
constructor.modifiers,
endPosition - offset,
endPosition - offset,
R_DEFAULT);
} else {
requestor.acceptMethod(
currentType.qualifiedPackageName(),
currentType.qualifiedSourceName(),
currentType.sourceName(),
parameterPackageNames,
parameterTypeNames,
parameterNames,
TypeConstants.NoChar,
TypeConstants.NoChar,
completion,
constructor.modifiers,
endPosition - offset,
endPosition - offset,
R_DEFAULT);
}
}
}
}
}
// Helper method for findFields(char[], ReferenceBinding, Scope, ObjectVector, boolean)
private void findFields(
char[] fieldName,
FieldBinding[] fields,
Scope scope,
ObjectVector fieldsFound,
ObjectVector localsFound,
boolean onlyStaticFields,
ReferenceBinding receiverType,
InvocationSite invocationSite,
Scope invocationScope,
boolean implicitCall) {
// Inherited fields which are hidden by subclasses are filtered out
// No visibility checks can be performed without the scope & invocationSite
int fieldLength = fieldName.length;
next : for (int f = fields.length; --f >= 0;) {
FieldBinding field = fields[f];
if (field.isSynthetic()) continue next;
if (onlyStaticFields && !field.isStatic()) continue next;
if (fieldLength > field.name.length) continue next;
if (!CharOperation.prefixEquals(fieldName, field.name, false /* ignore case */)) continue next;
if (options.checkVisibility
&& !field.canBeSeenBy(receiverType, invocationSite, scope)) continue next;
boolean prefixRequired = false;
for (int i = fieldsFound.size; --i >= 0;) {
Object[] other = (Object[])fieldsFound.elementAt(i);
FieldBinding otherField = (FieldBinding) other[0];
ReferenceBinding otherReceiverType = (ReferenceBinding) other[1];
if (field == otherField && receiverType == otherReceiverType)
continue next;
if (CharOperation.equals(field.name, otherField.name, true)) {
if (field.declaringClass.isSuperclassOf(otherField.declaringClass))
continue next;
if (otherField.declaringClass.isInterface())
if (field.declaringClass.implementsInterface(otherField.declaringClass, true))
continue next;
prefixRequired = true;
}
}
for (int l = localsFound.size; --l >= 0;) {
LocalVariableBinding local = (LocalVariableBinding) localsFound.elementAt(l);
if (CharOperation.equals(field.name, local.name, true)) {
SourceTypeBinding declarationType = scope.enclosingSourceType();
if (declarationType.isAnonymousType() && declarationType != invocationScope.enclosingSourceType()) {
continue next;
}
prefixRequired = true;
break;
}
}
fieldsFound.add(new Object[]{field, receiverType});
char[] completion = field.name;
if(prefixRequired || options.forceImplicitQualification){
char[] prefix = computePrefix(scope.enclosingSourceType(), invocationScope.enclosingSourceType(), field.isStatic());
completion = CharOperation.concat(prefix,completion,'.');
}
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(fieldName, field.name);
relevance += computeRelevanceForExpectingType(field.type);
requestor
.acceptField(
field.declaringClass.qualifiedPackageName(),
field.declaringClass.qualifiedSourceName(),
field.name,
field.type.qualifiedPackageName(),
field.type.qualifiedSourceName(),
completion,
// may include some qualification to resolve ambiguities
field.modifiers, startPosition - offset, endPosition - offset,
relevance);
}
}
private void findFields(
char[] fieldName,
ReferenceBinding receiverType,
Scope scope,
ObjectVector fieldsFound,
ObjectVector localsFound,
boolean onlyStaticFields,
InvocationSite invocationSite,
Scope invocationScope,
boolean implicitCall) {
if (fieldName == null)
return;
ReferenceBinding currentType = receiverType;
ReferenceBinding[][] interfacesToVisit = null;
int lastPosition = -1;
do {
ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
if (itsInterfaces != NoSuperInterfaces) {
if (interfacesToVisit == null)
interfacesToVisit = new ReferenceBinding[5][];
if (++lastPosition == interfacesToVisit.length)
System.arraycopy(
interfacesToVisit,
0,
interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
0,
lastPosition);
interfacesToVisit[lastPosition] = itsInterfaces;
}
FieldBinding[] fields = currentType.availableFields();
if(fields != null) {
findFields(
fieldName,
fields,
scope,
fieldsFound,
localsFound,
onlyStaticFields,
receiverType,
invocationSite,
invocationScope,
implicitCall);
}
currentType = currentType.superclass();
} while (currentType != null);
if (interfacesToVisit != null) {
for (int i = 0; i <= lastPosition; i++) {
ReferenceBinding[] interfaces = interfacesToVisit[i];
for (int j = 0, length = interfaces.length; j < length; j++) {
ReferenceBinding anInterface = interfaces[j];
if ((anInterface.tagBits & TagBits.InterfaceVisited) == 0) {
// if interface as not already been visited
anInterface.tagBits |= TagBits.InterfaceVisited;
FieldBinding[] fields = anInterface.availableFields();
if(fields != null) {
findFields(
fieldName,
fields,
scope,
fieldsFound,
localsFound,
onlyStaticFields,
receiverType,
invocationSite,
invocationScope,
implicitCall);
}
ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
if (itsInterfaces != NoSuperInterfaces) {
if (++lastPosition == interfacesToVisit.length)
System.arraycopy(
interfacesToVisit,
0,
interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
0,
lastPosition);
interfacesToVisit[lastPosition] = itsInterfaces;
}
}
}
}
// bit reinitialization
for (int i = 0; i <= lastPosition; i++) {
ReferenceBinding[] interfaces = interfacesToVisit[i];
for (int j = 0, length = interfaces.length; j < length; j++)
interfaces[j].tagBits &= ~TagBits.InterfaceVisited;
}
}
}
private void findFieldsAndMethods(
char[] token,
TypeBinding receiverType,
Scope scope,
InvocationSite invocationSite,
Scope invocationScope,
boolean implicitCall) {
if (token == null)
return;
if (receiverType.isBaseType())
return; // nothing else is possible with base types
if (receiverType.isArrayType()) {
if (token.length <= lengthField.length
&& CharOperation.prefixEquals(token, lengthField, false /* ignore case */
)) {
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(token,lengthField);
relevance += computeRelevanceForExpectingType(BaseTypes.IntBinding);
requestor.acceptField(
NoChar,
NoChar,
lengthField,
NoChar,
NoChar,
lengthField,
CompilerModifiers.AccPublic,
startPosition - offset,
endPosition - offset,
relevance);
}
receiverType = scope.getJavaLangObject();
}
findFields(
token,
(ReferenceBinding) receiverType,
scope,
new ObjectVector(),
new ObjectVector(),
false,
invocationSite,
invocationScope,
implicitCall);
findMethods(
token,
null,
(ReferenceBinding) receiverType,
scope,
new ObjectVector(),
false,
false,
false,
invocationSite,
invocationScope,
implicitCall);
}
private void findImports(CompletionOnImportReference importReference) {
char[][] tokens = importReference.tokens;
char[] importName = CharOperation.concatWith(tokens, '.');
if (importName.length == 0)
return;
char[] lastToken = tokens[tokens.length - 1];
if(lastToken != null && lastToken.length == 0)
importName = CharOperation.concat(importName, new char[]{'.'});
resolvingImports = true;
setSourceRange(
importReference.sourceStart,
importReference.declarationSourceEnd);
token = importName;
// want to replace the existing .*;
nameEnvironment.findPackages(importName, this);
nameEnvironment.findTypes(importName, this);
}
// what about onDemand types? Ignore them since it does not happen!
// import p1.p2.A.*;
private void findKeywords(char[] keyword, char[][] choices, Scope scope) {
int length = keyword.length;
if (length > 0)
for (int i = 0; i < choices.length; i++)
if (length <= choices[i].length
&& CharOperation.prefixEquals(keyword, choices[i], false /* ignore case */
)){
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(keyword, choices[i]);
requestor.acceptKeyword(choices[i], startPosition - offset, endPosition - offset,relevance);
}
}
// Helper method for findMemberTypes(char[], ReferenceBinding, Scope)
private void findMemberTypes(
char[] typeName,
ReferenceBinding[] memberTypes,
ObjectVector typesFound,
ReferenceBinding receiverType,
SourceTypeBinding invocationType) {
// Inherited member types which are hidden by subclasses are filtered out
// No visibility checks can be performed without the scope & invocationSite
int typeLength = typeName.length;
next : for (int m = memberTypes.length; --m >= 0;) {
ReferenceBinding memberType = memberTypes[m];
// if (!wantClasses && memberType.isClass()) continue next;
// if (!wantInterfaces && memberType.isInterface()) continue next;
if (typeLength > memberType.sourceName.length)
continue next;
if (!CharOperation.prefixEquals(typeName, memberType.sourceName, false
/* ignore case */
))
continue next;
if (options.checkVisibility
&& !memberType.canBeSeenBy(receiverType, invocationType))
continue next;
for (int i = typesFound.size; --i >= 0;) {
ReferenceBinding otherType = (ReferenceBinding) typesFound.elementAt(i);
if (memberType == otherType)
continue next;
if (CharOperation.equals(memberType.sourceName, otherType.sourceName, true)) {
if (memberType.enclosingType().isSuperclassOf(otherType.enclosingType()))
continue next;
if (otherType.enclosingType().isInterface())
if (memberType.enclosingType()
.implementsInterface(otherType.enclosingType(), true))
continue next;
}
}
typesFound.add(memberType);
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(typeName, memberType.sourceName);
relevance += computeRelevanceForExpectingType(memberType);
if (memberType.isClass()) {
relevance += computeRelevanceForClass();
requestor.acceptClass(
memberType.qualifiedPackageName(),
memberType.qualifiedSourceName(),
memberType.sourceName(),
memberType.modifiers,
startPosition - offset,
endPosition - offset,
relevance);
} else {
relevance += computeRelevanceForInterface();
requestor.acceptInterface(
memberType.qualifiedPackageName(),
memberType.qualifiedSourceName(),
memberType.sourceName(),
memberType.modifiers,
startPosition - offset,
endPosition - offset,
relevance);
}
}
}
private void findMemberTypes(
char[] typeName,
ReferenceBinding receiverType,
Scope scope,
SourceTypeBinding typeInvocation) {
ReferenceBinding currentType = receiverType;
if (typeName == null)
return;
if (currentType.superInterfaces() == null)
return; // we're trying to find a supertype
ObjectVector typesFound = new ObjectVector();
if (insideQualifiedReference
|| typeName.length == 0) { // do not search up the hierarchy
findMemberTypes(
typeName,
currentType.memberTypes(),
typesFound,
receiverType,
typeInvocation);
return;
}
ReferenceBinding[][] interfacesToVisit = null;
int lastPosition = -1;
do {
ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
if (itsInterfaces != NoSuperInterfaces) {
if (interfacesToVisit == null)
interfacesToVisit = new ReferenceBinding[5][];
if (++lastPosition == interfacesToVisit.length)
System.arraycopy(
interfacesToVisit,
0,
interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
0,
lastPosition);
interfacesToVisit[lastPosition] = itsInterfaces;
}
findMemberTypes(
typeName,
currentType.memberTypes(),
typesFound,
receiverType,
typeInvocation);
currentType = currentType.superclass();
} while (currentType != null);
if (interfacesToVisit != null) {
for (int i = 0; i <= lastPosition; i++) {
ReferenceBinding[] interfaces = interfacesToVisit[i];
for (int j = 0, length = interfaces.length; j < length; j++) {
ReferenceBinding anInterface = interfaces[j];
if ((anInterface.tagBits & TagBits.InterfaceVisited) == 0) {
// if interface as not already been visited
anInterface.tagBits |= TagBits.InterfaceVisited;
findMemberTypes(
typeName,
anInterface.memberTypes(),
typesFound,
receiverType,
typeInvocation);
ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
if (itsInterfaces != NoSuperInterfaces) {
if (++lastPosition == interfacesToVisit.length)
System.arraycopy(
interfacesToVisit,
0,
interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
0,
lastPosition);
interfacesToVisit[lastPosition] = itsInterfaces;
}
}
}
}
// bit reinitialization
for (int i = 0; i <= lastPosition; i++) {
ReferenceBinding[] interfaces = interfacesToVisit[i];
for (int j = 0, length = interfaces.length; j < length; j++)
interfaces[j].tagBits &= ~TagBits.InterfaceVisited;
}
}
}
private void findIntefacesMethods(
char[] selector,
TypeBinding[] argTypes,
ReferenceBinding receiverType,
ReferenceBinding[] itsInterfaces,
Scope scope,
ObjectVector methodsFound,
boolean onlyStaticMethods,
boolean exactMatch,
boolean isCompletingDeclaration,
InvocationSite invocationSite,
Scope invocationScope,
boolean implicitCall) {
if (selector == null)
return;
if (itsInterfaces != NoSuperInterfaces) {
ReferenceBinding[][] interfacesToVisit = new ReferenceBinding[5][];
int lastPosition = 0;
interfacesToVisit[lastPosition] = itsInterfaces;
for (int i = 0; i <= lastPosition; i++) {
ReferenceBinding[] interfaces = interfacesToVisit[i];
for (int j = 0, length = interfaces.length; j < length; j++) {
ReferenceBinding currentType = interfaces[j];
if ((currentType.tagBits & TagBits.InterfaceVisited) == 0) {
// if interface as not already been visited
currentType.tagBits |= TagBits.InterfaceVisited;
MethodBinding[] methods = currentType.availableMethods();
if(methods != null) {
if(isCompletingDeclaration){
findLocalMethodDeclarations(
selector,
methods,
scope,
methodsFound,
onlyStaticMethods,
exactMatch,
receiverType);
} else {
findLocalMethods(
selector,
argTypes,
methods,
scope,
methodsFound,
onlyStaticMethods,
exactMatch,
receiverType,
invocationSite,
invocationScope,
implicitCall);
}
}
itsInterfaces = currentType.superInterfaces();
if (itsInterfaces != NoSuperInterfaces) {
if (++lastPosition == interfacesToVisit.length)
System.arraycopy(
interfacesToVisit,
0,
interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
0,
lastPosition);
interfacesToVisit[lastPosition] = itsInterfaces;
}
}
}
}
// bit reinitialization
for (int i = 0; i <= lastPosition; i++) {
ReferenceBinding[] interfaces = interfacesToVisit[i];
for (int j = 0, length = interfaces.length; j < length; j++){
interfaces[j].tagBits &= ~TagBits.InterfaceVisited;
}
}
}
}
private void findImplicitMessageSends(
char[] token,
TypeBinding[] argTypes,
Scope scope,
InvocationSite invocationSite,
Scope invocationScope) {
if (token == null)
return;
boolean staticsOnly = false;
// need to know if we're in a static context (or inside a constructor)
ObjectVector methodsFound = new ObjectVector();
done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found
switch (scope.kind) {
case Scope.METHOD_SCOPE :
// handle the error case inside an explicit constructor call (see MethodScope>>findField)
MethodScope methodScope = (MethodScope) scope;
staticsOnly |= methodScope.isStatic | methodScope.isConstructorCall;
break;
case Scope.CLASS_SCOPE :
ClassScope classScope = (ClassScope) scope;
SourceTypeBinding enclosingType = classScope.referenceContext.binding;
findMethods(
token,
argTypes,
enclosingType,
classScope,
methodsFound,
staticsOnly,
true,
false,
invocationSite,
invocationScope,
true);
staticsOnly |= enclosingType.isStatic();
break;
case Scope.COMPILATION_UNIT_SCOPE :
break done;
}
scope = scope.parent;
}
}
// Helper method for findMethods(char[], TypeBinding[], ReferenceBinding, Scope, ObjectVector, boolean, boolean, boolean)
private void findLocalMethods(
char[] methodName,
TypeBinding[] argTypes,
MethodBinding[] methods,
Scope scope,
ObjectVector methodsFound,
boolean onlyStaticMethods,
boolean exactMatch,
ReferenceBinding receiverType,
InvocationSite invocationSite,
Scope invocationScope,
boolean implicitCall) {
// Inherited methods which are hidden by subclasses are filtered out
// No visibility checks can be performed without the scope & invocationSite
int methodLength = methodName.length;
int minArgLength = argTypes == null ? 0 : argTypes.length;
next : for (int f = methods.length; --f >= 0;) {
MethodBinding method = methods[f];
if (method.isSynthetic()) continue next;
if (method.isDefaultAbstract()) continue next;
if (method.isConstructor()) continue next;
// if (noVoidReturnType && method.returnType == BaseTypes.VoidBinding) continue next;
if (onlyStaticMethods && !method.isStatic()) continue next;
if (options.checkVisibility
&& !method.canBeSeenBy(receiverType, invocationSite, scope)) continue next;
if (exactMatch) {
if (!CharOperation.equals(methodName, method.selector, false /* ignore case */
))
continue next;
} else {
if (methodLength > method.selector.length)
continue next;
if (!CharOperation.prefixEquals(methodName, method.selector, false
/* ignore case */
))
continue next;
}
if (minArgLength > method.parameters.length)
continue next;
for (int a = minArgLength; --a >= 0;){
if (argTypes[a] != null){ // can be null if it could not be resolved properly
if (!scope.areTypesCompatible(argTypes[a], method.parameters[a])) {
continue next;
}
}
}
boolean prefixRequired = false;
for (int i = methodsFound.size; --i >= 0;) {
Object[] other = (Object[]) methodsFound.elementAt(i);
MethodBinding otherMethod = (MethodBinding) other[0];
ReferenceBinding otherReceiverType = (ReferenceBinding) other[1];
if (method == otherMethod && receiverType == otherReceiverType)
continue next;
if (CharOperation.equals(method.selector, otherMethod.selector, true)
&& method.areParametersEqual(otherMethod)) {
if (method.declaringClass.isSuperclassOf(otherMethod.declaringClass))
continue next;
if (otherMethod.declaringClass.isInterface())
if (method
.declaringClass
.implementsInterface(otherMethod.declaringClass, true))
continue next;
if (method.declaringClass.isInterface())
if(otherMethod
.declaringClass
.implementsInterface(method.declaringClass,true))
continue next;
prefixRequired = true;
}
}
methodsFound.add(new Object[]{method, receiverType});
int length = method.parameters.length;
char[][] parameterPackageNames = new char[length][];
char[][] parameterTypeNames = new char[length][];
for (int i = 0; i < length; i++) {
TypeBinding type = method.parameters[i];
parameterPackageNames[i] = type.qualifiedPackageName();
parameterTypeNames[i] = type.qualifiedSourceName();
}
char[][] parameterNames = findMethodParameterNames(method,parameterTypeNames);
char[] completion = TypeConstants.NoChar;
int previousStartPosition = startPosition;
// nothing to insert - do not want to replace the existing selector & arguments
if (!exactMatch) {
if (source != null
&& source.length > endPosition
&& source[endPosition] == '(')
completion = method.selector;
else
completion = CharOperation.concat(method.selector, new char[] { '(', ')' });
} else {
if(prefixRequired && (source != null)) {
completion = CharOperation.subarray(source, startPosition, endPosition);
} else {
startPosition = endPosition;
}
}
if(prefixRequired || options.forceImplicitQualification){
char[] prefix = computePrefix(scope.enclosingSourceType(), invocationScope.enclosingSourceType(), method.isStatic());
completion = CharOperation.concat(prefix,completion,'.');
}
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(methodName, method.selector);
relevance += computeRelevanceForExpectingType(method.returnType);
requestor.acceptMethod(
method.declaringClass.qualifiedPackageName(),
method.declaringClass.qualifiedSourceName(),
method.selector,
parameterPackageNames,
parameterTypeNames,
parameterNames,
method.returnType.qualifiedPackageName(),
method.returnType.qualifiedSourceName(),
completion,
method.modifiers,
startPosition - offset,
endPosition - offset,
relevance);
startPosition = previousStartPosition;
}
}
private int computeRelevanceForCaseMatching(char[] token, char[] proposalName){
if (CharOperation.prefixEquals(token, proposalName, true /* do not ignore case */)) {
return R_CASE;
} else {
return R_DEFAULT;
}
}
private int computeRelevanceForClass(){
if(assistNodeIsClass) {
return R_CLASS;
}
return 0;
}
private int computeRelevanceForInterface(){
if(assistNodeIsInterface) {
return R_INTERFACE;
}
return R_DEFAULT;
}
private int computeRelevanceForException(char[] proposalName){
if(assistNodeIsException &&
(CharOperation.match(EXCEPTION_PATTERN, proposalName, false) ||
CharOperation.match(ERROR_PATTERN, proposalName, false))) {
return R_EXCEPTION;
}
return R_DEFAULT;
}
private int computeRelevanceForExpectingType(TypeBinding proposalType){
if(expectedTypes != null && proposalType != null) {
for (int i = 0; i < expectedTypes.length; i++) {
if(Scope.areTypesCompatible(proposalType, expectedTypes[i])) {
return R_EXPECTED_TYPE;
}
}
}
return R_DEFAULT;
}
private int computeRelevanceForExpectingType(char[] packageName, char[] typeName){
if(expectedTypes != null) {
for (int i = 0; i < expectedTypes.length; i++) {
if(CharOperation.equals(expectedTypes[i].qualifiedPackageName(), packageName) &&
CharOperation.equals(expectedTypes[i].qualifiedSourceName(), typeName)) {
return R_EXPECTED_TYPE;
}
}
}
return R_DEFAULT;
}
// Helper method for findMethods(char[], MethodBinding[], Scope, ObjectVector, boolean, boolean, boolean, TypeBinding)
private void findLocalMethodDeclarations(
char[] methodName,
MethodBinding[] methods,
Scope scope,
ObjectVector methodsFound,
// boolean noVoidReturnType, how do you know?
boolean onlyStaticMethods,
boolean exactMatch,
ReferenceBinding receiverType) {
// Inherited methods which are hidden by subclasses are filtered out
// No visibility checks can be performed without the scope & invocationSite
int methodLength = methodName.length;
next : for (int f = methods.length; --f >= 0;) {
MethodBinding method = methods[f];
if (method.isSynthetic()) continue next;
if (method.isDefaultAbstract()) continue next;
if (method.isConstructor()) continue next;
if (method.isFinal()) continue next;
// if (noVoidReturnType && method.returnType == BaseTypes.VoidBinding) continue next;
if (onlyStaticMethods && !method.isStatic()) continue next;
if (options.checkVisibility
&& !method.canBeSeenBy(receiverType, FakeInvocationSite , scope)) continue next;
if (exactMatch) {
if (!CharOperation.equals(methodName, method.selector, false /* ignore case */
))
continue next;
} else {
if (methodLength > method.selector.length)
continue next;
if (!CharOperation.prefixEquals(methodName, method.selector, false
/* ignore case */
))
continue next;
}
for (int i = methodsFound.size; --i >= 0;) {
MethodBinding otherMethod = (MethodBinding) methodsFound.elementAt(i);
if (method == otherMethod)
continue next;
if (CharOperation.equals(method.selector, otherMethod.selector, true)
&& method.areParametersEqual(otherMethod)) {
continue next;
}
}
methodsFound.add(method);
int length = method.parameters.length;
char[][] parameterPackageNames = new char[length][];
char[][] parameterTypeNames = new char[length][];
for (int i = 0; i < length; i++) {
TypeBinding type = method.parameters[i];
parameterPackageNames[i] = type.qualifiedPackageName();
parameterTypeNames[i] = type.qualifiedSourceName();
}
char[][] parameterNames = findMethodParameterNames(method,parameterTypeNames);
StringBuffer completion = new StringBuffer(10);
// flush uninteresting modifiers
int insertedModifiers = method.modifiers & ~(CompilerModifiers.AccNative | CompilerModifiers.AccAbstract);
if (!exactMatch) {
if(insertedModifiers != CompilerModifiers.AccDefault){
completion.append(AstNode.modifiersString(insertedModifiers));
}
char[] returnPackageName = method.returnType.qualifiedPackageName();
char[] returnTypeName = method.returnType.qualifiedSourceName();
if(mustQualifyType(returnPackageName, returnTypeName)) {
completion.append(CharOperation.concat(returnPackageName, returnTypeName,'.'));
} else {
completion.append(method.returnType.sourceName());
}
completion.append(' ');
completion.append(method.selector);
completion.append('(');
for(int i = 0; i < length ; i++){
if(mustQualifyType(parameterPackageNames[i], parameterTypeNames[i])){
completion.append(CharOperation.concat(parameterPackageNames[i], parameterTypeNames[i], '.'));
} else {
completion.append(parameterTypeNames[i]);
}
completion.append(' ');
if(parameterNames != null){
completion.append(parameterNames[i]);
} else {
completion.append('%');
}
if(i != (length - 1))
completion.append(',');
}
completion.append(')');
ReferenceBinding[] exceptions = method.thrownExceptions;
if (exceptions != null && exceptions.length > 0){
completion.append(' ');
completion.append(THROWS);
completion.append(' ');
for(int i = 0; i < exceptions.length ; i++){
ReferenceBinding exception = exceptions[i];
char[] exceptionPackageName = exception.qualifiedPackageName();
char[] exceptionTypeName = exception.qualifiedSourceName();
if(i != 0){
completion.append(',');
completion.append(' ');
}
if(mustQualifyType(exceptionPackageName, exceptionTypeName)){
completion.append(CharOperation.concat(exceptionPackageName, exceptionTypeName, '.'));
} else {
completion.append(exception.sourceName());
}
}
}
}
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(methodName, method.selector);
requestor.acceptMethodDeclaration(
method.declaringClass.qualifiedPackageName(),
method.declaringClass.qualifiedSourceName(),
method.selector,
parameterPackageNames,
parameterTypeNames,
parameterNames,
method.returnType.qualifiedPackageName(),
method.returnType.qualifiedSourceName(),
completion.toString().toCharArray(),
method.modifiers,
startPosition - offset,
endPosition - offset,
relevance);
}
}
private void findMethods(
char[] selector,
TypeBinding[] argTypes,
ReferenceBinding receiverType,
Scope scope,
ObjectVector methodsFound,
boolean onlyStaticMethods,
boolean exactMatch,
boolean isCompletingDeclaration,
InvocationSite invocationSite,
Scope invocationScope,
boolean implicitCall) {
if (selector == null)
return;
if(isCompletingDeclaration) {
MethodBinding[] methods = receiverType.availableMethods();
if (methods != null){
for (int i = 0; i < methods.length; i++) {
if(!methods[i].isDefaultAbstract()) {
methodsFound.add(methods[i]);
}
}
}
}
ReferenceBinding currentType = receiverType;
if (receiverType.isInterface()) {
if(isCompletingDeclaration) {
findIntefacesMethods(
selector,
argTypes,
receiverType,
currentType.superInterfaces(),
scope,
methodsFound,
onlyStaticMethods,
exactMatch,
isCompletingDeclaration,
invocationSite,
invocationScope,
implicitCall);
} else {
findIntefacesMethods(
selector,
argTypes,
receiverType,
new ReferenceBinding[]{currentType},
scope,
methodsFound,
onlyStaticMethods,
exactMatch,
isCompletingDeclaration,
invocationSite,
invocationScope,
implicitCall);
}
currentType = scope.getJavaLangObject();
} else {
if(isCompletingDeclaration){
findIntefacesMethods(
selector,
argTypes,
receiverType,
currentType.superInterfaces(),
scope,
methodsFound,
onlyStaticMethods,
exactMatch,
isCompletingDeclaration,
invocationSite,
invocationScope,
implicitCall);
currentType = receiverType.superclass();
}
}
boolean hasPotentialDefaultAbstractMethods = true;
while (currentType != null) {
MethodBinding[] methods = currentType.availableMethods();
if(methods != null) {
if(isCompletingDeclaration){
findLocalMethodDeclarations(
selector,
methods,
scope,
methodsFound,
onlyStaticMethods,
exactMatch,
receiverType);
} else{
findLocalMethods(
selector,
argTypes,
methods,
scope,
methodsFound,
onlyStaticMethods,
exactMatch,
receiverType,
invocationSite,
invocationScope,
implicitCall);
}
}
if(hasPotentialDefaultAbstractMethods && currentType.isAbstract()){
findIntefacesMethods(
selector,
argTypes,
receiverType,
currentType.superInterfaces(),
scope,
methodsFound,
onlyStaticMethods,
exactMatch,
isCompletingDeclaration,
invocationSite,
invocationScope,
implicitCall);
} else {
hasPotentialDefaultAbstractMethods = false;
}
currentType = currentType.superclass();
}
}
private char[][] findMethodParameterNames(MethodBinding method, char[][] parameterTypeNames){
ReferenceBinding bindingType = method.declaringClass;
char[][] parameterNames = null;
int length = parameterTypeNames.length;
if (length == 0){
return TypeConstants.NoCharChar;
}
// look into the corresponding unit if it is available
if (bindingType instanceof SourceTypeBinding){
SourceTypeBinding sourceType = (SourceTypeBinding) bindingType;
if (sourceType.scope != null){
TypeDeclaration parsedType;
if ((parsedType = sourceType.scope.referenceContext) != null){
AbstractMethodDeclaration methodDecl = parsedType.declarationOf(method);
if (methodDecl != null){
Argument[] arguments = methodDecl.arguments;
parameterNames = new char[length][];
for(int i = 0 ; i < length ; i++){
parameterNames[i] = arguments[i].name;
}
}
}
}
}
// look into the model
if(parameterNames == null){
NameEnvironmentAnswer answer = nameEnvironment.findType(bindingType.compoundName);
if(answer != null){
if(answer.isSourceType()) {
ISourceType sourceType = answer.getSourceTypes()[0];
ISourceMethod[] sourceMethods = sourceType.getMethods();
int len = sourceMethods == null ? 0 : sourceMethods.length;
for(int i = 0; i < len ; i++){
ISourceMethod sourceMethod = sourceMethods[i];
char[][] argTypeNames = sourceMethod.getArgumentTypeNames();
if(argTypeNames != null &&
CharOperation.equals(method.selector,sourceMethod.getSelector()) &&
CharOperation.equals(argTypeNames,parameterTypeNames)){
parameterNames = sourceMethod.getArgumentNames();
break;
}
}
}
}
}
return parameterNames;
}
private void findNestedTypes(
char[] typeName,
SourceTypeBinding currentType,
Scope scope) {
if (typeName == null)
return;
int typeLength = typeName.length;
while (scope != null) { // done when a COMPILATION_UNIT_SCOPE is found
switch (scope.kind) {
case Scope.METHOD_SCOPE :
case Scope.BLOCK_SCOPE :
BlockScope blockScope = (BlockScope) scope;
next : for (int i = 0, length = blockScope.scopeIndex; i < length; i++) {
if (blockScope.subscopes[i] instanceof ClassScope) {
SourceTypeBinding localType =
((ClassScope) blockScope.subscopes[i]).referenceContext.binding;
if (!localType.isAnonymousType()) {
if (typeLength > localType.sourceName.length)
continue next;
if (!CharOperation.prefixEquals(typeName, localType.sourceName, false
/* ignore case */
))
continue next;
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(typeName, localType.sourceName);
relevance += computeRelevanceForExpectingType(localType);
relevance += computeRelevanceForClass();
requestor.acceptClass(
localType.qualifiedPackageName(),
localType.sourceName,
localType.sourceName,
localType.modifiers,
startPosition - offset,
endPosition - offset,
relevance);
}
}
}
break;
case Scope.CLASS_SCOPE :
findMemberTypes(typeName, scope.enclosingSourceType(), scope, currentType);
if (typeLength == 0)
return; // do not search outside the class scope if no prefix was provided
break;
case Scope.COMPILATION_UNIT_SCOPE :
return;
}
scope = scope.parent;
}
}
private void findPackages(CompletionOnPackageReference packageStatement) {
token = CharOperation.concatWith(packageStatement.tokens, '.');
if (token.length == 0)
return;
setSourceRange(packageStatement.sourceStart, packageStatement.sourceEnd);
nameEnvironment.findPackages(CharOperation.toLowerCase(token), this);
}
private void findTypesAndPackages(char[] token, Scope scope) {
if (token == null)
return;
if (scope.enclosingSourceType() != null)
findNestedTypes(token, scope.enclosingSourceType(), scope);
if (unitScope != null) {
int typeLength = token.length;
SourceTypeBinding[] types = unitScope.topLevelTypes;
for (int i = 0, length = types.length; i < length; i++) {
SourceTypeBinding sourceType = types[i];
if (typeLength > sourceType.sourceName.length) continue;
if (!CharOperation.prefixEquals(token, sourceType.sourceName, false)) continue;
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(token, sourceType.sourceName);
relevance += computeRelevanceForExpectingType(sourceType);
if (sourceType.isClass()){
relevance += computeRelevanceForClass();
requestor.acceptClass(
sourceType.qualifiedPackageName(),
sourceType.sourceName(),
sourceType.sourceName(),
sourceType.modifiers,
startPosition - offset,
endPosition - offset,
relevance);
} else {
relevance += computeRelevanceForInterface();
requestor.acceptInterface(
sourceType.qualifiedPackageName(),
sourceType.sourceName(),
sourceType.sourceName(),
sourceType.modifiers,
startPosition - offset,
endPosition - offset,
relevance);
}
}
}
if (token.length == 0)
return;
findKeywords(token, baseTypes, scope);
nameEnvironment.findTypes(token, this);
nameEnvironment.findPackages(token, this);
}
private void findTypesAndSubpackages(
char[] token,
PackageBinding packageBinding) {
char[] qualifiedName =
CharOperation.concatWith(packageBinding.compoundName, token, '.');
if (token == null || token.length == 0) {
int length = qualifiedName.length;
System.arraycopy(
qualifiedName,
0,
qualifiedName = new char[length + 1],
0,
length);
qualifiedName[length] = '.';
}
nameEnvironment.findTypes(qualifiedName, this);
nameEnvironment.findPackages(qualifiedName, this);
}
private void findVariablesAndMethods(
char[] token,
Scope scope,
InvocationSite invocationSite,
Scope invocationScope) {
if (token == null)
return;
// Should local variables hide fields from the receiver type or any of its enclosing types?
// we know its an implicit field/method access... see BlockScope getBinding/getImplicitMethod
boolean staticsOnly = false;
// need to know if we're in a static context (or inside a constructor)
int tokenLength = token.length;
ObjectVector localsFound = new ObjectVector();
ObjectVector fieldsFound = new ObjectVector();
ObjectVector methodsFound = new ObjectVector();
Scope currentScope = scope;
done1 : while (true) { // done when a COMPILATION_UNIT_SCOPE is found
switch (currentScope.kind) {
case Scope.METHOD_SCOPE :
// handle the error case inside an explicit constructor call (see MethodScope>>findField)
MethodScope methodScope = (MethodScope) currentScope;
staticsOnly |= methodScope.isStatic | methodScope.isConstructorCall;
case Scope.BLOCK_SCOPE :
BlockScope blockScope = (BlockScope) currentScope;
next : for (int i = 0, length = blockScope.locals.length; i < length; i++) {
LocalVariableBinding local = blockScope.locals[i];
if (local == null)
break next;
if (tokenLength > local.name.length)
continue next;
if (!CharOperation.prefixEquals(token, local.name, false /* ignore case */
))
continue next;
if (local.isSecret())
continue next;
for (int f = 0; f < localsFound.size; f++) {
LocalVariableBinding otherLocal =
(LocalVariableBinding) localsFound.elementAt(f);
if (CharOperation.equals(otherLocal.name, local.name, false /* ignore case */
))
continue next;
}
localsFound.add(local);
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(token, local.name);
relevance += computeRelevanceForExpectingType(local.type);
requestor.acceptLocalVariable(
local.name,
NoChar,
local.type == null
? local.declaration.type.toString().toCharArray()
: local.type.qualifiedSourceName(),
local.modifiers,
startPosition - offset,
endPosition - offset,
relevance);
}
break;
case Scope.COMPILATION_UNIT_SCOPE :
break done1;
}
currentScope = currentScope.parent;
}
currentScope = scope;
done2 : while (true) { // done when a COMPILATION_UNIT_SCOPE is found
switch (currentScope.kind) {
case Scope.CLASS_SCOPE :
ClassScope classScope = (ClassScope) currentScope;
SourceTypeBinding enclosingType = classScope.referenceContext.binding;
/* if (tokenLength == 0) { // only search inside the type itself if no prefix was provided
findFields(token, enclosingType.fields(), classScope, fieldsFound, staticsOnly);
findMethods(token, enclosingType.methods(), classScope, methodsFound, staticsOnly, false);
break done;
} else { */
findFields(
token,
enclosingType,
classScope,
fieldsFound,
localsFound,
staticsOnly,
invocationSite,
invocationScope,
true);
findMethods(
token,
null,
enclosingType,
classScope,
methodsFound,
staticsOnly,
false,
false,
invocationSite,
invocationScope,
true);
staticsOnly |= enclosingType.isStatic();
// }
break;
case Scope.COMPILATION_UNIT_SCOPE :
break done2;
}
currentScope = currentScope.parent;
}
}
// Helper method for private void findVariableNames(char[] name, TypeReference type )
private void findVariableName(char[] token, char[] qualifiedPackageName, char[] qualifiedSourceName, char[] sourceName, char[][] excludeNames, int dim){
if(sourceName == null || sourceName.length == 0)
return;
char[] name = null;
// compute variable name for base type
try{
nameScanner.setSource(sourceName);
switch (nameScanner.getNextToken()) {
case TokenNameint :
case TokenNamebyte :
case TokenNameshort :
case TokenNamechar :
case TokenNamelong :
case TokenNamefloat :
case TokenNamedouble :
case TokenNameboolean :
if(token != null && token.length != 0)
return;
name = computeBaseNames(sourceName[0], excludeNames);
break;
}
if(name != null) {
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(token, name);
// accept result
requestor.acceptVariableName(
qualifiedPackageName,
qualifiedSourceName,
name,
name,
startPosition - offset,
endPosition - offset,
relevance);
return;
}
} catch(InvalidInputException e){
}
// compute variable name for non base type
char[][] names = computeNames(sourceName, dim > 0);
char[] displayName;
if (dim > 0){
int l = qualifiedSourceName.length;
displayName = new char[l+(2*dim)];
System.arraycopy(qualifiedSourceName, 0, displayName, 0, l);
for(int i = 0; i < dim; i++){
displayName[l+(i*2)] = '[';
displayName[l+(i*2)+1] = ']';
}
} else {
displayName = qualifiedSourceName;
}
next : for(int i = 0 ; i < names.length ; i++){
name = names[i];
if (!CharOperation.prefixEquals(token, name, false))
continue next;
// completion must be an identifier (not a keyword, ...).
try{
nameScanner.setSource(name);
if(nameScanner.getNextToken() != TokenNameIdentifier)
continue next;
} catch(InvalidInputException e){
continue next;
}
int count = 2;
char[] originalName = name;
for(int j = 0 ; j < excludeNames.length ; j++){
if(CharOperation.equals(name, excludeNames[j], false)) {
name = CharOperation.concat(originalName, String.valueOf(count++).toCharArray());
j = 0;
}
}
int relevance = R_DEFAULT;
relevance += computeRelevanceForCaseMatching(token, name);
// accept result
requestor.acceptVariableName(
qualifiedPackageName,
displayName,
name,
name,
startPosition - offset,
endPosition - offset,
relevance);
}
}
private void findVariableNames(char[] name, TypeReference type , char[][] excludeNames){
if(type != null &&
type.binding != null &&
type.binding.problemId() == Binding.NoError){
TypeBinding tb = type.binding;
findVariableName(
name,
tb.leafComponentType().qualifiedPackageName(),
tb.leafComponentType().qualifiedSourceName(),
tb.leafComponentType().sourceName(),
excludeNames,
type.dimensions());
}/* else {
char[][] typeName = type.getTypeName();
findVariableName(
name,
NoChar,
CharOperation.concatWith(typeName, '.'),
typeName[typeName.length - 1],
excludeNames,
type.dimensions());
}*/
}
public AssistParser getParser() {
return parser;
}
protected void reset() {
super.reset();
this.knownPkgs = new HashtableOfObject(10);
this.knownTypes = new HashtableOfObject(10);
}
private void setSourceRange(int start, int end) {
this.startPosition = start;
this.endPosition = end + 1;
}
private char[] computeBaseNames(char firstName, char[][] excludeNames){
char[] name = new char[]{firstName};
for(int i = 0 ; i < excludeNames.length ; i++){
if(CharOperation.equals(name, excludeNames[i], false)) {
name[0]++;
if(name[0] > 'z')
name[0] = 'a';
if(name[0] == firstName)
return null;
i = 0;
}
}
return name;
}
private void computeExpectedTypes(AstNode parent, Scope scope){
int expectedTypeCount = 0;
expectedTypes = new TypeBinding[1];
if(parent instanceof AbstractVariableDeclaration) {
TypeBinding binding = ((AbstractVariableDeclaration)parent).type.binding;
if(binding != null) {
expectedTypes[expectedTypeCount++] = binding;
}
} else if(parent instanceof Assignment) {
TypeBinding binding = ((Assignment)parent).lhsType;
if(binding != null) {
expectedTypes[expectedTypeCount++] = binding;
}
} else if(parent instanceof ReturnStatement) {
MethodBinding methodBinding = ((AbstractMethodDeclaration) scope.methodScope().referenceContext).binding;
TypeBinding binding = methodBinding == null ? null : methodBinding.returnType;
if(binding != null) {
expectedTypes[expectedTypeCount++] = binding;
}
}
System.arraycopy(expectedTypes, 0, expectedTypes = new TypeBinding[expectedTypeCount], 0, expectedTypeCount);
}
private char[][] computeNames(char[] sourceName, boolean forArray){
char[][] names = new char[5][];
int nameCount = 0;
boolean previousIsUpperCase = false;
for(int i = sourceName.length - 1 ; i >= 0 ; i--){
boolean isUpperCase = Character.isUpperCase(sourceName[i]);
if(isUpperCase && !previousIsUpperCase){
char[] name = CharOperation.subarray(sourceName,i,sourceName.length);
if(name.length > 1){
if(nameCount == names.length) {
System.arraycopy(names, 0, names = new char[nameCount * 2][], 0, nameCount);
}
name[0] = Character.toLowerCase(name[0]);
if(forArray) {
int length = name.length;
if (name[length-1] == 's'){
System.arraycopy(name, 0, name = new char[length + 2], 0, length);
name[length] = 'e';
name[length+1] = 's';
} else {
System.arraycopy(name, 0, name = new char[length + 1], 0, length);
name[length] = 's';
}
}
names[nameCount++] = name;
}
}
previousIsUpperCase = isUpperCase;
}
if(nameCount == 0){
char[] name = CharOperation.toLowerCase(sourceName);
if(forArray) {
int length = name.length;
if (name[length-1] == 's'){
System.arraycopy(name, 0, name = new char[length + 2], 0, length);
name[length] = 'e';
name[length+1] = 's';
} else {
System.arraycopy(name, 0, name = new char[length + 1], 0, length);
name[length] = 's';
}
}
names[nameCount++] = name;
}
System.arraycopy(names, 0, names = new char[nameCount][], 0, nameCount);
return names;
}
private char[] computePrefix(SourceTypeBinding declarationType, SourceTypeBinding invocationType, boolean isStatic){
StringBuffer completion = new StringBuffer(10);
if (isStatic) {
completion.append(declarationType.sourceName());
} else if (declarationType == invocationType) {
completion.append(THIS);
} else {
if (!declarationType.isNestedType()) {
completion.append(declarationType.sourceName());
completion.append('.');
completion.append(THIS);
} else if (!declarationType.isAnonymousType()) {
completion.append(declarationType.sourceName());
completion.append('.');
completion.append(THIS);
}
}
return completion.toString().toCharArray();
}
private boolean isEnclosed(ReferenceBinding possibleEnclosingType, ReferenceBinding type){
if(type.isNestedType()){
ReferenceBinding enclosing = type.enclosingType();
while(enclosing != null ){
if(possibleEnclosingType == enclosing)
return true;
enclosing = enclosing.enclosingType();
}
}
return false;
}
}