--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v0.5
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v05.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ ******************************************************************************/
+package net.sourceforge.phpdt.internal.codeassist;
+
+import java.util.*;
+
+import net.sourceforge.phpdt.core.compiler.InvalidInputException;
+import net.sourceforge.phpdt.internal.compiler.*;
+import net.sourceforge.phpdt.internal.compiler.env.*;
+
+import net.sourceforge.phpdt.internal.codeassist.impl.*;
+import net.sourceforge.phpdt.core.ICompletionRequestor;
+import net.sourceforge.phpdt.core.IType;
+//import net.sourceforge.phpdt.core.JavaCore;
+//import net.sourceforge.phpdt.core.JavaModelException;
+import net.sourceforge.phpdt.core.compiler.*;
+import net.sourceforge.phpdt.core.compiler.IProblem;
+import net.sourceforge.phpdt.internal.codeassist.complete.*;
+
+import net.sourceforge.phpdt.internal.compiler.ast.*;
+import net.sourceforge.phpdt.internal.compiler.lookup.*;
+import net.sourceforge.phpdt.internal.compiler.parser.*;
+import net.sourceforge.phpdt.internal.compiler.problem.*;
+import net.sourceforge.phpdt.internal.compiler.util.*;
+import net.sourceforge.phpdt.internal.core.BasicCompilationUnit;
+import net.sourceforge.phpdt.internal.core.TypeConverter;
+import net.sourceforge.phpdt.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;
+
+ 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 net.sourceforge.phpdt.internal.codeassist.ISearchableNameEnvironment
+ * used to resolve type/package references and search for types/packages
+ * based on partial names.
+ *
+ * @param requestor net.sourceforge.phpdt.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) {
+
+ super(settings);
+ this.requestor = requestor;
+ this.nameEnvironment = nameEnvironment;
+
+ problemReporter = new ProblemReporter(
+ DefaultErrorHandlingPolicies.proceedWithAllProblems(),
+ this.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, this.compilerOptions.assertMode);
+ this.lookupEnvironment =
+ new LookupEnvironment(this, this.compilerOptions, problemReporter, nameEnvironment);
+ this.nameScanner =
+ new Scanner(false, false, false, this.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, this.compilerOptions.maxProblemsPerUnit); //$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 = JavaCore.getOption(JavaCore.CORE_ENCODING);
+// BasicCompilationUnit fakeUnit = new BasicCompilationUnit(
+// fakeSource,
+// null,
+// "FakeType.java", //$NON-NLS-1$
+// encoding);
+//
+// actualCompletionPosition = prefix.length() + position - 1;
+//
+// CompilationResult fakeResult = new CompilationResult(fakeUnit, 1, 1, this.compilerOptions.maxProblemsPerUnit);
+// 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 net.sourceforge.phpdt.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, this.compilerOptions.maxProblemsPerUnit);
+ 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;
+ if (field.declaringClass.isInterface())
+ if (otherField.declaringClass.implementsInterface(field.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;
+
+ if (memberType.enclosingType().isInterface())
+ if (otherType.enclosingType()
+ .implementsInterface(memberType.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, true))
+ continue next;
+ }
+ localsFound.add(local);
+
+ int relevance = R_DEFAULT;
+ relevance += computeRelevanceForCaseMatching(token, local.name);
+ relevance += computeRelevanceForExpectingType(local.type);
+
+ requestor.acceptLocalVariable(
+ local.name,
+ local.type == null
+ ? NoChar
+ : local.type.qualifiedPackageName(),
+ 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);
+ nameScanner.getNextToken(); // 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;
+ }
+
+}